18
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-15 17:34:09 +01:00

feat: lock field shunting at zero and on resistive positions

New optional Cntrl. key ScndCtrlResistiveLock=Yes: IncScndCtrl() refuses
to step the shunt controller up while the master controller is at its
no-power position or on a resistive RList position (R >= 0.01), as the
interlock of the ET21, ET22 and EP09 does. Decreasing the shunt stays
allowed so it can't get stuck. The AI doesn't choose field shunting
while the lock applies. Without the key nothing changes.

Game data: the key is to be added to the Cntrl. line of the ET21
(et21_v2), ET22 (et22_v3, et22-2000_v2) and EP09 (ep09_v1, ep09_v2)
.fiz files that define it; until then they behave as before.

(eu07/maszyna#402)
This commit is contained in:
jerrrrycho
2026-11-07 21:01:54 +01:00
parent 939e42b611
commit 5bc4e7cd2e
4 changed files with 92 additions and 2 deletions

View File

@@ -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;

View File

@@ -2751,7 +2751,7 @@ bool TMoverParameters::IncScndCtrl(int CtrlSpeed)
{
// if (RList[MainCtrlPos].R=0) and (MainCtrlPos>0) and (ScndCtrlPos<ScndCtrlPosNo) and
// (not CoupledCtrl) then
if (ScndCtrlPos < ScndCtrlPosNo && !CoupledCtrl)
if (ScndCtrlPos < ScndCtrlPosNo && !CoupledCtrl && !IsScndCtrlLockedByMainCtrl())
{
// TBD, TODO: refactor this validation as part of relay check routine (currently in tractionforce()
// TBD, TODO: diesel electric engine utilize scndctrlactualpos like the other types?
@@ -2872,6 +2872,12 @@ bool TMoverParameters::DecScndCtrl(int CtrlSpeed)
return OK;
}
bool TMoverParameters::IsScndCtrlLockedByMainCtrl() const
{
// ET21, ET22, EP09: the field shunting engages only on the resistance-free positions of the master controller (eu07/maszyna#402)
return ScndCtrlResistiveLock && (IsMainCtrlNoPowerPos() || RList[MainCtrlPos].R >= 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, "");

View File

@@ -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);
}
}
}

View File

@@ -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 :