First devices

This commit is contained in:
Marc Riera 2024-10-09 22:54:28 +02:00
parent 51ef0ececc
commit 8d9c08276a
8 changed files with 539 additions and 21 deletions

111
src/Devices/Deadman.cs Normal file
View file

@ -0,0 +1,111 @@
using System;
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
{
/// <summary>A deadman switch device to stop the train if the driver does not periodically acknowledge it.</summary>
internal class Deadman : Device
{
/// <summary>Represents the state of the device.</summary>
private enum DeviceStates
{
/// <summary>The device is inactive.</summary>
Inactive,
/// <summary>The device is active.</summary>
Active,
/// <summary>The device is in emergency state.</summary>
Emergency,
}
/// <summary>The current state of the device.</summary>
private DeviceStates DeviceState;
/// <summary>The counter. This starts at zero and counts up until the brake delay time is reached.</summary>
private double Counter;
/// <summary>The delay time until the service brake is applied, in seconds.</summary>
private double BrakeDelay = 5.0;
/// <summary>Whether the train must be completely stopped to release the brakes.</summary>
private bool FullReset = false;
/// <summary>Is called when the device state should be updated.</summary>
/// <param name="train">The current train.</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, 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;
}
}
/// <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.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 & Math.Abs(train.State.Speed.KilometersPerHour) < 0.01)
{
// Reset the counter after the train has stopped
Counter = 0;
DeviceState = DeviceStates.Active;
}
break;
}
}
}
/// <summary>Is called when the plugin should perform the AI.</summary>
/// <param name="data">The AI data.</param>
/// <param name="train">The current train.</param>
internal override void PerformAI(AIData data, Train train)
{
if (DeviceState != DeviceStates.Inactive)
{
KeyChange(VirtualKeys.S, true, train);
}
}
}
}

64
src/Devices/Device.cs Normal file
View file

@ -0,0 +1,64 @@
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
{
/// <summary>Represents an abstract device.</summary>
internal abstract class Device
{
/// <summary>The power notch requested by the device.</summary>
internal int RequestedPowerNotch = -1;
/// <summary>The brake notch requested by the device.</summary>
internal int RequestedBrakeNotch = -1;
/// <summary>The door interlock requested by the device.</summary>
internal DoorInterlockStates RequestedDoorInterlock;
/// <summary>Is called when the device state should be updated.</summary>
/// <param name="train">The current train.</param>
/// <param name="init">Whether the device should initialize.</param>
/// <param name="elapsedTime">The time elapsed since the previous call.</param>
internal virtual void Update(Train train, bool init, Time elapsedTime) { }
/// <summary>Is called when the driver changes the reverser.</summary>
/// <param name="reverser">The new reverser position.</param>
internal virtual void SetReverser(int reverser) { }
/// <summary>Is called when the driver changes the power notch.</summary>
/// <param name="powerNotch">The new power notch.</param>
internal virtual void SetPower(int powerNotch) { }
/// <summary>Is called when the driver changes the brake notch.</summary>
/// <param name="brakeNotch">The new brake notch.</param>
internal virtual void SetBrake(int brakeNotch) { }
/// <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 virtual void KeyChange(VirtualKeys key, bool pressed, Train train) { }
/// <summary>Is called when the state of the doors changes.</summary>
/// <param name="oldState">The old state of the doors.</param>
/// <param name="newState">The new state of the doors.</param>
internal virtual void DoorChange(DoorStates oldState, DoorStates newState) { }
/// <summary>Is called when a horn is played or when the music horn is stopped.</summary>
/// <param name="type">The type of horn.</param>
internal virtual void HornBlow(HornTypes type) { }
/// <summary>Is called to inform about signals.</summary>
/// <param name="signal">The signal data.</param>
internal virtual void SetSignal(SignalData[] signal) { }
/// <summary>Is called when a beacon is passed.</summary>
/// <param name="beacon">The beacon data.</param>
internal virtual void SetBeacon(BeaconData beacon) { }
/// <summary>Is called when the plugin should perform the AI.</summary>
/// <param name="data">The AI data.</param>
/// <param name="train">The current train.</param>
internal virtual void PerformAI(AIData data, Train train) { }
}
}

167
src/Devices/Doors.cs Normal file
View file

