From 8f51ea9a19a8900d424236deb545e978d3ff0811 Mon Sep 17 00:00:00 2001 From: jerrrrycho Date: Sat, 7 Nov 2026 21:32:41 +0100 Subject: [PATCH] feat(ai): suggested pantograph setup for shunting in the .fiz (eu07/maszyna#461) A new optional key in the AI section of the .fiz, ShuntPantstate= (bits as in Pantstate: 1 front, 2 rear, 3 both), sets the pantographs the AI driver keeps up while its order is Shunt or Loose_shunt, moving and standing alike: it raises those of the setup and lowers the others once one of the setup collects the voltage, and doesn't raise more when idling. Lowering on the track's demand still comes first. Without the key (default 0) nothing changes, so an ET41 no longer has to shunt on all four pantographs once its .fiz says so. SVN data, optional: add "AI: ShuntPantstate=<1|2|3>" to the .fiz of vehicles that should shunt on fewer pantographs, e.g. dynamic/pkp/et41_v2/203e-a.fiz, 203e-b.fiz and their _dsa150 variants (no AI line there yet). Without it the vehicles behave as before. (eu07/maszyna#461) --- McZapkie/MOVER.h | 3 +++ McZapkie/Mover.cpp | 1 + tests/unit/test_ai_hints.cpp | 31 +++++++++++++++++++++++++ vehicle/Driver.cpp | 44 +++++++++++++++++++++++++++++++++++- vehicle/Driver.h | 1 + 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/unit/test_ai_hints.cpp diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 199c65a0..b29f2afa 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -2300,6 +2300,9 @@ class TMoverParameters plc::basic_controller m_plc; int AIHintPantstate{0}; // suggested pantograph setup + // suggested pantograph setup in shunting (AI: ShuntPantstate=, eu07/maszyna#461), the bits as in Pantstate: 1 front, + // 2 rear, 3 both; 0 (the default) shunts with the pantographs handled as in train mode + int AIHintShuntPantstate{0}; bool AIHintPantUpIfIdle{true}; // whether raise both pantographs if idling for a while double AIHintLocalBrakeAccFactor{1.05}; // suggested acceleration weight for local brake operation diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 330e8eb8..7fb2ed8b 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -11474,6 +11474,7 @@ void TMoverParameters::LoadFIZ_AI(std::string const &Input) { extract_value(AIHintPantstate, "Pantstate", Input, ""); + extract_value(AIHintShuntPantstate, "ShuntPantstate", Input, ""); extract_value(AIHintPantUpIfIdle, "IdlePantUp", Input, ""); extract_value(AIHintLocalBrakeAccFactor, "LocalBrakeAccFactor", Input, ""); } diff --git a/tests/unit/test_ai_hints.cpp b/tests/unit/test_ai_hints.cpp new file mode 100644 index 00000000..d01e421c --- /dev/null +++ b/tests/unit/test_ai_hints.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "doctest.h" +#include "vehicle.h" + +using test::definition; +using test::vehicle; + +// the hints for the AI driver from the AI section of the .fiz + +TEST_CASE("ai hints: without the AI section the pantographs follow the built-in logic") { + vehicle wagon{definition::load("wagon")}; + + CHECK(wagon->AIHintPantstate == 0); + CHECK(wagon->AIHintShuntPantstate == 0); + CHECK(wagon->AIHintPantUpIfIdle); +} + +TEST_CASE("ai hints: the suggested pantograph setup for shunting is read apart from the one for train mode") { + // eu07/maszyna#461: so the ET41 doesn't shunt with all of its four pantographs up + vehicle shunting{definition::load("wagon").set("AI", "Pantstate", 1).set("AI", "ShuntPantstate", 2)}; + + CHECK(shunting->AIHintPantstate == 1); + CHECK(shunting->AIHintShuntPantstate == 2); +} + +TEST_CASE("ai hints: a .fiz with the setup for train mode only has none for shunting") { + vehicle train{definition::load("wagon").set("AI", "Pantstate", 3)}; + + CHECK(train->AIHintPantstate == 3); + CHECK(train->AIHintShuntPantstate == 0); +} diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 08f0c0d2..682061b6 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -6115,6 +6115,13 @@ TController::control_pantographs() { fActionTime = -2.0 - Random(10); } + // the pantograph setup the .fiz suggests for shunting (AI: ShuntPantstate=, eu07/maszyna#461), 0 when there's none + // or the vehicle isn't shunting + auto const shuntpantographsetup { + ( OrderCurrentGet() & ( Shunt | Loose_shunt ) ) != 0 ? + mvOccupied->AIHintShuntPantstate : + 0 }; + // raise/lower pantographs as needed auto const useregularpantographlayout { pVehicle->Next(coupling::control) == nullptr // standalone @@ -6141,7 +6148,11 @@ TController::control_pantographs() { // jeśli nie trzeba opuszczać pantografów // TODO: check if faction alone reduces frequency enough if( fActionTime > 0.0 ) { - if( mvOccupied->AIHintPantstate == 0 ) { + if( shuntpantographsetup != 0 ) { + // shunting with the pantograph setup the .fiz suggests for it + control_pantographs_setup( shuntpantographsetup ); + } + else if( mvOccupied->AIHintPantstate == 0 ) { // jazda na tylnym if( iDirection >= 0 && useregularpantographlayout ) { // jak jedzie w kierunku sprzęgu 0 @@ -6194,6 +6205,12 @@ TController::control_pantographs() { } } } + else if( shuntpantographsetup != 0 ) { + // shunting with the suggested setup, also while standing, and without raising more pantographs when idling + if( fOverhead2 <= 0.0 && iOverheadDown == 0 ) { + control_pantographs_setup( shuntpantographsetup ); + } + } else { if( !mvOccupied->AIHintPantUpIfIdle || IdleTime <= 45.0 || std::abs(fStopTime) <= 15.0 || mvPantographUnit->EnginePowerSource.CollectorParameters.CollectorsNo <= 1 ) { @@ -6220,6 +6237,31 @@ TController::control_pantographs() { } } +// raises the pantographs of the suggested setup (bit 0: front, bit 1: rear) and lowers the others, these only once +// a pantograph of the setup collects the voltage, so the vehicle doesn't lose its power on the way +void +TController::control_pantographs_setup( int const Setup ) { + + auto const front { ( Setup & 1 << 0 ) != 0 }; + auto const rear { ( Setup & 1 << 1 ) != 0 }; + if( front ) { + cue_action( driver_hint::frontpantographvalveon ); + } + if( rear ) { + cue_action( driver_hint::rearpantographvalveon ); + } + if( auto const setuplive { ( front && mvPantographUnit->PantFrontVolt != 0.0 ) || ( rear && mvPantographUnit->PantRearVolt != 0.0 ) }; + false == setuplive ) { + return; + } + if( false == front ) { + cue_action( driver_hint::frontpantographvalveoff ); + } + if( false == rear ) { + cue_action( driver_hint::rearpantographvalveoff ); + } +} + void TController::control_horns( double const Timedelta ) { // horn control diff --git a/vehicle/Driver.h b/vehicle/Driver.h index f88e115a..8c661328 100644 --- a/vehicle/Driver.h +++ b/vehicle/Driver.h @@ -320,6 +320,7 @@ private: void scan_obstacles( double const Range ); void control_wheelslip(); void control_pantographs(); + void control_pantographs_setup( int const Setup ); void control_horns( double const Timedelta ); void control_security_system( double const Timedelta ); void control_handles();