diff --git a/src/Devices/AtcDimetronic.cs b/src/Devices/AtcDimetronic.cs index 9f6c5d5..6ce85e1 100644 --- a/src/Devices/AtcDimetronic.cs +++ b/src/Devices/AtcDimetronic.cs @@ -1,4 +1,6 @@ -using OpenBveApi.Runtime; +using System.Collections.Generic; +using System.Globalization; +using OpenBveApi.Runtime; namespace OpenbveFcmbTrainPlugin { @@ -49,9 +51,6 @@ namespace OpenbveFcmbTrainPlugin /// The current state of the track. private TrackStates TrackState; - /// The upcoming signals in the route. - private SignalData[] Signals; - /// The maximum speed in YARD mode. private readonly Speed YardMaximumSpeed; @@ -64,7 +63,6 @@ namespace OpenbveFcmbTrainPlugin /// Whether ATO is available or not on the train. private readonly bool AtoAvailable; - /// Represents an ATC speed code. private class SpeedCode { @@ -72,13 +70,13 @@ namespace OpenbveFcmbTrainPlugin internal Speed WarningOn { get; private set; } internal Speed WarningOff { get; private set; } internal Speed TargetLimit { get; private set; } - internal double TargetLocation { get; private set; } + internal double TargetDistance { get; private set; } - internal SpeedCode(Speed limit, Speed target, double location) + internal SpeedCode(Speed limit, Speed target, double distance) { CurrentLimit = limit; TargetLimit = target; - TargetLocation = location; + TargetDistance = distance; if (limit == target) { // Constant speed @@ -97,11 +95,56 @@ namespace OpenbveFcmbTrainPlugin { } + /// Updates the ATC speed code. + /// The distance to the end of the signalling block. + internal void Update(double distance) + { + TargetDistance = distance; + } + } /// The current speed code received by the train. private SpeedCode CurrentSpeedCode; + /// Represents a signal code. + internal class SignalCode + { + internal int Aspect; + internal Speed Limit; + internal Speed Target; + + internal SignalCode(int aspect, string data) + { + Aspect = aspect; + Limit = new Speed(0); + Target = new Speed(0); + if (data.Contains("/")) + { + int separator = data.IndexOf('/'); + string a = data.Substring(0, separator); + string b = data.Substring(separator + 1); + if (double.TryParse(a, NumberStyles.Float, CultureInfo.InvariantCulture, out double an) && double.TryParse(b, NumberStyles.Float, CultureInfo.InvariantCulture, out double bn)) + { + if (an >= bn && an >= 0) + { + Limit = new Speed(an / 3.6); + } + if (bn <= an && bn >= 0) + { + Target = new Speed(bn / 3.6); + } + } + } + } + } + + /// The list of signal codes recognised by the device. + private readonly List SignalCodes; + + /// The aspect of the current signal in the route. + private int CurrentSignalAspect; + /// The ideal power notch for the AI. private int AiPowerNotch; @@ -110,10 +153,12 @@ namespace OpenbveFcmbTrainPlugin /// Creates an instance of the Bombardier ATC device. /// The maximum speed in YARD mode, in km/h. + /// The list of signal codes recognised by the device. /// Whether ATO is available or not. - internal AtcDimetronic(Speed yardMaxSpeed, bool atoAvailable) + internal AtcDimetronic(Speed yardMaxSpeed, List signalCodes, bool atoAvailable) { YardMaximumSpeed = yardMaxSpeed; + SignalCodes = signalCodes; AtoAvailable = atoAvailable; } @@ -207,11 +252,13 @@ namespace OpenbveFcmbTrainPlugin case DeviceStates.ATP: train.ContinuousProtection = true; train.VigilanceOverride = false; + CurrentSpeedCode = GetSpeedCodeFromAspect(SignalCodes, CurrentSignalAspect); break; // ATC device is in ATO driving mode case DeviceStates.ATO: train.ContinuousProtection = true; train.VigilanceOverride = true; + CurrentSpeedCode = GetSpeedCodeFromAspect(SignalCodes, CurrentSignalAspect); break; } @@ -265,8 +312,6 @@ namespace OpenbveFcmbTrainPlugin if (train.PhysicalHandles.Reverser == 1 && TrackState > TrackStates.Unprotected) { DeviceState = DeviceStates.ATP; - // Update ATC speed code - CurrentSpeedCode = new SpeedCode(AspectLimit[Signals[0].Aspect], AspectLimit[Signals[0].Aspect]); } } } @@ -281,8 +326,6 @@ namespace OpenbveFcmbTrainPlugin if (train.PhysicalHandles.Reverser == 1 && TrackState > TrackStates.Unprotected) { DeviceState = DeviceStates.ATO; - // Update ATC speed code - CurrentSpeedCode = new SpeedCode(AspectLimit[Signals[0].Aspect], AspectLimit[Signals[0].Aspect]); } } } @@ -310,6 +353,7 @@ namespace OpenbveFcmbTrainPlugin // Override device if possible if (stopped && train.PhysicalHandles.PowerNotch == 0) { + CurrentSpeedCode = new SpeedCode(new Speed(0), new Speed(0)); DeviceState = DeviceStates.Override; } break; @@ -331,12 +375,7 @@ namespace OpenbveFcmbTrainPlugin /// The signal data. internal override void SetSignal(SignalData[] signal) { - Signals = signal; - - if (DeviceState == DeviceStates.ATP || DeviceState == DeviceStates.ATO) - { - CurrentSpeedCode = new SpeedCode(AspectLimit[signal[0].Aspect], AspectLimit[signal[0].Aspect]); - } + CurrentSignalAspect = signal[0].Aspect; } /// Is called when a beacon is passed. @@ -364,13 +403,16 @@ namespace OpenbveFcmbTrainPlugin /// The current route. internal override void PerformAI(AIData data, Train train, Route route) { - // Set AI notches to what the AI is doing right now - AiBrakeNotch = data.Handles.BrakeNotch; - AiPowerNotch = data.Handles.PowerNotch; - double speed = train.State.Speed.KilometersPerHour; bool stopped = speed < 0.05; + // Set reverser to forward + if (data.Handles.Reverser != 1) + { + data.Handles.Reverser = 1; + data.Response = AIResponse.Short; + } + switch (DeviceState) { case DeviceStates.NoMode: @@ -386,46 +428,101 @@ namespace OpenbveFcmbTrainPlugin } break; case DeviceStates.YARD: + // Select ATP (M+ATP) mode if the train is stopped and receiving ATC codes from the track + if (stopped) + { + switch (TrackState) + { + case TrackStates.Enable: + case TrackStates.ATC: + KeyChange(VirtualKeys.J, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.J, false, train); + break; + } + } + CalculateAiNotches(data, train, route); + break; case DeviceStates.ATP: - // Calculate ideal notch for AI when approaching the speed limit - if (speed > CurrentSpeedCode.WarningOn.KilometersPerHour - 2 && train.Acceleration > 0.1) + // Select YARD (M+25) mode if the train is stopped and not receiving ATC codes from the track or a mode change is required + if (stopped) { - if (data.Handles.PowerNotch > 0) + switch (TrackState) { - AiPowerNotch -= 1; - } - else if (train.Specs.HasHoldBrake) - { - data.Handles.HoldBrake = true; - } - else - { - AiBrakeNotch += 1; - } - } - else if (speed < CurrentSpeedCode.WarningOff.KilometersPerHour - 2 && train.Acceleration < 0.25) - { - if (data.Handles.BrakeNotch > 0) - { - AiBrakeNotch -= 1; - } - else - { - AiPowerNotch += 1; - } - } - // If still far from next station, take control of AI handles - if (speed / (route.CurrentStation.StopPosition - train.State.Location) < 0.2) - { - if (speed > CurrentSpeedCode.CurrentLimit.KilometersPerHour - 5) - { - data.Handles.PowerNotch = AiPowerNotch; - data.Handles.BrakeNotch = AiBrakeNotch; - data.Response = AIResponse.Medium; + case TrackStates.Disable: + case TrackStates.Unprotected: + KeyChange(VirtualKeys.I, true, train); + data.Response = AIResponse.Short; + KeyChange(VirtualKeys.I, false, train); + break; } } + CalculateAiNotches(data, train, route); break; } } + + /// Calculates the ideal notches for the AI driver. + /// The AI data. + /// The current train. + /// The current route. + private void CalculateAiNotches(AIData data, Train train, Route route) + { + double speed = train.State.Speed.KilometersPerHour; + bool stopped = speed < 0.05; + + // Set AI notches to what the AI is doing right now + AiBrakeNotch = data.Handles.BrakeNotch; + AiPowerNotch = data.Handles.PowerNotch; + + // Calculate ideal notch for AI when approaching the speed limit + if (speed > CurrentSpeedCode.WarningOn.KilometersPerHour - 2 && train.Acceleration > 0.1) + { + if (data.Handles.PowerNotch > 0) + { + AiPowerNotch -= 1; + } + else + { + AiBrakeNotch += 1; + } + } + else if (speed < CurrentSpeedCode.WarningOff.KilometersPerHour - 2 && train.Acceleration < 0.25) + { + if (data.Handles.BrakeNotch > 0) + { + AiBrakeNotch -= 1; + } + else + { + AiPowerNotch += 1; + } + } + // If still far from next station, take control of AI handles + if (speed / (route.CurrentStation.StopPosition - train.State.Location) < 0.2) + { + if (speed > CurrentSpeedCode.CurrentLimit.KilometersPerHour - 5) + { + data.Handles.PowerNotch = AiPowerNotch; + data.Handles.BrakeNotch = AiBrakeNotch; + data.Response = AIResponse.Medium; + } + } + } + + /// Generates the corresponding speed code from a signal aspect. + /// The list of signal codes. + /// The current signal aspect. + private SpeedCode GetSpeedCodeFromAspect(List signalCodes, int aspect) + { + foreach (SignalCode signal in signalCodes) + { + if (signal.Aspect == aspect) + { + return new SpeedCode(signal.Limit, signal.Target); + } + } + return new SpeedCode(new Speed(0), new Speed(0)); + } } } diff --git a/src/Managers/ConfigManager.cs b/src/Managers/ConfigManager.cs index 50a1d77..4d1ebe7 100644 --- a/src/Managers/ConfigManager.cs +++ b/src/Managers/ConfigManager.cs @@ -1,4 +1,6 @@ using System; +using System.Globalization; +using System.Collections.Generic; using OpenBveApi.Runtime; namespace OpenbveFcmbTrainPlugin @@ -32,6 +34,7 @@ namespace OpenbveFcmbTrainPlugin internal Speed AtcDimetronicYardSpeedLimit = new Speed(25 / 3.6); internal bool AtcDimetronicAtoAvailable; + internal List AtcDimetronicSignalCodes = new List(); } /// Represents the plugin settings. @@ -207,6 +210,18 @@ namespace OpenbveFcmbTrainPlugin case "atoavailable": PluginSettings.AtcDimetronicAtoAvailable = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; break; + default: + int aspect; + if (int.TryParse(Key, NumberStyles.Integer, CultureInfo.InvariantCulture, out aspect)) + { + AtcDimetronic.SignalCode signalCode = new AtcDimetronic.SignalCode(aspect, Value); + if (signalCode != null) + { + PluginSettings.AtcDimetronicSignalCodes.Add(signalCode); + break; + } + } + break; } break; } diff --git a/src/Train/Train.cs b/src/Train/Train.cs index d7d17e1..e9e1fb8 100644 --- a/src/Train/Train.cs +++ b/src/Train/Train.cs @@ -98,7 +98,7 @@ namespace OpenbveFcmbTrainPlugin } if (settings.AtcDimetronicDeviceEnabled) { - Devices.Add(new AtcDimetronic(settings.AtcDimetronicYardSpeedLimit, settings.AtcDimetronicAtoAvailable)); + Devices.Add(new AtcDimetronic(settings.AtcDimetronicYardSpeedLimit, settings.AtcDimetronicSignalCodes, settings.AtcDimetronicAtoAvailable)); } }