feat(blackbox): the cab panel as fits the vehicle, every switch the driver worked in the events

- The rest of the cab record is read when written: what drives the
  vehicle, the doors' permits and departure signal, the brakes of the
  whole train, a diesel's oil and temperatures, the cruise control and
  the rest.
- Cab panel: the traction as fits what drives the vehicle (voltage and
  current of an electric one; revolutions, oil pressure and temperatures
  of a diesel; clutch or torque converter of a mechanical or hydraulic
  one; nothing for a draisine), the equalizing reservoir, the main
  reservoir pipe and the most braked cylinder of the train, the brakes'
  setting and mode, a group of the doors, the pumps of a diesel where an
  electric vehicle has its pantographs, the hand brake, the releaser, the
  horn's tone, the radio's channel, the integrated handle of a unit.
- Events: every switch on and off, doors opened and closed, the departure
  signal, the hand brake, the brakes' setting, the radio channel, the
  cruise control's speed; moving with the doors open and an overheating
  engine as faults.
- A vehicle with no lamps at an end (a draisine) is not judged on its
  lights, and one which never had a voltage shows none.
This commit is contained in:
maj00r
2026-11-11 02:43:51 +01:00
parent 4dcfd1c4e3
commit 0d16524953
9 changed files with 585 additions and 89 deletions

View File

@@ -165,6 +165,13 @@ public sealed class BlackBoxReader
// The lamps the vehicles at the ends have, which a pattern is checked against, later again.
if (r.Remaining >= 4)
sample = sample with { EquippedHead = (Lights)r.U16(), EquippedTail = (Lights)r.U16() };
// What drives it, the doors' permits and the rest, later again; the arguments are read in their order.
if (r.Remaining >= 54)
sample = sample with
{
Extra = new CabExtra((EngineKind)r.U8(), r.U8(), r.U8(), r.F32(), r.F32(), r.F32(), r.F32(), r.F32(),
r.F32(), r.F32(), r.F32(), r.I8(), r.U8(), r.U8(), r.F32(), r.F32(), r.F32(), r.F32())
};
rec.AddCab(sample);
break;
}

View File

@@ -71,7 +71,7 @@ public static class HeadCodes
};
/// <summary>Whether the head of the train showed what it was to; null when nothing tells it.</summary>
public static bool? HeadRight(CabSample sample) => sample.Movement switch
public static bool? HeadRight(CabSample sample) => NoLamps(sample.EquippedHead) ? null : sample.Movement switch
{
Movement.Train => Meets(sample.LightsHead, sample.EquippedHead, sample.LightHintHead, HeadCode.Pc1),
Movement.Shunting => Shows(HeadCode.Tb1, sample.LightsHead, sample.EquippedHead),
@@ -79,7 +79,7 @@ public static class HeadCodes
};
/// <summary>Whether the tail of the train showed what it was to; null when nothing tells it.</summary>
public static bool? TailRight(CabSample sample) => sample.Movement switch
public static bool? TailRight(CabSample sample) => NoLamps(sample.EquippedTail) ? null : sample.Movement switch
{
Movement.Train => Meets(sample.LightsTail, sample.EquippedTail, sample.LightHintTail, HeadCode.Pc5),
Movement.Shunting => Shows(HeadCode.Tb1, sample.LightsTail, sample.EquippedTail),
@@ -87,6 +87,12 @@ public static class HeadCodes
};
/// <summary>Whether both ends showed what they were to; null when nothing tells it.</summary>
/// <summary>
/// An end with no lamp at all, a draisine's: the simulator still asks it for a signal it can't show, so it
/// is not judged. A recording made before the lamps were written can't tell.
/// </summary>
public static bool NoLamps(Lights? equipped) => equipped == Lights.None;
public static bool? Right(CabSample sample) =>
HeadRight(sample) is { } head && TailRight(sample) is { } tail ? head && tail : null;

View File

@@ -79,9 +79,56 @@ public enum CabDevices : uint
Heating = 0x2000,
/// <summary>Applied.</summary>
SpringBrake = 0x4000,
ShuntMode = 0x8000
ShuntMode = 0x8000,
/// <summary>Sounding.</summary>
DepartureSignal = 0x0001_0000,
/// <summary>The doors may be opened.</summary>
DoorPermitLeft = 0x0002_0000,
DoorPermitRight = 0x0004_0000,
/// <summary>The steps come out with the doors.</summary>
DoorStep = 0x0008_0000,
/// <summary>The doors lock above a speed.</summary>
DoorLock = 0x0010_0000,
/// <summary>Locked now.</summary>
DoorsLocked = 0x0020_0000,
/// <summary>Releasing the brakes of the vehicle.</summary>
BrakeReleaser = 0x0040_0000,
FuelPump = 0x0080_0000,
OilPump = 0x0100_0000,
/// <summary>The fans of the traction motors.</summary>
MotorBlowers = 0x0200_0000,
/// <summary>The diesel engine is over its temperature.</summary>
EngineHot = 0x0400_0000,
/// <summary>The cruise control holds a speed.</summary>
SpeedControl = 0x0800_0000
}
/// <summary>What drives the vehicle whose motors the cab drives.</summary>
public enum EngineKind : byte
{
/// <summary>Nothing of its own: a draisine, a car.</summary>
None = 0,
Electric = 1,
ElectricInduction = 2,
/// <summary>With a mechanical or a hydraulic transmission.</summary>
Diesel = 3,
DieselElectric = 4,
Steam = 5
}
/// <summary>
/// The rest of a look at the user's train, written from the same version as the doors' permits: what drives it,
/// the brakes' setting and mode, the hand brake, the radio, the horn, the pressures of the equalizing reservoir
/// and the main reservoir pipe, a diesel's oil, temperatures, clutch and torque converter, the cruise control,
/// the power and the integrated controller of a unit, the highest brake cylinder pressure of the train. The
/// temperatures and the oil mean something for a diesel only.
/// </summary>
public readonly record struct CabExtra(EngineKind Engine, byte BrakeDelay, byte RadioChannel, float EqualizingReservoir,
float FeedPipe, float OilPressure, float WaterTemperature, float OilTemperature,
float EngineTemperature, float SpeedControlKmh, float EnginePower, sbyte HandBrake,
byte BrakeMode, byte Horn, float IntegratedController, float Clutch, float ConverterFill,
float ConsistBrakeCylinder);
/// <summary>The train protection: the alerter (CA) and the cab signalling (SHP) apart.</summary>
[Flags]
public enum SecurityFlags : byte
@@ -319,7 +366,7 @@ public readonly record struct CabSample(double Time, int Vehicle, Position Posit
Lights LightsHead, Lights LightsTail, sbyte CabEnd = 0,
SecurityFlags Security = SecurityFlags.None, float LateralAcceleration = 0,
Movement Movement = Movement.Unknown, short LightHintHead = -1, short LightHintTail = -1,
Lights? EquippedHead = null, Lights? EquippedTail = null)
Lights? EquippedHead = null, Lights? EquippedTail = null, CabExtra? Extra = null)
{
public bool Has(CabDevices device) => (Devices & device) == device;
public bool Has(SecurityFlags flag) => (Security & flag) == flag;