17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-18 18:29:40 +02:00
Files
maszyna/tests/unit/vehicle.h
jerrrrycho ec345f94cb fix: automatic coupler crashes, horns, compressor start delay, radio and cab lever sounds
- automatic couplers don't catch above the safe collision speed (#86): with crash damage on they couple at a
  closing speed of 15 km/h at most, a faster hit locks the coupling until the vehicles part and the collision
  runs its course. On the way: CollisionVelocity() read a head-on 10 + 10 km/h as 14.1 km/h (quaternions turn
  by half the angle), it's now taken from the touching ends; CollisionDetect() took the other vehicle's speed
  the wrong way round for vehicles facing each other. Head-on hits now derail sooner, at the intended limit,
- a single horn lever can't sound both tones at once (eu07/maszyna#392),
- a skid ends once the vehicle stops on locked wheels (eu07/maszyna#479),
- electronic destination signs are blanked at the end of the timetable (eu07/maszyna#446),
- horns need the air or the voltage to sound (eu07/maszyna#290, #76): HornType=[Pneumatic]|Electric and
  Horn3Type in Cntrl.; a pneumatic horn needs 0.2 MPa in the main reservoir (or the main pipe without one),
  an electric one the low voltage; the lever stays where it was put,
- compressor start delay (#67): CompressorStartDelay [s] in Brake., counted from the compressor getting its
  power, after the engine starts say; none by default,
- the channel and volume buttons of a radio without power do nothing (eu07/maszyna#429); the power check of
  the radio is TMoverParameters::RadioPowered(),
- a lever moved smoothly sounds the positions it passes (#83).

Sonar findings in the functions changed: nesting, nested conditionals, commented-out code, std::format,
references to const. Unit tests: test::pair for two vehicles running into each other, test_couplers,
test_horn, test_wheelslip, test_compressor, test_radio, test_gauge_positions.
2026-10-17 14:41:59 +02:00

122 lines
4.7 KiB
C++

#pragma once
#include <array>
#include <memory>
#include <stdexcept>
#include <string>
#include <nlohmann/json.hpp>
#include "McZapkie/MOVER.h"
namespace test {
// thrown for a test vehicle definition which can't be used: missing, malformed, or not taken by the loader
class definition_error : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
// definition of a test vehicle: one of the JSON files in tests/unit/data, with the sections and keys
// of the game's vehicle definitions, changed by the test where it needs to:
// auto heavy = definition::load("wagon").set("Param", "M", 60000).set("Brake", "BrakeValve", "ESt3");
class definition {
public:
// tests/unit/data/<Name>.json
static definition load(std::string const &Name);
// sets the key of the section, adding either one when it's missing
definition &set(std::string const &Section, std::string const &Key, nlohmann::ordered_json const &Value);
// drops the whole section, or one key of it
definition &remove(std::string const &Section);
definition &remove(std::string const &Section, std::string const &Key);
// the definition in the form the game's vehicle loader reads
std::string text() const;
private:
nlohmann::ordered_json m_sections;
};
// conditions the vehicle runs in: straight track of the given friction and gradient (rise/run)
struct track {
double friction{0.15};
double gradient{0.0};
};
// one vehicle created the way TDynamicObject::Init() creates it, from the given definition
class vehicle {
public:
explicit vehicle(definition const &Definition, double Velocity = 0.0, std::string const &Name = "test");
TMoverParameters &mover() { return *m_mover; }
TMoverParameters *operator->() { return m_mover.get(); }
// keeps the brake pipe at the given pressure [bar], as a locomotive coupled to the vehicle would;
// a negative value leaves the pipe alone
void hold_brake_pipe(double Pressure) { m_pipepressure = Pressure; }
// advances the physics by one step of Dt, the way the simulation loop does
void step(track const &Track = {}, double Dt = 0.01);
// advances the physics by Seconds in steps of Dt
void run(double Seconds, track const &Track = {}, double Dt = 0.01);
// vehicle speed in kilometres per hour, negative when moving backwards
double speed() const { return m_mover->V * 3.6; }
private:
friend class pair;
// the two halves of a step: the forces, taken by all the vehicles in the scene first, then the movement,
// the vehicle placed at Location. returns the distance travelled [m], positive forwards
void compute_forces(double Dt);
double compute_movement(track const &Track, double Dt, TLocation const &Location);
std::unique_ptr<TMoverParameters> m_mover;
double m_pipepressure{-1.0};
};
// two vehicles on a straight track, the FirstEnd of the first one facing the SecondEnd of the second one Gap metres
// away (from coupler to coupler), with nothing else around. a step runs them the way the simulation runs the
// vehicles of a scene (vehicle_table::update()): each one finds its neighbour, then both take the forces, the
// coupler and buffer ones among them (CouplerForce()), then both move and handle the collisions (CollisionDetect()).
// they couple and part on their own, as the couplers let them; the speed of each runs along its own axis
// test::pair pair{moving, end::front, standing, end::rear, 1.0 /* m */};
// pair.run(5.0 /* s */);
// CHECK(pair.coupled());
class pair {
public:
pair(vehicle &First, int FirstEnd, vehicle &Second, int SecondEnd, double Gap);
~pair();
pair(pair const &) = delete;
pair &operator=(pair const &) = delete;
// the vehicles point at the stand-ins of the pair, which stay where they are
pair(pair &&) = delete;
pair &operator=(pair &&) = delete;
// advances both vehicles by one step of Dt
void step(track const &Track = {}, double Dt = 0.01);
// advances both vehicles by Seconds in steps of Dt
void run(double Seconds, track const &Track = {}, double Dt = 0.01);
// distance between the facing couplers [m], negative while the buffers are pressed in
double gap() const;
// type of the coupling between the vehicles, coupling::faux when they aren't coupled
int coupling() const;
bool coupled() const { return coupling() != coupling::faux; }
private:
struct placement {
vehicle *owner{nullptr};
int end{0}; // the end facing the other vehicle
double position{0.0}; // of the middle of the vehicle along the track [m]
double heading{1.0}; // +1 when the vehicle runs forwards towards the growing position, -1 otherwise
std::unique_ptr<std::byte[]> standin; // what the other vehicle sees as this one, see vehicle.cpp
};
void find_neighbours();
std::array<placement, 2> m_vehicles;
};
} // namespace test