18
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-14 16:50:53 +01:00
Files
maszyna/tests/unit/test_cab.cpp
jerrrrycho 329dc4e42e feat: SM42 vigilance device switched off in the shunt mode (eu07/maszyna#457)
The shunt mode of the diesel-electrics (ShuntMode=1: SM42, SP42, SU42) now
suspends their vigilance device (CA): no alerter while running, no alarm for the
button held too long, and an alarm already on goes out. The cab signalling (SHP)
stays as it was. Back in the line mode the CA works as before.
2026-11-07 20:13:46 +01:00

265 lines
10 KiB
C++

#include "stdafx.h"
#include "doctest.h"
#include "consist.h"
#include "scenario.h"
#include "drive.h"
#include "McZapkie/hamulce.h"
#include "utilities/utilities.h"
// characterisation of what the cab (Train.cpp) and the AI (Driver.cpp, driverhints.cpp) call on a vehicle
// besides the controls of test_controls.cpp: the gauges and lamps reading other vehicles of the train, the
// controller of the electrodynamic brake apart from the local brake, the spring brake, the ep brake's fuse,
// cutting off damaged motors, the cab activated by itself, a road vehicle's place across the road, and the
// lamps and the buzzer of the security system
using test::consist;
using test::definition;
using test::scenario;
using test::vehicle;
using namespace test::channels;
TEST_CASE("cab: the engine rotation of up to three vehicles in multiple, on the tachometers of the SN61") {
consist train;
auto &first{train.add(definition::fiz("sn61_v2/sn61"), 1).mover()};
auto &second{train.add(definition::fiz("sn61_v2/sn61")).mover()};
auto &third{train.add(definition::fiz("sn61_v2/sn61")).mover()};
first.enrot = -10.0;
second.enrot = 20.0;
third.enrot = 30.0;
CHECK(first.ShowEngineRotation(1) == doctest::Approx(10.0));
CHECK(first.ShowEngineRotation(2) == doctest::Approx(20.0));
CHECK(first.ShowEngineRotation(3) == doctest::Approx(30.0));
CHECK(first.ShowEngineRotation(4) == doctest::Approx(0.0));
// the others look the same way along their couplers, front first
CHECK(second.ShowEngineRotation(2) == doctest::Approx(10.0));
CHECK(third.ShowEngineRotation(2) == doctest::Approx(20.0));
CHECK(third.ShowEngineRotation(3) == doctest::Approx(10.0));
// no control cable, no reading
consist apart;
auto const &alone{apart.add(definition::fiz("sn61_v2/sn61"), 1).mover()};
apart.add(definition::fiz("sn61_v2/sn61"), 0, coupling::coupler | coupling::brakehose);
CHECK(alone.ShowEngineRotation(2) == doctest::Approx(0.0));
}
TEST_CASE("cab: the lamps of the overload relay and the starting resistors, read through the control cable") {
consist unit;
auto &trailer{unit.add(definition::fiz("ed72_v1/5bs-ra"), 1).mover()};
auto &motor{unit.add(definition::fiz("ed72_v1/6bs-sa")).mover()};
REQUIRE(trailer.Power < 0.01);
REQUIRE(motor.Power > 0.01);
CHECK_FALSE(trailer.FuseFlagCheck());
CHECK_FALSE(trailer.ResistorsFlagCheck());
motor.FuseFlag = true;
motor.ResistorsFlag = true;
CHECK(trailer.FuseFlagCheck());
CHECK(trailer.ResistorsFlagCheck());
CHECK(motor.FuseFlagCheck());
CHECK(motor.ResistorsFlagCheck());
}
TEST_CASE("cab: the engine's idle speed, and the ET42's second controller shown in its dynamic brake") {
vehicle sn61{definition::fiz("sn61_v2/sn61")};
CHECK(sn61->EngineIdleRPM() == doctest::Approx(sn61->dizel_nmin * 60.0));
vehicle su45{definition::fiz("su45_v2/301d")};
CHECK(su45->EngineIdleRPM() == doctest::Approx(su45->DElist[su45->MainCtrlNoPowerPos()].RPM));
vehicle eu07{definition::fiz("4e_v1/4e")};
CHECK(eu07->EngineIdleRPM() == std::numeric_limits<double>::max());
vehicle et42{definition::fiz("et42_v2/112e-a")};
CHECK(et42->GetVirtualScndPos() == 0);
et42->DynamicBrakeFlag = true;
CHECK(et42->GetVirtualScndPos() == -1);
et42->ScndCtrlPos = 1;
CHECK(et42->GetVirtualScndPos() == 1);
// with a speed control in place of the second controller, either end of it, for the AI
vehicle railbus{definition::fiz("21xm_v1/214ma")};
REQUIRE(railbus->SpeedCtrl);
railbus->ScndCtrlPos = 1;
CHECK(railbus->IsScndCtrlNoPowerPos());
CHECK(railbus->IsScndCtrlMaxPowerPos());
CHECK(eu07->IsScndCtrlNoPowerPos());
CHECK_FALSE(eu07->IsScndCtrlMaxPowerPos());
}
TEST_CASE("cab: the controller of the electrodynamic brake apart from the local brake") {
// none of the game's vehicles has one yet (SplitEDPneumaticBrake): the electrodynamic brake follows its own
// controller of DBPN positions, the local brake is pneumatic alone
scenario scenario{"cab/split electrodynamic brake", motion() + traction() + brakes()};
scenario.add("e186_v2/eu47", 1, consist::full_coupling, 100.0).add("bdhpumn_v3/bdhpumn", 0, consist::full_coupling, 100.0);
auto &eu47{scenario.cab()};
CHECK_FALSE(eu47.IncDynamicBrakeLevel(1.f));
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(0.0));
scenario.train()[0].mover().SplitEDPneumaticBrake = true;
REQUIRE(scenario.start_up());
REQUIRE(eu47.DynamicBrakeCtrlPosNo == 10);
CHECK(eu47.DynamicBrakeAvailable());
scenario.act("dynamic brake up");
for (int i = 0; i < 5; ++i) {
CHECK(eu47.IncDynamicBrakeLevel(1.f));
}
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(0.5));
scenario.wait(10.0);
CHECK(eu47.DynamicBrakeFlag);
CHECK(eu47.Im < -50.0);
CHECK(eu47.BrakePress < 0.1);
scenario.act("dynamic brake full");
CHECK(eu47.DynamicBrakeLevelSet(2.0));
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(1.0));
CHECK_FALSE(eu47.IncDynamicBrakeLevel(1.f));
scenario.wait(10.0);
scenario.act("dynamic brake off");
for (int i = 0; i < 20 && eu47.DecDynamicBrakeLevel(1.f); ++i) {
// down a notch at a time
}
CHECK(eu47.DynamicBrakeRatio() == doctest::Approx(0.0));
// the local brake no longer brings the motors in
scenario.act("local brake");
eu47.IncLocalBrakeLevel(LocalBrakePosNo);
scenario.wait(5.0);
CHECK(eu47.BrakePress > 1.0);
scenario.check();
}
TEST_CASE("cab: the spring brake shut off and released by hand, the ep brake's fuse, the train brake handle stepped") {
scenario scenario{"cab/214m spring brake", motion() + brakes()};
scenario.add("21xm_v1/214ma", 1);
REQUIRE(scenario.start_up());
auto &railbus{scenario.cab()};
scenario.act("spring brake on");
railbus.SpringBrakeActivate(true);
scenario.wait(10.0);
CHECK(railbus.SpringBrake.IsActive);
// released by hand only with its cylinder empty of the air holding it off
auto const pressure{railbus.SpringBrake.Cylinder->P()};
CHECK(railbus.SpringBrakeRelease() == (pressure < railbus.SpringBrake.MinForcePressure));
scenario.act("shut off");
CHECK(railbus.SpringBrakeShutOff(true));
CHECK_FALSE(railbus.SpringBrakeShutOff(true));
scenario.wait(5.0);
CHECK(railbus.SpringBrakeShutOff(false));
scenario.act("spring brake off");
railbus.SpringBrakeActivate(false);
scenario.wait(10.0);
// the ep brake's fuse
CHECK(railbus.EpFuseSwitch(false));
CHECK_FALSE(railbus.EpFuseSwitch(false));
CHECK(railbus.EpFuseSwitch(true));
// the train brake handle stepped as the AI does, false at its ends
scenario.act("handle stepped");
railbus.BrakeLevelSet(railbus.Handle->GetPos(bh_RP));
CHECK(railbus.BrakeLevelAdd(1.0));
CHECK(railbus.BrakeCtrlPos == static_cast<int>(railbus.Handle->GetPos(bh_RP)) + 1);
CHECK(railbus.BrakeLevelAdd(-1.0));
CHECK_FALSE(railbus.BrakeLevelAdd(-10.0));
CHECK_FALSE(railbus.BrakeLevelAdd(100.0));
scenario.wait(5.0);
scenario.check();
}
TEST_CASE("cab: damaged motors cut off in a car without a driver") {
// cutting off takes the damage off the engine and half of the powered axles with it; only in a car nobody
// drives, with its engine damaged
vehicle driven{definition::fiz("ew58_v1/ew58-sa"), 0.0, "test", 1};
driven->BatterySwitch(true);
REQUIRE(driven->CabActivisation());
driven->DamageFlag |= dtrain_engine;
CHECK_FALSE(driven->CutOffEngine());
vehicle car{definition::fiz("ew58_v1/ew58-sa")};
auto const axles{car->NPoweredAxles};
CHECK_FALSE(car->CutOffEngine());
car->DamageFlag |= dtrain_engine;
CHECK(car->CutOffEngine());
CHECK(car->NPoweredAxles == axles / 2);
CHECK_FALSE(TestFlag(car->DamageFlag, dtrain_engine));
vehicle wagon{definition::fiz("3wl_v2/3wl")};
wagon->DamageFlag |= dtrain_engine;
CHECK_FALSE(wagon->CutOffEngine());
}
TEST_CASE("cab: activated by itself, where the vehicle allows it") {
for (auto const allowed : {false, true}) {
CAPTURE(allowed);
vehicle eu07{definition::fiz("4e_v1/4e").set("Cntrl", "AutomaticCabActivation", allowed ? "Yes" : "No"), 0.0, "test", 1};
eu07->BatterySwitch(true);
CHECK(eu07->CabActivisationAuto() == allowed);
CHECK((eu07->CabActive != 0) == allowed);
CHECK(eu07->CabDeactivisationAuto() == allowed);
CHECK(eu07->CabActive == 0);
}
}
TEST_CASE("cab: a road vehicle moved across its road") {
vehicle bus{definition::fiz("road/man/nl293")};
bus.step(test::track{0.15, 0.0, 0.0, 2, 1, 7.0});
REQUIRE(TestFlag(bus->CategoryFlag, 2));
CHECK(bus->ChangeOffsetH(0.5));
CHECK(bus->OffsetTrackH == doctest::Approx(0.5));
// off the edge of the road
CHECK_FALSE(bus->ChangeOffsetH(100.0));
vehicle eu07{definition::fiz("4e_v1/4e")};
CHECK_FALSE(eu07->ChangeOffsetH(0.5));
}
TEST_CASE("cab: the lamps and the buzzer of the security system left unattended") {
scenario scenario{"cab/security system", motion() + brakes()};
scenario.add("4e_v1/4e", 1);
REQUIRE(scenario.start_up());
auto &eu07{scenario.cab()};
scenario.get_going();
scenario.full_power(10.0);
auto const &security{eu07.SecuritySystem};
CHECK_FALSE(security.is_beeping());
scenario.act("unattended");
// the train runs with nobody acknowledging the vigilance
bool blinked{false};
bool beeped{false};
for (int i = 0; i < 120; ++i) {
scenario.train().run(1.0, scenario.line());
blinked |= security.is_vigilance_blinking();
beeped |= security.is_beeping();
}
CHECK(blinked);
CHECK(beeped);
CHECK_FALSE(security.is_cabsignal_beeping());
CHECK(eu07.PipePress < 1.0);
CHECK(security.radiostop_available());
scenario.check();
}
TEST_CASE("cab: the SM42's vigilance device switched off in the shunt mode") {
// eu07/maszyna#457: the shunt mode of the SM42 switches its vigilance device (CA) off, it alerts again
// once the shunt mode is off
scenario scenario{"cab/sm42 vigilance in shunt mode", motion() + brakes(), test::track{}};
scenario.add("sm42_v1/6d1", 1);
REQUIRE(scenario.start_up());
auto &sm42{scenario.cab()};
auto const &security{sm42.SecuritySystem};
REQUIRE(sm42.CurrentSwitch(true));
REQUIRE(sm42.ShuntMode);
scenario.get_going();
sm42.AnPos = 0.3;
sm42.IncMainCtrl(1);
scenario.act("unattended in shunt mode");
bool blinked{false};
for (int i = 0; i < 120; ++i) {
scenario.train().run(1.0, scenario.line());
blinked |= security.is_blinking();
}
CHECK(sm42.Vel > 1.0);
CHECK_FALSE(blinked);
CHECK(sm42.PipePress > 3.5);
scenario.act("shunt mode off");
scenario.notch_back(0);
REQUIRE(sm42.CurrentSwitch(false));
sm42.IncMainCtrl(1);
scenario.act("unattended");
for (int i = 0; i < 120; ++i) {
scenario.train().run(1.0, scenario.line());
blinked |= security.is_vigilance_blinking();
}
CHECK(blinked);
scenario.check();
}