From f1e9c3e654cbef1a24c11a3555d90934aac07b1d Mon Sep 17 00:00:00 2001 From: Marc Riera Date: Tue, 15 Oct 2024 23:29:31 +0200 Subject: [PATCH] Bombardier ATC --- src/Devices/AtcBombardier.cs | 287 ++++++++++++++++++++++++++++++ src/OpenbveFcmbTrainPlugin.cs | 3 + src/OpenbveFcmbTrainPlugin.csproj | 1 + src/Train/Train.cs | 34 +++- 4 files changed, 322 insertions(+), 3 deletions(-) create mode 100644 src/Devices/AtcBombardier.cs diff --git a/src/Devices/AtcBombardier.cs b/src/Devices/AtcBombardier.cs new file mode 100644 index 0000000..832bfa2 --- /dev/null +++ b/src/Devices/AtcBombardier.cs @@ -0,0 +1,287 @@ +using System; +using OpenBveApi.Runtime; + +namespace OpenbveFcmbTrainPlugin +{ + /// A device simulating ATC by Bombardier. + internal class AtcBombardier : Device + { + /// Represents the state of the device. + private enum DeviceStates + { + /// The device is being overriden. + Override, + /// The device is initializing. + Initializing, + /// The device has been initialized and no driving mode is selected. + Initialized, + /// The device is in YARD (M+25) driving mode. + YARD, + /// The device is in ATP (M+ATP) driving mode. + ATP, + /// The device is in ATO mode. + ATO, + } + + /// The current state of the device. + private DeviceStates DeviceState; + + /// Represents the state of the device. + private enum AtcBrakeStates + { + /// The brakes are released. + Released, + /// The service brake is applied and can be released. + Service, + /// The service brake is applied and cannot be released. + ServiceNoRelease, + /// The service brake is fully applied. + Full, + /// The emergency brake is applied. + Emergency, + } + + /// The current state of the ATC brake. + private AtcBrakeStates AtcBrakeState; + + /// The time needed by the ATC device to initialize, in seconds. + private double InitializationTime = 30; + + /// A counter to keep track of initialization time, in seconds. + private double InitializationCounter; + + /// The blinking time for lamps, in milliseconds. + private double BlinkTime = 500; + + /// A counter to keep track of blink time, in milliseconds. + private double BlinkCounter; + + /// The time after which YARD mode enters idle state and engages the service brake, in seconds. + private double YardIdleTime = 60; + + /// A counter to keep track of YARD mode idle time, in seconds. + private double YardIdleCounter; + + /// Is called when the device state should be updated. + /// The current train. + /// The current route. + /// Whether the device should initialize. + /// The time elapsed since the previous call. + internal override void Update(Train train, Route route, bool init, Time elapsedTime) + { + if (init) + { + // Initialize device on start + DeviceState = DeviceStates.Initializing; + } + + switch (DeviceState) + { + // ATC device is disabled (also called "Special Mode") + case DeviceStates.Override: + train.ContinuousProtection = false; + train.VigilanceOverride = false; + // Release control of the brakes and power + RequestedBrakeNotch = -1; + RequestedPowerNotch = -1; + break; + // ATC device is initializing + case DeviceStates.Initializing: + train.ContinuousProtection = false; + train.VigilanceOverride = false; + // Apply emergency brake during initialization + AtcBrakeState = AtcBrakeStates.Emergency; + // Wait during initialization time + InitializationCounter += elapsedTime.Seconds; + // If initialization is complete, change the state of the device + if (InitializationCounter > InitializationTime) + { + DeviceState = DeviceStates.Initialized; + InitializationCounter = 0; + } + break; + // ATC device is initialized and a driving mode is required + case DeviceStates.Initialized: + train.ContinuousProtection = false; + train.VigilanceOverride = false; + // Apply service brake while no driving mode is selected + AtcBrakeState = AtcBrakeStates.ServiceNoRelease; + break; + // ATC device is in YARD (M+25) driving mode + case DeviceStates.YARD: + train.ContinuousProtection = false; + train.VigilanceOverride = false; + // If the train is not moving, count idle time + if (train.State.Speed.KilometersPerHour < 0.05) + { + YardIdleCounter += elapsedTime.Seconds; + } + else + { + YardIdleCounter = 0; + } + // If above idle time, apply the service brake + if (YardIdleCounter >= YardIdleTime) + { + AtcBrakeState = AtcBrakeStates.Service; + } + else + { + AtcBrakeState = AtcBrakeStates.Released; + } + break; + // ATC device is in ATP (M+ATP) driving mode + case DeviceStates.ATP: + train.ContinuousProtection = true; + train.VigilanceOverride = false; + break; + // ATC device is in ATO driving mode + case DeviceStates.ATO: + train.ContinuousProtection = true; + train.VigilanceOverride = true; + break; + } + + if (DeviceState != DeviceStates.Override) + { + // Brake application + switch (AtcBrakeState) + { + case AtcBrakeStates.Released: + RequestedBrakeNotch = -1; + break; + case AtcBrakeStates.Service: + case AtcBrakeStates.ServiceNoRelease: + RequestedBrakeNotch = train.ServiceBrakeNotch; + break; + case AtcBrakeStates.Emergency: + RequestedBrakeNotch = train.Specs.BrakeNotches + 1; + break; + } + } + // Blinking light animation + BlinkCounter += elapsedTime.Milliseconds; + bool blink = BlinkCounter >= BlinkTime; + if (BlinkCounter > (BlinkTime * 2)) BlinkCounter = 0; + + // Panel indicators + train.Panel[25] = DeviceState == DeviceStates.Override ? 1 : 0; + train.Panel[26] = DeviceState == DeviceStates.YARD ? 1 : 0; + train.Panel[26] += DeviceState == DeviceStates.Initialized && train.State.Speed.KilometersPerHour < 0.05 && blink ? 1 : 0; + train.Panel[27] = DeviceState == DeviceStates.ATP ? 1 : 0; + train.Panel[28] = DeviceState == DeviceStates.ATP ? 1 : 0; + switch (AtcBrakeState) + { + case AtcBrakeStates.Released: + case AtcBrakeStates.Emergency: + train.Panel[32] = 0; + break; + case AtcBrakeStates.ServiceNoRelease: + case AtcBrakeStates.Full: + train.Panel[32] = 1; + break; + case AtcBrakeStates.Service: + train.Panel[32] = blink ? 1 : 0; + break; + } + } + + + /// Is called when the state of a key changes. + /// The key. + /// Whether the key is pressed or released. + /// The current train. + internal override void KeyChange(VirtualKeys key, bool pressed, Train train) + { + if (pressed) + { + switch (key) + { + case VirtualKeys.I: + // YARD (M+25) mode selection button + if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) + { + if (DeviceState == DeviceStates.Initialized && train.State.Speed.KilometersPerHour < 0.05) + { + DeviceState = DeviceStates.YARD; + } + } + break; + case VirtualKeys.J: + // ATP (M+ATP) mode selection button + if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) + { + } + break; + case VirtualKeys.K: + // ATO mode selection button + if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) + { + } + break; + case VirtualKeys.L: + // ATO start button + if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) + { + } + break; + case VirtualKeys.B1: + // ATC brake release + if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) + { + // Allow brake release under certain conditions + if (AtcBrakeState == AtcBrakeStates.Service) + { + AtcBrakeState = AtcBrakeStates.Released; + // Reset YARD idle counter + YardIdleCounter = 0; + } + } + break; + case VirtualKeys.C1: + // Override toggle + if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key]) + { + switch (DeviceState) + { + case DeviceStates.Initializing: + case DeviceStates.Initialized: + case DeviceStates.YARD: + // Override device if possible + if (train.State.Speed.KilometersPerHour < 0.05 && train.PhysicalHandles.PowerNotch == 0) + { + DeviceState = DeviceStates.Override; + } + break; + case DeviceStates.Override: + // Disable override if possible + if (train.State.Speed.KilometersPerHour < 0.05 && train.PhysicalHandles.PowerNotch == 0) + { + DeviceState = DeviceStates.Initializing; + } + break; + } + } + break; + } + } + } + + /// Is called when a beacon is passed. + /// The beacon data. + internal override void SetBeacon(BeaconData beacon) + { + + } + + /// Is called when the device should perform the AI. + /// The AI data. + /// The current train. + /// The current route. + internal override void PerformAI(AIData data, Train train, Route route) + { + + } + + } +} diff --git a/src/OpenbveFcmbTrainPlugin.cs b/src/OpenbveFcmbTrainPlugin.cs index dcc400e..a1e805b 100644 --- a/src/OpenbveFcmbTrainPlugin.cs +++ b/src/OpenbveFcmbTrainPlugin.cs @@ -64,18 +64,21 @@ namespace OpenbveFcmbTrainPlugin /// The new reverser position. public void SetReverser(int reverser) { + Train.SetReverser(reverser); } /// Is called when the driver changes the power notch. /// The new power notch. public void SetPower(int powerNotch) { + Train.SetPower(powerNotch); } /// Is called when the driver changes the brake notch. /// The new brake notch. public void SetBrake(int brakeNotch) { + Train.SetBrake(brakeNotch); } /// Is called when a virtual key is pressed. diff --git a/src/OpenbveFcmbTrainPlugin.csproj b/src/OpenbveFcmbTrainPlugin.csproj index e6fc0c3..b52c989 100644 --- a/src/OpenbveFcmbTrainPlugin.csproj +++ b/src/OpenbveFcmbTrainPlugin.csproj @@ -41,6 +41,7 @@ + diff --git a/src/Train/Train.cs b/src/Train/Train.cs index cf62add..68cec90 100644 --- a/src/Train/Train.cs +++ b/src/Train/Train.cs @@ -6,6 +6,14 @@ namespace OpenbveFcmbTrainPlugin /// Represents a train that is simulated by this plugin. internal class Train { + /// Represents the driver's handles. + internal class DriverHandles + { + internal int Reverser; + internal int PowerNotch; + internal int BrakeNotch; + } + /// The train panel variables. internal int[] Panel; @@ -27,12 +35,18 @@ namespace OpenbveFcmbTrainPlugin /// The devices equipped in the train. private List Devices; + /// The physical handles of the train. + internal DriverHandles PhysicalHandles { get; private set; } + /// Whether the train's vigilance device is overridden. internal bool VigilanceOverride; /// Whether the train is using a continuous protection system instead of an intermittent protection system. internal bool ContinuousProtection; + /// The service brake notch for ATC. + internal int ServiceBrakeNotch { get; private set; } + /// Creates a new train with the device configuration provided. /// The array of panel variables.(); Devices.Add(new Doors()); Devices.Add(new Deadman()); Devices.Add(new TrainStop()); + Devices.Add(new AtcBombardier()); } /// Is called when the train should initialize. @@ -58,6 +74,9 @@ namespace OpenbveFcmbTrainPlugin internal void SetVehicleSpecs(VehicleSpecs specs) { Specs = specs; + + // Set the service brake notch for ATC + ServiceBrakeNotch = specs.BrakeNotches > 1 ? specs.BrakeNotches - 1 : 1; } /// Is called every frame. @@ -84,15 +103,24 @@ namespace OpenbveFcmbTrainPlugin /// Is called when the driver changes the reverser. /// The new reverser position. - internal void SetReverser(int reverser) { } + internal void SetReverser(int reverser) + { + PhysicalHandles.Reverser = reverser; + } /// Is called when the driver changes the power notch. /// The new power notch. - internal void SetPower(int powerNotch) { } + internal void SetPower(int powerNotch) + { + PhysicalHandles.PowerNotch = powerNotch; + } /// Is called when the driver changes the brake notch. /// The new brake notch. - internal void SetBrake(int brakeNotch) { } + internal void SetBrake(int brakeNotch) + { + PhysicalHandles.BrakeNotch = brakeNotch; + } /// Is called when a key is pressed. /// The key.