diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 199c65a0..825bec6d 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1943,6 +1943,7 @@ class TMoverParameters double MirrorVelClose{5.0}; bool MirrorForbidden{false}; /*czy jest pozwolenie na otworzenie lusterek (przycisk)*/ bool ScndS = false; /*Czy jest bocznikowanie na szeregowej*/ + bool ScndCtrlResistiveLock{false}; // the shunt controller can't be moved up while the master controller is at zero or on a resistive position (ScndCtrlResistiveLock=Yes in Cntrl.) bool SpeedCtrl = false; /*czy jest tempomat*/ speed_control SpeedCtrlUnit; /*parametry tempomatu*/ double SpeedCtrlButtons[10]{30, 40, 50, 60, 70, 80, 90, 100, 110, 120}; /*przyciski prędkości*/ @@ -2372,6 +2373,8 @@ class TMoverParameters bool IncScndCtrl(int CtrlSpeed); bool DecScndCtrl(int CtrlSpeed); int GetVirtualScndPos() const; + // whether ScndCtrlResistiveLock keeps the shunt controller from being moved up at the current master controller position + bool IsScndCtrlLockedByMainCtrl() const; bool IsScndCtrlNoPowerPos() const; bool IsScndCtrlMaxPowerPos() const; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 330e8eb8..b9d18d0b 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -2751,7 +2751,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed) { // if (RList[MainCtrlPos].R=0) and (MainCtrlPos>0) and (ScndCtrlPos= 0.01); +} + int TMoverParameters::GetVirtualScndPos() const { if (TrainType == dt_ET42 && DynamicBrakeFlag && !ScndCtrlPos) @@ -10897,6 +10903,7 @@ void TMoverParameters::LoadFIZ_Cntrl(std::string const &line) extract_value(EIMCtrlEmergency, "EIMCtrlEmergency", line, ""); extract_value(ScndS, "ScndS", line, ""); // brak pozycji rownoleglej przy niskiej nastawie PSR + extract_value(ScndCtrlResistiveLock, "ScndCtrlResistiveLock", line, ""); // bocznikowanie tylko na pozycjach bezoporowych extract_value(InitialCtrlDelay, "IniCDelay", line, ""); extract_value(CtrlDelay, "SCDelay", line, ""); diff --git a/tests/unit/test_cntrl.cpp b/tests/unit/test_cntrl.cpp index 0dd3ddc2..43c983ed 100644 --- a/tests/unit/test_cntrl.cpp +++ b/tests/unit/test_cntrl.cpp @@ -37,3 +37,83 @@ TEST_CASE("cntrl: the relays of a multiple unit start on their own, of other veh CHECK(unit->ConverterOverloadRelayStart == start_t::automatic); } } + +namespace { + +// the first position of the master controller after Position without the starting resistors in the circuit +int resistance_free_position(TMoverParameters const &Vehicle, int Position = 0) { + for (auto position{Position + 1}; position <= Vehicle.MainCtrlPosNo; ++position) { + if (Vehicle.RList[position].R < 0.01) { + return position; + } + } + return 0; +} + +// the vehicle with its cab occupied, the master controller set to Position and the shunt controller off +void controllers_at(vehicle &Vehicle, int Position) { + Vehicle->CabActive = 1; + Vehicle->MainCtrlPos = Position; + Vehicle->ScndCtrlPos = 0; +} + +} // namespace + +TEST_CASE("cntrl: ScndCtrlResistiveLock keeps the field shunting off at zero and on the resistive positions (eu07/maszyna#402)") { + for (auto const *path : {"et22_v3/201e-rn", "ep09_v2/104e"}) { + CAPTURE(path); + vehicle locked{definition::fiz(path).set("Cntrl", "ScndCtrlResistiveLock", "Yes"), 0.0, "test", 1}; + REQUIRE(locked->ScndCtrlResistiveLock); + REQUIRE(locked->ScndCtrlPosNo > 0); + auto const resistancefree{resistance_free_position(locked.mover())}; + REQUIRE(resistancefree > 1); + REQUIRE(locked->RList[resistancefree - 1].R >= 0.01); + + SUBCASE("zero") { + controllers_at(locked, 0); + CHECK(locked->IsScndCtrlLockedByMainCtrl()); + CHECK_FALSE(locked->IncScndCtrl(1)); + CHECK_FALSE(locked->IncScndCtrl(2)); + CHECK(locked->ScndCtrlPos == 0); + } + SUBCASE("resistive positions") { + for (auto position{1}; position < resistancefree; ++position) { + CAPTURE(position); + controllers_at(locked, position); + CHECK_FALSE(locked->IncScndCtrl(1)); + CHECK(locked->ScndCtrlPos == 0); + } + } + SUBCASE("every resistance-free position") { + for (auto position{resistancefree}; position > 0; position = resistance_free_position(locked.mover(), position)) { + CAPTURE(position); + controllers_at(locked, position); + CHECK_FALSE(locked->IsScndCtrlLockedByMainCtrl()); + CHECK(locked->IncScndCtrl(1)); + CHECK(locked->ScndCtrlPos == 1); + } + } + SUBCASE("the shunting still goes off on a resistive position") { + controllers_at(locked, resistancefree); + REQUIRE(locked->IncScndCtrl(1)); + locked->MainCtrlPos = resistancefree - 1; + CHECK(locked->DecScndCtrl(1)); + CHECK(locked->ScndCtrlPos == 0); + } + } +} + +TEST_CASE("cntrl: without ScndCtrlResistiveLock the field shunting engages on any position, as before") { + for (auto const *path : {"et22_v3/201e-rn", "ep09_v2/104e"}) { + CAPTURE(path); + vehicle unlocked{definition::fiz(path), 0.0, "test", 1}; + CHECK_FALSE(unlocked->ScndCtrlResistiveLock); + for (auto const position : {0, 1}) { + CAPTURE(position); + controllers_at(unlocked, position); + CHECK_FALSE(unlocked->IsScndCtrlLockedByMainCtrl()); + CHECK(unlocked->IncScndCtrl(1)); + CHECK(unlocked->ScndCtrlPos == 1); + } + } +} diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 08f0c0d2..bb6ff823 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -3364,7 +3364,7 @@ bool TController::IncSpeed() mvOccupied->Vel <= (IsCargoTrain ? 40 : 30) + (seriesmodefieldshunting ? 5 : 0) - (fAccGravity < -0.025 ? 10 : 0)); // when not in series mode use the first available parallel mode configuration until 50/60 km/h for passenger/cargo train // (if there's only one parallel mode configuration it'll be used regardless of current speed) - auto const usefieldshunting = mvControlling->StLinFlag && mvControlling->RList[mvControlling->MainCtrlPos].R < 0.01 && + auto const usefieldshunting = mvControlling->StLinFlag && mvControlling->RList[mvControlling->MainCtrlPos].R < 0.01 && false == mvControlling->IsScndCtrlLockedByMainCtrl() && (useseriesmode ? mvControlling->RList[mvControlling->MainCtrlPos].Bn == 1 : true == sufficientacceleration && true == sufficienttractionforce && mvOccupied->Vel <= (IsCargoTrain ? 60 : 50) + (parallelmodefieldshunting ? 5 : 0) ? mvControlling->RList[mvControlling->MainCtrlPos].Bn > 1 :