diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index 825bec6d..6a70147a 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -1943,7 +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 ScndCtrlResistiveLock{false}; // the shunt controller can't be moved, up or down, while the master controller is at zero or on a resistive position, and drops to 0 when the master controller gets there (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*/ @@ -2373,8 +2373,11 @@ 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 + // whether ScndCtrlResistiveLock keeps the shunt controller from being moved at the current master controller position bool IsScndCtrlLockedByMainCtrl() const; + // ScndCtrlResistiveLock: the shunt controller drops to 0 once the master controller is at zero or on a resistive position, so it + // can't stay engaged there, locked in place (eu07/maszyna#402) + void ScndCtrlResistiveLockCheck(); bool IsScndCtrlNoPowerPos() const; bool IsScndCtrlMaxPowerPos() const; diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index b9d18d0b..8154eb92 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1631,6 +1631,8 @@ void TMoverParameters::compute_movement_(double const Deltatime) BRVentilators = false; } + // the master controller set directly, not through IncMainCtrl()/DecMainCtrl() (AI, consist): the locked shunt follows it as well + ScndCtrlResistiveLockCheck(); // automatyczny rozruch if (EngineType == TEngineType::ElectricSeriesMotor && AutoRelayCheck()) { @@ -2537,6 +2539,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) if (true == OK) { + ScndCtrlResistiveLockCheck(); // e.g. from the last series position on to the resistive parallel ones SendCtrlToNext("MainCtrl", MainCtrlPos, CabActive); //??? SendCtrlToNext("ScndCtrl", ScndCtrlPos, CabActive); } @@ -2662,6 +2665,7 @@ bool TMoverParameters::DecMainCtrl(int CtrlSpeed) if (OK) { + ScndCtrlResistiveLockCheck(); // the shunt drops when the master controller leaves a resistance-free position /*OK:=*/SendCtrlToNext("MainCtrl", MainCtrlPos, CabActive); // hmmmm...???!!! /*OK:=*/SendCtrlToNext("ScndCtrl", ScndCtrlPos, CabActive); } @@ -2822,7 +2826,7 @@ bool TMoverParameters::DecScndCtrl(int CtrlSpeed) } else if (ScndCtrlPosNo > 0 && CabActive != 0) { - if (ScndCtrlPos > 0 && !CoupledCtrl && (EngineType != TEngineType::DieselElectric || !AutoRelayFlag)) + if (ScndCtrlPos > 0 && !CoupledCtrl && (EngineType != TEngineType::DieselElectric || !AutoRelayFlag) && !IsScndCtrlLockedByMainCtrl()) { if (CtrlSpeed == 1) { @@ -2878,6 +2882,16 @@ bool TMoverParameters::IsScndCtrlLockedByMainCtrl() const return ScndCtrlResistiveLock && (IsMainCtrlNoPowerPos() || RList[MainCtrlPos].R >= 0.01); } +void TMoverParameters::ScndCtrlResistiveLockCheck() +{ + // the shunt controller, locked in both directions on zero and the resistive positions, would otherwise stay engaged there once the + // master controller is taken back (or set from elsewhere, e.g. by the AI or another vehicle of the consist) with the shunt on + if (ScndCtrlPos > 0 && !CoupledCtrl && IsScndCtrlLockedByMainCtrl()) + { + ScndCtrlPos = 0; + } +} + int TMoverParameters::GetVirtualScndPos() const { if (TrainType == dt_ET42 && DynamicBrakeFlag && !ScndCtrlPos) diff --git a/tests/unit/test_cntrl.cpp b/tests/unit/test_cntrl.cpp index e6c53940..c73c45d8 100644 --- a/tests/unit/test_cntrl.cpp +++ b/tests/unit/test_cntrl.cpp @@ -96,12 +96,71 @@ TEST_CASE("cntrl: ScndCtrlResistiveLock keeps the field shunting off at zero and CHECK(locked->ScndCtrlPos == 1); } } - SUBCASE("the shunting still goes off on a resistive position") { + SUBCASE("the shunt goes down on a resistance-free position") { + controllers_at(locked, resistancefree); + REQUIRE(locked->IncScndCtrl(1)); + REQUIRE(locked->IncScndCtrl(1)); + CHECK(locked->DecScndCtrl(1)); + CHECK(locked->ScndCtrlPos == 1); + CHECK(locked->DecScndCtrl(2)); + CHECK(locked->ScndCtrlPos == 0); + } + SUBCASE("the shunt doesn't go down on a resistive position either") { + // a state the lock doesn't let arise (see below), set by hand to check the controller alone + controllers_at(locked, resistancefree - 1); + locked->ScndCtrlPos = 1; + CHECK(locked->IsScndCtrlLockedByMainCtrl()); + CHECK_FALSE(locked->DecScndCtrl(1)); + CHECK_FALSE(locked->DecScndCtrl(2)); + CHECK(locked->ScndCtrlPos == 1); + } + SUBCASE("the master controller taken back from a shunt step drops the shunt") { + controllers_at(locked, resistancefree); + REQUIRE(locked->IncScndCtrl(1)); + if (locked->TrainType == dt_ET22) { + // the ET22's master controller doesn't turn with the shunt on: the shunt goes off first, as before + CHECK_FALSE(locked->DecMainCtrl(1)); + CHECK(locked->MainCtrlPos == resistancefree); + CHECK(locked->DecScndCtrl(1)); + } + CHECK(locked->DecMainCtrl(1)); + CHECK(locked->MainCtrlPos == resistancefree - 1); + CHECK(locked->ScndCtrlPos == 0); + // and the master controller goes on down to zero, nothing left locked + while (locked->MainCtrlPos > 0 && locked->DecMainCtrl(1)) { + } + CHECK(locked->MainCtrlPos == 0); + } + SUBCASE("the master controller set directly (AI, consist) drops the shunt in the next update") { controllers_at(locked, resistancefree); REQUIRE(locked->IncScndCtrl(1)); locked->MainCtrlPos = resistancefree - 1; - CHECK(locked->DecScndCtrl(1)); + locked.step(); CHECK(locked->ScndCtrlPos == 0); + CHECK(locked->MainCtrlPos == resistancefree - 1); + } + SUBCASE("the master controller moved up from a shunt step onto a resistive position drops the shunt") { + // the last series position, followed by the resistive parallel ones + auto lastseries{0}; + for (auto position{resistancefree}; position > 0 && position < locked->MainCtrlPosNo; position = resistance_free_position(locked.mover(), position)) { + if (locked->RList[position + 1].R >= 0.01) { + lastseries = position; + break; + } + } + REQUIRE(lastseries > 0); + controllers_at(locked, lastseries); + locked->DirActive = 1; + REQUIRE(locked->IncScndCtrl(1)); + if (locked->TrainType == dt_ET22) { + CHECK_FALSE(locked->IncMainCtrl(1)); // the ET22's interlock, as before + CHECK(locked->ScndCtrlPos == 1); + } + else { + CHECK(locked->IncMainCtrl(1)); + CHECK(locked->MainCtrlPos == lastseries + 1); + CHECK(locked->ScndCtrlPos == 0); + } } } }