diff --git a/OpenBveFcmbTrainPlugin.sln b/OpenbveFcmbTrainPlugin.sln
similarity index 82%
rename from OpenBveFcmbTrainPlugin.sln
rename to OpenbveFcmbTrainPlugin.sln
index b855c5f..97200f0 100644
--- a/OpenBveFcmbTrainPlugin.sln
+++ b/OpenbveFcmbTrainPlugin.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenBveFcmbTrainPlugin", "src/OpenBveFcmbTrainPlugin.csproj", "{9197FFA4-D95A-410C-A705-4E7CD187546A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenbveFcmbTrainPlugin", "src/OpenbveFcmbTrainPlugin.csproj", "{9197FFA4-D95A-410C-A705-4E7CD187546A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/src/Devices/Deadman.cs b/src/Devices/Deadman.cs
new file mode 100644
index 0000000..a541693
--- /dev/null
+++ b/src/Devices/Deadman.cs
@@ -0,0 +1,111 @@
+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 = false;
+
+ /// Is called when the device state should be updated.
+ /// The current train.
+ /// Whether the device should initialize.
+ /// The time elapsed since the previous call.
+ 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;
+ }
+ }
+
+ /// 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.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;
+ }
+ }
+ }
+
+ /// Is called when the plugin should perform the AI.
+ /// The AI data.
+ /// The current train.
+ internal override void PerformAI(AIData data, Train train)
+ {
+ if (DeviceState != DeviceStates.Inactive)
+ {
+ KeyChange(VirtualKeys.S, true, train);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Devices/Device.cs b/src/Devices/Device.cs
new file mode 100644
index 0000000..3bd0843
--- /dev/null
+++ b/src/Devices/Device.cs
@@ -0,0 +1,64 @@
+using OpenBveApi.Runtime;
+
+namespace OpenbveFcmbTrainPlugin
+{
+ /// Represents an abstract device.
+ internal abstract class Device
+ {
+
+ /// The power notch requested by the device.
+ internal int RequestedPowerNotch = -1;
+
+ /// The brake notch requested by the device.
+ internal int RequestedBrakeNotch = -1;
+
+ /// The door interlock requested by the device.
+ internal DoorInterlockStates RequestedDoorInterlock;
+
+ /// Is called when the device state should be updated.
+ /// The current train.
+ /// Whether the device should initialize.
+ /// The time elapsed since the previous call.
+ internal virtual void Update(Train train, bool init, Time elapsedTime) { }
+
+ /// Is called when the driver changes the reverser.
+ /// The new reverser position.
+ internal virtual void SetReverser(int reverser) { }
+
+ /// Is called when the driver changes the power notch.
+ /// The new power notch.
+ internal virtual void SetPower(int powerNotch) { }
+
+ /// Is called when the driver changes the brake notch.
+ /// The new brake notch.
+ internal virtual void SetBrake(int brakeNotch) { }
+
+ /// Is called when the state of a key changes.
+ /// The key.
+ /// Whether the key is pressed or released.
+ /// The current train.
+ internal virtual void KeyChange(VirtualKeys key, bool pressed, Train train) { }
+
+ /// Is called when the state of the doors changes.
+ /// The old state of the doors.
+ /// The new state of the doors.
+ internal virtual void DoorChange(DoorStates oldState, DoorStates newState) { }
+
+ /// Is called when a horn is played or when the music horn is stopped.
+ /// The type of horn.
+ internal virtual void HornBlow(HornTypes type) { }
+
+ /// Is called to inform about signals.
+ /// The signal data.
+ internal virtual void SetSignal(SignalData[] signal) { }
+
+ /// Is called when a beacon is passed.
+ /// The beacon data.
+ internal virtual void SetBeacon(BeaconData beacon) { }
+
+ /// Is called when the plugin should perform the AI.
+ /// The AI data.
+ /// The current train.
+ internal virtual void PerformAI(AIData data, Train train) { }
+ }
+}
diff --git a/src/Devices/Doors.cs b/src/Devices/Doors.cs
new file mode 100644
index 0000000..6d231dd
--- /dev/null
+++ b/src/Devices/Doors.cs
@@ -0,0 +1,167 @@
+using System;
+using OpenBveApi.Runtime;
+
+namespace OpenbveFcmbTrainPlugin
+{
+ /// A device controlling the door operation.
+ internal class Doors : Device
+ {
+ /// Whether the left doors are closing.
+ private bool LeftDoorsClosing;
+
+ /// Whether the right doors are closing.
+ private bool RightDoorsClosing;
+
+ /// Is called when the device state should be updated.
+ /// The current train.
+ /// Whether the device should initialize.
+ /// The time elapsed since the previous call.
+ 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;
+ }
+
+ /// 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.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
+ }
+ }
+
+ /// Is called when the state of the doors changes.
+ /// The old state of the doors.
+ /// The new state of the doors.
+ internal override void DoorChange(DoorStates oldState, DoorStates newState)
+ {
+ LeftDoorsClosing = false;
+ RightDoorsClosing = false;
+ }
+
+ /// Is called when the device should perform the AI.
+ /// The AI data.
+ /// The current train.
+ 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;
+ // }
+ //}
+ }
+ }
+}
+
diff --git a/src/Devices/TrainStop.cs b/src/Devices/TrainStop.cs
new file mode 100644
index 0000000..f2797cd
--- /dev/null
+++ b/src/Devices/TrainStop.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace OpenbveFcmbTrainPlugin
+{
+ /// A train stop device to stop the train if a stop signal is passed.
+ internal class TrainStop : Device
+ {
+
+ }
+}
diff --git a/src/OpenBveFcmbTrainPlugin.cs b/src/OpenbveFcmbTrainPlugin.cs
similarity index 78%
rename from src/OpenBveFcmbTrainPlugin.cs
rename to src/OpenbveFcmbTrainPlugin.cs
index 767f5e1..203e429 100644
--- a/src/OpenBveFcmbTrainPlugin.cs
+++ b/src/OpenbveFcmbTrainPlugin.cs
@@ -4,27 +4,25 @@ using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin
{
/// The interface to be implemented by the plugin.
- public partial class OpenbveFcmbTrainPlugin : IRuntime
+ public class OpenbveFcmbTrainPlugin : IRuntime
{
+ /// The train that is simulated by this plugin.
+ private Train Train;
- /// Holds the aspect last reported to the plugin in the SetSignal call.
- int LastAspect = -1;
-
- /// Holds the array of panel variables.
- /// Be sure to initialize in the Load call.
- public static int[] Panel;
+ /// Whether the train is initializing or reinitializing.
+ internal static bool Initializing = true;
/// Remembers which of the virtual keys are currently pressed down.
- public static bool[] KeysPressed = new bool[34];
-
- /// Whether the plugin is initializing or reinitializing.
- public static bool Initializing;
+ internal static bool[] KeysPressed = new bool[Enum.GetNames(typeof(VirtualKeys)).Length];
/// Is called when the plugin is loaded.
/// The properties supplied to the plugin on loading.
/// Whether the plugin was loaded successfully.
public bool Load(LoadProperties properties)
{
+ properties.AISupport = AISupport.Basic;
+ properties.Panel = new int[256];
+ Train = new Train(properties.Panel);
return true;
}
@@ -37,18 +35,24 @@ namespace OpenbveFcmbTrainPlugin
/// The specifications of the train.
public void SetVehicleSpecs(VehicleSpecs specs)
{
+ Train.SetVehicleSpecs(specs);
}
/// Is called when the plugin should initialize or reinitialize.
/// The mode of initialization.
public void Initialize(InitializationModes mode)
{
+ // On initialization, a variable is set so the first call to Elapse() can be used for initialization
+ Initializing = true;
+ Train.Initialize(mode);
}
/// Is called every frame.
/// The data passed to the plugin.
public void Elapse(ElapseData data)
{
+ Train.Elapse(data);
+ Initializing = false;
}
/// Is called when the driver changes the reverser.
@@ -73,12 +77,16 @@ namespace OpenbveFcmbTrainPlugin
/// The virtual key that was pressed.
public void KeyDown(VirtualKeys key)
{
+ Train.KeyDown(key);
+ KeysPressed[(int)key] = true;
}
/// Is called when a virtual key is released.
/// The virtual key that was released.
public void KeyUp(VirtualKeys key)
{
+ Train.KeyUp(key);
+ KeysPressed[(int)key] = false;
}
/// Is called when a horn is played or when the music horn is stopped.
@@ -92,16 +100,13 @@ namespace OpenbveFcmbTrainPlugin
/// The new state of the doors.
public void DoorChange(DoorStates oldState, DoorStates newState)
{
+ Train.DoorChange(oldState, newState);
}
/// Is called when the aspect in the current or in any of the upcoming sections changes, or when passing section boundaries.
/// The signal array is guaranteed to have at least one element. When accessing elements other than index 0, you must check the bounds of the array first.
public void SetSignal(SignalData[] signal)
{
- int aspect = signal[0].Aspect;
- if (aspect != LastAspect)
- {
- }
}
/// Is called when the train passes a beacon.
@@ -114,6 +119,7 @@ namespace OpenbveFcmbTrainPlugin
/// The AI data.
public void PerformAI(AIData data)
{
+ Train.PerformAI(data);
}
}
diff --git a/src/OpenBveFcmbTrainPlugin.csproj b/src/OpenbveFcmbTrainPlugin.csproj
similarity index 70%
rename from src/OpenBveFcmbTrainPlugin.csproj
rename to src/OpenbveFcmbTrainPlugin.csproj
index 244f830..54c6b9b 100644
--- a/src/OpenBveFcmbTrainPlugin.csproj
+++ b/src/OpenbveFcmbTrainPlugin.csproj
@@ -5,8 +5,8 @@
AnyCPU
{9197FFA4-D95A-410C-A705-4E7CD187546A}
Library
- OpenBveFcmbTrainPlugin
- OpenBveFcmbTrainPlugin
+ OpenbveFcmbTrainPlugin
+ OpenbveFcmbTrainPlugin
v4.7
@@ -29,13 +29,21 @@
- ..\..\..\..\lib\openbve\OpenBveApi.dll
- False
+ ..\..\..\Documents\OpenBVE\OpenBveApi.dll
-
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Train/Train.cs b/src/Train/Train.cs
new file mode 100644
index 0000000..8c5726f
--- /dev/null
+++ b/src/Train/Train.cs
@@ -0,0 +1,152 @@
+using System.Collections.Generic;
+using OpenBveApi.Runtime;
+
+namespace OpenbveFcmbTrainPlugin
+{
+ /// Represents a train that is simulated by this plugin.
+ internal class Train
+ {
+ /// The train panel variables.
+ internal int[] Panel;
+
+ /// The specifications of the train.
+ internal VehicleSpecs Specs { get; private set; }
+
+ /// The current state of the train.
+ internal VehicleState State { get; private set; }
+
+ /// The current state of the train doors.
+ internal DoorStates DoorState { get; private set; }
+
+ /// The previous state of the train doors.
+ internal DoorStates PreviousDoorState { get; private set; }
+
+ /// The latest initialization mode of the train.
+ internal InitializationModes InitializationMode { get; private set; }
+
+ /// The devices equipped in the train.
+ private List Devices;
+
+ /// 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;
+
+ /// 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());
+ }
+
+ /// Is called when the train should initialize.
+ /// The initialization mode.
+ internal void Initialize(InitializationModes mode)
+ {
+ InitializationMode = mode;
+ }
+
+ /// Is called after loading to inform the plugin about the specifications of the train.
+ /// The specifications of the train.
+ internal void SetVehicleSpecs(VehicleSpecs specs)
+ {
+ Specs = specs;
+ }
+
+ /// Is called every frame.
+ /// The data.
+ internal void Elapse(ElapseData data)
+ {
+ State = data.Vehicle;
+
+ // Reset data to be passed to the simulator
+ data.DoorInterlockState = DoorInterlockStates.Unlocked;
+ data.Handles.PowerNotch = data.Handles.PowerNotch;
+ data.Handles.BrakeNotch = data.Handles.BrakeNotch;
+
+ // Retrieve data from all devices
+ foreach (Device dev in Devices)
+ {
+ dev.Update(this, OpenbveFcmbTrainPlugin.Initializing, data.ElapsedTime);
+ data.DoorInterlockState |= dev.RequestedDoorInterlock;
+ if (dev.RequestedPowerNotch != -1) data.Handles.PowerNotch = dev.RequestedPowerNotch;
+ if (dev.RequestedBrakeNotch != -1) data.Handles.BrakeNotch = dev.RequestedBrakeNotch;
+ }
+ data.DebugMessage = DoorState.ToString() + " " + data.DoorInterlockState.ToString();
+ }
+
+ /// Is called when the driver changes the reverser.
+ /// The new reverser position.
+ internal void SetReverser(int reverser) { }
+
+ /// Is called when the driver changes the power notch.
+ /// The new power notch.
+ internal void SetPower(int powerNotch) { }
+
+ /// Is called when the driver changes the brake notch.
+ /// The new brake notch.
+ internal void SetBrake(int brakeNotch) { }
+
+ /// Is called when a key is pressed.
+ /// The key.
+ internal void KeyDown(VirtualKeys key)
+ {
+ foreach (Device dev in Devices)
+ {
+ dev.KeyChange(key, true, this);
+ }
+ }
+
+ /// Is called when a key is released.
+ /// The key.
+ internal void KeyUp(VirtualKeys key)
+ {
+ foreach (Device dev in Devices)
+ {
+ dev.KeyChange(key, false, this);
+ }
+ }
+
+ /// Is called when the state of the doors changes.
+ /// The old state of the doors.
+ /// The new state of the doors.
+ internal void DoorChange(DoorStates oldState, DoorStates newState)
+ {
+ PreviousDoorState = oldState;
+ DoorState = newState;
+ foreach (Device dev in Devices)
+ {
+ dev.DoorChange(oldState, newState);
+ }
+ }
+
+ /// Is called when a horn is played or when the music horn is stopped.
+ /// The type of horn.
+ internal void HornBlow(HornTypes type) { }
+
+ /// Is called to inform about signals.
+ /// The signal data.
+ internal void SetSignal(SignalData[] signal) { }
+
+ /// Is called when a beacon is passed.
+ /// The beacon data.
+ internal void SetBeacon(BeaconData beacon) { }
+
+ /// Is called when the plugin should perform the AI.
+ /// The AI data.
+ internal void PerformAI(AIData data)
+ {
+ foreach (Device dev in Devices)
+ {
+ dev.PerformAI(data, this);
+ }
+ }
+ }
+}