using System;
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
{
/// A deadman switch device to stop the train if the driver does not periodically acknowledge it.
internal class Deadman : Device
{
/// Represents the state of the device.
private enum DeviceStates
{
/// The device is inactive.
Inactive,
/// The device is active.
Active,
/// The device is in emergency state.
Emergency,
}
/// The current state of the device.
private DeviceStates DeviceState;
/// The counter. This starts at zero and counts up until the brake delay time is reached.
private double Counter;
/// The delay time until the service brake is applied, in seconds.
private double BrakeDelay = 5.0;
/// Whether the train must be completely stopped to release the brakes.
private bool FullReset;
/// Creates an instance of the Deadman device.
/// The delay before the brakes are applied, in seconds.
/// Whether the train must be completely stopped to release the brakes.
internal Deadman(double brakeDelay, bool fullReset)
{
BrakeDelay = brakeDelay;
FullReset = fullReset;
}
/// 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 (train.VigilanceOverride)
{
// The train wants to override vigilance devices, so the device is inactive
DeviceState = DeviceStates.Inactive;
}
switch (DeviceState)
{
case DeviceStates.Active:
// The device is active, update the counter every frame
Counter += elapsedTime.Seconds;
RequestedBrakeNotch = -1;
if (Counter > BrakeDelay)
{
// Switch to emergency mode when the counter reaches the brake delay time
DeviceState = DeviceStates.Emergency;
}
break;
case DeviceStates.Inactive:
// The device is being overridden, release the brakes
RequestedBrakeNotch = -1;
if (!train.VigilanceOverride)
{
// If the device is no longer overridden, it should be active again
DeviceState = DeviceStates.Active;
}
break;
case DeviceStates.Emergency:
// Apply the brakes when the device is in emergency mode
RequestedBrakeNotch = train.Specs.BrakeNotches;
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)
{
double speed = train.State.Speed.KilometersPerHour;
bool stopped = speed < 0.05;
if (pressed)
{
switch (key)
{
case VirtualKeys.S:
if (DeviceState == DeviceStates.Active || (DeviceState == DeviceStates.Emergency & !FullReset))
{
// Reset the counter if the delay time has not been exceeded or if a full reset is not required
Counter = 0;
DeviceState = DeviceStates.Active;
}
else if (DeviceState == DeviceStates.Emergency && stopped)
{
// Reset the counter after the train has stopped
Counter = 0;
DeviceState = DeviceStates.Active;
}
break;
}
}
}
/// Is called when the plugin should perform the AI.
/// The AI data.
/// The current train.
/// The current route.
internal override void PerformAI(AIData data, Train train, Route route)
{
if (DeviceState != DeviceStates.Inactive)
{
KeyChange(VirtualKeys.S, true, train);
}
}
}
}