@ -0,0 +1,167 @@
using System;
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
{
/// <summary>A device controlling the door operation.</summary>
internal class Doors : Device
{
/// <summary>Whether the left doors are closing.</summary>
private bool LeftDoorsClosing;
/// <summary>Whether the right doors are closing.</summary>
private bool RightDoorsClosing;
/// <summary>Is called when the device state should be updated.</summary>
/// <param name="train">The current train.</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, bool init, Time elapsedTime)
{
if (init)
{
// Set the selection state of the doors during initialization
switch (train.DoorState)
{
case DoorStates.None:
RequestedDoorInterlock = DoorInterlockStates.Locked;
break;
case DoorStates.Both:
RequestedDoorInterlock = DoorInterlockStates.Unlocked;
break;
case DoorStates.Left:
case DoorStates.Right:
RequestedDoorInterlock = (DoorInterlockStates)train.DoorState;
break;
}
}
// Cut power when the doors are open
RequestedPowerNotch = (train.DoorState != DoorStates.None) ? 0 : -1;
// Update panel variables for door selection state
train.Panel[50] = (RequestedDoorInterlock == DoorInterlockStates.Left || RequestedDoorInterlock == DoorInterlockStates.Unlocked) ? 1 : 0;
train.Panel[51] = (RequestedDoorInterlock == DoorInterlockStates.Right || RequestedDoorInterlock == DoorInterlockStates.Unlocked) ? 1 : 0;
}
/// <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.G:
// Change the selection state of the left doors
if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key])
{
// Button active only if the doors are not open
if ((train.DoorState & DoorStates.Left) == 0 || LeftDoorsClosing)
{
switch (RequestedDoorInterlock)
{
case DoorInterlockStates.Locked:
RequestedDoorInterlock = DoorInterlockStates.Left;
break;
case DoorInterlockStates.Left:
RequestedDoorInterlock = DoorInterlockStates.Locked;
break;
case DoorInterlockStates.Right:
RequestedDoorInterlock = DoorInterlockStates.Unlocked;
break;
case DoorInterlockStates.Unlocked:
RequestedDoorInterlock = DoorInterlockStates.Right;
break;
}
}
}
break;
case VirtualKeys.H:
// Change the selection state of the right doors
if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key])
{
// Button active only if the doors are not open
if ((train.DoorState & DoorStates.Right) == 0 || RightDoorsClosing)
{
switch (RequestedDoorInterlock)
{
case DoorInterlockStates.Locked:
RequestedDoorInterlock = DoorInterlockStates.Right;
break;
case DoorInterlockStates.Left:
RequestedDoorInterlock = DoorInterlockStates.Unlocked;
break;
case DoorInterlockStates.Right:
RequestedDoorInterlock = DoorInterlockStates.Locked;
break;
case DoorInterlockStates.Unlocked:
RequestedDoorInterlock = DoorInterlockStates.Left;
break;
}
}
}
break;
case VirtualKeys.LeftDoors:
if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key])
{
// Unselect doors automatically when closing
if ((train.DoorState & DoorStates.Left) > 0 && (RequestedDoorInterlock == DoorInterlockStates.Left || RequestedDoorInterlock == DoorInterlockStates.Unlocked))
{
LeftDoorsClosing = true;
RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Left ? DoorInterlockStates.Locked : DoorInterlockStates.Right;
}
}
break;
case VirtualKeys.RightDoors:
if (!OpenbveFcmbTrainPlugin.KeysPressed[(int)key])
{
// Unselect doors automatically when closing
if ((train.DoorState & DoorStates.Right) > 0 && (RequestedDoorInterlock == DoorInterlockStates.Right || RequestedDoorInterlock == DoorInterlockStates.Unlocked))
{
RightDoorsClosing = true;
RequestedDoorInterlock = RequestedDoorInterlock == DoorInterlockStates.Right ? DoorInterlockStates.Locked : DoorInterlockStates. Left;
}
}
break;
}
// TODO: OpenBVE does not trigger virtual buttons when the AI driver is active, needs changes to main program
}
}
/// <summary>Is called when the state of the doors changes.</summary>
/// <param name="oldState">The old state of the doors.</param>
/// <param name="newState">The new state of the doors.</param>
internal override void DoorChange(DoorStates oldState, DoorStates newState)
{
LeftDoorsClosing = false;
RightDoorsClosing = false;
}
/// <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>
internal override void PerformAI(AIData data, Train train)
{
//// Select the correct doors for the current station
//if (Stations.State != Stations.StationStates.Completed)
//{
// if (((Stations.Current.OpenLeftDoors && !LeftDoorsSelected) || (!Stations.Current.OpenLeftDoors && LeftDoorsSelected)) && !LeftDoorsClosing)
// {
// // Select or unselect the doors on the left.
// KeyDown(VirtualKeys.G);
// data.Response = AIResponse.Short;
// }
// if (((Stations.Current.OpenRightDoors && !RightDoorsSelected) || (!Stations.Current.OpenRightDoors && RightDoorsSelected)) && !RightDoorsClosing)
// {
// // Select or unselect the doors on the right.
// KeyDown(VirtualKeys.H);
// data.Response = AIResponse.Short;
// }
//}
}
}
}

10
src/Devices/TrainStop.cs Normal file
View file

@ -0,0 +1,10 @@
using System;
namespace OpenbveFcmbTrainPlugin
{
/// <summary>A train stop device to stop the train if a stop signal is passed.</summary>
internal class TrainStop : Device
{
}
}