Black box: the queries of the user's train worked out once a read, lights as the driver checks them

- The stations, the signals passed, the horn points, the couplings, the
  consists and the timetable progress of the user's train are kept until
  a read adds something; the views asked for them every frame, which on
  a long session with many trains took the window down. A read that adds
  nothing no longer counts as a change.
- The lights are checked against the lamps the vehicles at the ends have
  (written by the simulator from now on), as its driver checks them: Pc 1
  without the upper headlight, with the auxiliary lamps, Pc 5 with the
  rear-end signals. Only while the user drives: the autopilot's lights
  are its own.
- A train off the platform counts at both ends: its head past the stop
  point as well as its tail before the platform's end.
- A recording without the couplings keeps the timetable for any vehicle
  of the user.
- A signal passed at danger is ringed on the map only once it was.
- In the tapes window a black box opens on a click or Enter, not when
  the arrow keys go over it.
- The viewer's screenshot of a window not laid out saves nothing and
  still closes.
This commit is contained in:
maj00r
2026-11-11 00:57:57 +01:00
parent e530b95352
commit 532875fc7b
14 changed files with 254 additions and 52 deletions

View File

@@ -108,7 +108,7 @@ internal sealed class BlackBoxFile
float target = 40, float nextLimit = -1, float nextDistance = -1, float brakeCylinder = 0,
uint devices = 0x0007, bool extended = true, sbyte cabEnd = 1, byte security = 0,
float lateral = 0.2f, ushort lightsHead = 0x15, ushort lightsTail = 0x22, byte? movement = null,
short hintHead = -1, short hintTail = -1) =>
short hintHead = -1, short hintTail = -1, ushort? equippedHead = null, ushort? equippedTail = null) =>
Record(0x20, w =>
{
w.Write(time);
@@ -154,6 +154,12 @@ internal sealed class BlackBoxFile
w.Write(kind);
w.Write(hintHead);
w.Write(hintTail);
// the lamps the vehicles at the ends have: a recording made before them ends above
if (equippedHead is { } head)
{
w.Write(head);
w.Write(equippedTail ?? head);
}
}
}
});

View File

@@ -68,6 +68,62 @@ public class HeadCodeTests
Assert.False(fault.TailWrong);
}
private static Recording DriveWith(ushort head, ushort tail, ushort equippedHead, ushort equippedTail, byte movement = 1, byte driving = 1)
{
var f = new BlackBoxFile().Info();
for (double t = 0; t < 30; t += 0.5)
f.Cab(t, 1, 1000 + t * 10, 40, driving: driving, lightsHead: head, lightsTail: tail, movement: movement,
equippedHead: equippedHead, equippedTail: equippedTail);
return BlackBoxReader.Read(f.End(30).Stream());
}
[Fact]
public void The_lamps_the_vehicles_have_are_read()
{
var sample = DriveWith(Pc1, Pc5, 0x1B5, 0x22).Cab[0];
Assert.Equal((Lights)0x1B5, sample.EquippedHead);
Assert.Equal((Lights)0x22, sample.EquippedTail);
}
[Theory]
// without the upper headlight Pc 1 is the two others
[InlineData(0x11, 0x11)]
// with auxiliary lamps, those and the upper headlight
[InlineData(0x184, 0x1B5)]
// a vehicle with no lamps for it: the rear-end signals
[InlineData(0x40, 0x40)]
public void Pc1_is_what_the_vehicle_can_show_of_it(ushort lit, ushort equipped)
{
var summary = DriveWith(lit, Pc5, equipped, Pc5).Summarize();
Assert.True(summary.LightsJudgedSeconds > 25);
Assert.Empty(summary.LightsFaults);
}
[Fact]
public void A_tail_with_the_rear_end_signals_only_shows_Pc5_with_them()
{
Assert.Empty(DriveWith(Pc1, 0x40, Pc1, 0x40).Summarize().LightsFaults);
var fault = Assert.Single(DriveWith(Pc1, 0x00, Pc1, 0x40).Summarize().LightsFaults);
Assert.False(fault.HeadWrong);
Assert.True(fault.TailWrong);
}
[Fact]
public void A_vehicle_with_all_the_lamps_is_to_light_all_three_for_Pc1()
{
var fault = Assert.Single(DriveWith(0x11, Pc5, 0x35, Pc5).Summarize().LightsFaults);
Assert.True(fault.HeadWrong);
Assert.Equal(HeadCode.Pc1, fault.HeadRequired);
}
[Fact]
public void The_lights_the_autopilot_sets_are_not_the_users()
{
var summary = DriveWith(Tb1, Tb1, Pc1, Pc5, driving: 2).Summarize();
Assert.Equal(0, summary.LightsJudgedSeconds);
Assert.Empty(summary.LightsFaults);
}
[Fact]
public void An_older_recording_tells_nothing_of_the_signals()
{

View File

@@ -31,6 +31,22 @@ public class PlatformTests
Assert.Equal(201, stop.PlatformLength);
}
[Fact]
public void A_train_whose_head_stands_past_the_stop_point_is_off_the_platform_by_that_much()
{
var stop = Stop(-5f, 129.8f, 201);
Assert.False(stop.OnPlatform);
Assert.Equal(5, stop.OffPlatform, 1);
}
[Fact]
public void A_metre_off_still_counts_as_along_the_platform()
{
var stop = Stop(-1f, 129.8f, 201);
Assert.True(stop.OnPlatform);
Assert.Equal(1, stop.OffPlatform, 1);
}
[Fact]
public void A_train_within_the_platform_is_along_it()
{