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_brake_handles.cpp
jerrrrycho db65dfa5f0 test: file-local helpers instead of lambdas in the brake handle tests (Sonar S2259)
The analyser loses track of references captured by lambdas; the St113 EP and MHZ buttons tests call plain helpers, as ep_brake() and ready_knorr_ep() do, which it doesn't report.
2026-11-08 21:15:12 +01:00

268 lines
10 KiB
C++

#include "stdafx.h"
#include "doctest.h"
#include "vehicle.h"
#include "golden.h"
#include "real_vehicles.h"
#include "drive.h"
#include "consist.h"
#include "McZapkie/hamulce.h"
using test::definition;
using test::vehicle;
// characterisation of the driver's brake handles and the local brake handles of the real locomotives and
// units: every position of the handle, with the main reservoir kept full, followed in golden traces
// (data/golden/brake handles/). the vehicle runs alone, so the handle drives only its own brake pipe
namespace {
std::vector<test::channel> handle_channels() {
using namespace test::channels;
return brakes() + handles() + air();
}
// the vehicles with a driver's brake handle
bool has_handle(vehicle &Vehicle) {
return Vehicle->BrakeCtrlPosNo > 1;
}
void hold(vehicle &Vehicle, test::trace &Trace, std::string const &Event, double Position, double Seconds) {
Trace.mark(Event);
Vehicle->BrakeLevelSet(Position);
Vehicle.run(Seconds);
}
// every position of the driver's handle, recorded in the trace
void every_position(vehicle &Vehicle, test::trace &Trace) {
Vehicle.hold_main_reservoir(std::max(Vehicle->MaxCompressor, 8.0));
REQUIRE(Vehicle->Handle != nullptr);
auto &handle{*Vehicle->Handle};
Vehicle.record(Trace);
hold(Vehicle, Trace, "running", handle.GetPos(bh_RP), 90.0);
// every position from the lowest to the highest, emergency included, then back to running
auto const lowest{static_cast<int>(std::ceil(handle.GetPos(bh_MIN)))};
auto const highest{static_cast<int>(std::floor(handle.GetPos(bh_MAX)))};
for (auto position = lowest; position <= highest; ++position) {
hold(Vehicle, Trace, std::format("position {}", position), position, 15.0);
hold(Vehicle, Trace, "running", handle.GetPos(bh_RP), 30.0);
}
// filling stroke, neutral and the positions of the ep brake, where the handle has them
hold(Vehicle, Trace, "filling stroke", handle.GetPos(bh_FS), 10.0);
hold(Vehicle, Trace, "neutral", handle.GetPos(bh_NP), 20.0);
hold(Vehicle, Trace, "first step", handle.GetPos(bh_MB), 20.0);
hold(Vehicle, Trace, "full service", handle.GetPos(bh_FB), 20.0);
hold(Vehicle, Trace, "ep release", handle.GetPos(bh_EPR), 10.0);
hold(Vehicle, Trace, "ep hold", handle.GetPos(bh_EPN), 10.0);
hold(Vehicle, Trace, "ep brake", handle.GetPos(bh_EPB), 10.0);
hold(Vehicle, Trace, "running", handle.GetPos(bh_RP), 60.0);
}
// the handle put to the given position and the vehicle driven from its cab for Seconds, marked in the trace
void driven_at(vehicle &Vehicle, test::trace &Trace, std::string const &Event, double Position, double Seconds) {
Trace.mark(Event);
Vehicle->BrakeLevelSet(Position);
test::drive(test::driving(Vehicle), {}, Seconds);
}
} // namespace
TEST_CASE("brake handles: every position of the driver's handle of every real vehicle") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path)};
if (false == has_handle(real)) {
continue;
}
test::trace trace{"brake handles/" + path, handle_channels(), 100};
every_position(real, trace);
trace.check();
}
}
TEST_CASE("brake handles: the handles the game knows but none of its vehicles has, fitted to an EU07") {
// test is a handle for trying things out, D2 and Westinghouse fall back to the plain handle of the base class
for (auto const *name : {"test", "D2", "Westinghouse", "MHZ_EN57", "St113", "no such handle"}) {
CAPTURE(name);
test::reseed_random();
vehicle real{definition::fiz("4e_v1/4e").set("Cntrl", "BrakeHandle", name)};
test::trace trace{std::string{"brake handles/fitted/"} + name, handle_channels(), 100};
every_position(real, trace);
trace.check();
}
}
TEST_CASE("brake handles: the local brake handle of every real vehicle") {
for (auto const &path : test::real_vehicles()) {
CAPTURE(path);
test::reseed_random();
vehicle real{definition::fiz(path)};
if (false == has_handle(real)) {
continue;
}
real.hold_main_reservoir(std::max(real->MaxCompressor, 8.0));
test::trace trace{"local brake/" + path, handle_channels(), 100};
real.record(trace);
REQUIRE(real->Handle != nullptr);
real->BrakeLevelSet(real->Handle->GetPos(bh_RP));
trace.mark("charge");
real.run(60.0);
for (auto const position : {0.25, 0.5, 1.0, 0.5, 0.0}) {
trace.mark(std::format("local {:f}", position));
real->LocalBrakePosA = position;
real.run(15.0);
}
trace.check();
}
}
TEST_CASE("brake handles: the overload and the high pressure buttons of the MHZ handles") {
// the overload button charges the pipe over its running pressure (TP), which then decays; the high pressure
// button gives the filling stroke its overpressure, where the handle has one only with the button pressed.
// from the cab, with the battery on and the driver attending to the vigilance: the electro-pneumatic handles
// work only so
// the MHZ_EN57, whose unit doesn't charge its pipe alone, fitted to an EU07
for (auto const &[name, definition] : std::vector<std::pair<std::string, test::definition>>{
{"eu47", definition::fiz("e186_v2/eu47")},
{"e6act", definition::fiz("e6act_v2/e6acta")},
{"eu07 with MHZ_EN57", definition::fiz("4e_v1/4e").set("Cntrl", "BrakeHandle", "MHZ_EN57")}}) {
CAPTURE(name);
test::reseed_random();
vehicle real{definition, 0.0, "test", 1};
real->BatterySwitch(true);
real->CabActivisation();
real.hold_main_reservoir(std::max(real->MaxCompressor, 8.0));
test::trace trace{"brake handles/buttons/" + name, handle_channels(), 100};
real.record(trace);
REQUIRE(real->Handle != nullptr);
auto &handle{*real->Handle};
driven_at(real, trace, "running", handle.GetPos(bh_RP), 60.0);
auto const running{real->PipePress};
handle.SetUniversalFlag(TUniversalBrake::ub_Overload);
driven_at(real, trace, "overload button", handle.GetPos(bh_RP), 30.0);
handle.SetUniversalFlag(0);
auto const overloaded{real->PipePress};
driven_at(real, trace, "overload decaying", handle.GetPos(bh_RP), 240.0);
auto const decayed{real->PipePress};
handle.SetUniversalFlag(TUniversalBrake::ub_HighPressure);
driven_at(real, trace, "high pressure button, filling stroke", handle.GetPos(bh_FS), 10.0);
auto const filled{real->PipePress};
handle.SetUniversalFlag(0);
driven_at(real, trace, "running", handle.GetPos(bh_RP), 120.0);
CHECK(running == doctest::Approx(5.0).epsilon(0.01));
CHECK(overloaded > running + 0.3);
CHECK(decayed < overloaded);
CHECK(filled >= decayed - 0.05);
trace.check();
}
}
// eu07/maszyna#134: the Knorr St113 of the EN57-001 releases the EP brake in the running position (bh_RP, Num 6)
// and applies it one position on (Num 8), short of the lap position (Num 5); the positions are those the keys
// select, and the EP brake works only with the control of the cab powered: the battery, the reverser out of
// neutral and the EP fuse on (Ctrl+Z), as the Oerlikon handle's
namespace {
// the front car of the EN57-001 driven from its cab, with the motor car behind it, which has the battery of the two;
// air in the reservoirs, the battery on
void ready_knorr_ep(test::consist &Unit) {
Unit.add(definition::fiz("en57_v1/6bai"), 1);
Unit.add(definition::fiz("en57_v1/5bgb"));
auto &cab{Unit[0]};
REQUIRE(cab->BrakeHandle == TBrakeHandle::St113);
REQUIRE(cab->Handle != nullptr);
cab->BatterySwitch(true);
cab->CabActivisation();
cab.hold_brake_pipe(5.0);
cab->BrakeLevelSet(cab->Handle->GetPos(bh_RP));
Unit.run(60.0);
}
// the EP brake button pressed in the given handle position, held for Seconds; the cylinder pressure of the cab car
double ep_brake(test::consist &Unit, double Position, double Seconds) {
auto &cab{Unit[0]};
cab->BrakeLevelSet(Position);
Unit.run(0.1);
cab->SwitchEPBrake(1);
Unit.run(Seconds);
return cab->BrakePress;
}
// the EP force the St113 handle of the cab car gives in the given position
double ep_at(test::consist &Unit, double Position) {
auto &cab{Unit[0]};
cab->BrakeLevelSet(Position);
Unit.run(0.1);
return cab->Handle->GetEP();
}
} // namespace
TEST_CASE("brake handles: the positions of the Knorr St113 EP brake") {
test::consist unit;
ready_knorr_ep(unit);
auto &cab{unit[0]};
REQUIRE(cab->Handle != nullptr);
auto &handle{*cab->Handle};
// the keys: Num 6 running, Num 8 the first position on, Num 5 the middle of the handle
CHECK(handle.GetPos(bh_RP) == doctest::Approx(0.0));
CHECK(ep_at(unit, 0.0) == doctest::Approx(-1.0)); // EP release
CHECK(ep_at(unit, 1.0) == doctest::Approx(1.0)); // EP brake
CHECK(ep_at(unit, cab->BrakeCtrlPosNo / 2) == doctest::Approx(0.0)); // lap, Num 5
CHECK(ep_at(unit, handle.GetPos(bh_FB)) == doctest::Approx(0.0));
CHECK(ep_at(unit, handle.GetPos(bh_EPR)) == doctest::Approx(-1.0));
CHECK(ep_at(unit, handle.GetPos(bh_EPB)) == doctest::Approx(1.0));
}
TEST_CASE("brake handles: the Knorr St113 EP brake needs the control of the cab powered") {
for (std::string const cut : {"reverser in neutral", "EP fuse off", "battery off"}) {
CAPTURE(cut);
test::consist unit;
ready_knorr_ep(unit);
auto &cab{unit[0]};
cab->DirectionForward();
unit.run(1.0);
REQUIRE(cab->EPBrakeControlPowered());
if (cut == "reverser in neutral") {
cab->DirectionBackward();
}
else if (cut == "EP fuse off") {
cab->EpFuseSwitch(false);
}
else {
cab->BatterySwitch(false);
}
unit.run(1.0);
REQUIRE_FALSE(cab->EPBrakeControlPowered());
// the button in the EP brake position does nothing
CHECK(ep_brake(unit, 1.0, 5.0) < 0.1);
CHECK(cab->EpForce == doctest::Approx(0.0));
cab->SwitchEPBrake(0);
}
test::consist unit;
ready_knorr_ep(unit);
auto &cab{unit[0]};
cab->DirectionForward();
unit.run(1.0);
// powered, the EP brake applies
auto const applied{ep_brake(unit, 1.0, 2.0)};
CHECK(applied > 0.5);
// the reverser put to neutral with the button still held: the EP brake drops out and holds what it applied
cab->DirectionBackward();
unit.run(0.1);
CHECK(cab->EpForce == doctest::Approx(0.0));
auto const held{cab->BrakePress};
unit.run(5.0);
CHECK(cab->BrakePress < held + 0.05);
CHECK(cab->BrakePress > 0.2);
// and doesn't release either, until the control is powered again
CHECK(ep_brake(unit, 0.0, 5.0) > 0.2);
cab->SwitchEPBrake(0);
cab->DirectionForward();
unit.run(0.1);
CHECK(ep_brake(unit, 0.0, 10.0) < 0.1);
}