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); } } } }