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.