Bombardier ATC

This commit is contained in:
Marc Riera 2024-10-15 23:29:31 +02:00
parent 3aae76416c
commit f1e9c3e654
4 changed files with 322 additions and 3 deletions

View file

@ -0,0 +1,287 @@
using System;
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
{
/// <summary>A device simulating ATC by Bombardier.</summary>
internal class AtcBombardier : Device
{
/// <summary>Represents the state of the device.</summary>
private enum DeviceStates
{
/// <summary>The device is being overriden.</summary>
Override,
/// <summary>The device is initializing.</summary>
Initializing,
/// <summary>The device has been initialized and no driving mode is selected.</summary>
Initialized,
/// <summary>The device is in YARD (M+25) driving mode.</summary>
YARD,
/// <summary>The device is in ATP (M+ATP) driving mode.</summary>
ATP,
/// <summary>The device is in ATO mode.</summary>
ATO,
}
/// <summary>The current state of the device.</summary>
private DeviceStates DeviceState;
/// <summary>Represents the state of the device.</summary>
private enum AtcBrakeStates
{
/// <summary>The brakes are released.</summary>
Released,
/// <summary>The service brake is applied and can be released.</summary>
Service,
/// <summary>The service brake is applied and cannot be released.</summary>
ServiceNoRelease,
/// <summary>The service brake is fully applied.</summary>
Full,
/// <summary>The emergency brake is applied.</summary>
Emergency,
}
/// <summary>The current state of the ATC brake.</summary>
private AtcBrakeStates AtcBrakeState;
/// <summary>The time needed by the ATC device to initialize, in seconds.</summary>
private double InitializationTime = 30;
/// <summary>A counter to keep track of initialization time, in seconds.</summary>
private double InitializationCounter;
/// <summary>The blinking time for lamps, in milliseconds.</summary>
private double BlinkTime = 500;
/// <summary>A counter to keep track of blink time, in milliseconds.</summary>
private double BlinkCounter;
/// <summary>The time after which YARD mode enters idle state and engages the service brake, in seconds.</summary>
private double YardIdleTime = 60;
/// <summary>A counter to keep track of YARD mode idle time, in seconds.</summary>
private double YardIdleCounter;
/// <summary>Is called when the device state should be updated.</summary>
/// <param name="train">The current train.</param>
/// <param name="route">The current route.</param>
/// <param name="init">Whether the device should initialize.</param>
/// <param name="elapsedTime">The time elapsed since the previous call.</param>
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;
}
}
/// <summary>Is called when the state of a key changes.</summary>
/// <param name="key">The key.</param>
/// <param name="pressed">Whether the key is pressed or released.</param>
/// <param name="train">The current train.</param>
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;
}
}
}
/// <summary>Is called when a beacon is passed.</summary>
/// <param name="beacon">The beacon data.</param>
internal override void SetBeacon(BeaconData beacon)
{
}
/// <summary>Is called when the device should perform the AI.</summary>
/// <param name="data">The AI data.</param>
/// <param name="train">The current train.</param>
/// <param name="route">The current route.</param>
internal override void PerformAI(AIData data, Train train, Route route)
{
}
}
}