ATC Dimetronic: Allow custom speed codes

This commit is contained in:
Marc Riera 2024-12-22 01:25:28 +01:00
parent e962d851b2
commit 77869e9c1e
3 changed files with 169 additions and 57 deletions

View file

@ -1,4 +1,6 @@
using OpenBveApi.Runtime; using System.Collections.Generic;
using System.Globalization;
using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin namespace OpenbveFcmbTrainPlugin
{ {
@ -49,9 +51,6 @@ namespace OpenbveFcmbTrainPlugin
/// <summary>The current state of the track.</summary> /// <summary>The current state of the track.</summary>
private TrackStates TrackState; private TrackStates TrackState;
/// <summary>The upcoming signals in the route.</summary>
private SignalData[] Signals;
/// <summary>The maximum speed in YARD mode.</summary> /// <summary>The maximum speed in YARD mode.</summary>
private readonly Speed YardMaximumSpeed; private readonly Speed YardMaximumSpeed;
@ -64,7 +63,6 @@ namespace OpenbveFcmbTrainPlugin
/// <summary>Whether ATO is available or not on the train.</summary> /// <summary>Whether ATO is available or not on the train.</summary>
private readonly bool AtoAvailable; private readonly bool AtoAvailable;
/// <summary>Represents an ATC speed code.</summary> /// <summary>Represents an ATC speed code.</summary>
private class SpeedCode private class SpeedCode
{ {
@ -72,13 +70,13 @@ namespace OpenbveFcmbTrainPlugin
internal Speed WarningOn { get; private set; } internal Speed WarningOn { get; private set; }
internal Speed WarningOff { get; private set; } internal Speed WarningOff { get; private set; }
internal Speed TargetLimit { 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; CurrentLimit = limit;
TargetLimit = target; TargetLimit = target;
TargetLocation = location; TargetDistance = distance;
if (limit == target) if (limit == target)
{ {
// Constant speed // Constant speed
@ -97,11 +95,56 @@ namespace OpenbveFcmbTrainPlugin
{ {
} }
/// <summary>Updates the ATC speed code.</summary>
/// <param name="distance">The distance to the end of the signalling block.</param>
internal void Update(double distance)
{
TargetDistance = distance;
}
} }
/// <summary>The current speed code received by the train.</summary> /// <summary>The current speed code received by the train.</summary>
private SpeedCode CurrentSpeedCode; private SpeedCode CurrentSpeedCode;
/// <summary>Represents a signal code.</summary>
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);
}
}
}
}
}
/// <summary>The list of signal codes recognised by the device.</summary>
private readonly List<SignalCode> SignalCodes;
/// <summary>The aspect of the current signal in the route.</summary>
private int CurrentSignalAspect;
/// <summary>The ideal power notch for the AI.</summary> /// <summary>The ideal power notch for the AI.</summary>
private int AiPowerNotch; private int AiPowerNotch;
@ -110,10 +153,12 @@ namespace OpenbveFcmbTrainPlugin
/// <summary>Creates an instance of the Bombardier ATC device.</summary> /// <summary>Creates an instance of the Bombardier ATC device.</summary>
/// <param name="yardMaxSpeed">The maximum speed in YARD mode, in km/h.</param> /// <param name="yardMaxSpeed">The maximum speed in YARD mode, in km/h.</param>
/// <param name="signalCodes">The list of signal codes recognised by the device.</param>
/// <param name="atoAvailable">Whether ATO is available or not.</param> /// <param name="atoAvailable">Whether ATO is available or not.</param>
internal AtcDimetronic(Speed yardMaxSpeed, bool atoAvailable) internal AtcDimetronic(Speed yardMaxSpeed, List<SignalCode> signalCodes, bool atoAvailable)
{ {
YardMaximumSpeed = yardMaxSpeed; YardMaximumSpeed = yardMaxSpeed;
SignalCodes = signalCodes;
AtoAvailable = atoAvailable; AtoAvailable = atoAvailable;
} }
@ -207,11 +252,13 @@ namespace OpenbveFcmbTrainPlugin
case DeviceStates.ATP: case DeviceStates.ATP:
train.ContinuousProtection = true; train.ContinuousProtection = true;
train.VigilanceOverride = false; train.VigilanceOverride = false;
CurrentSpeedCode = GetSpeedCodeFromAspect(SignalCodes, CurrentSignalAspect);
break; break;
// ATC device is in ATO driving mode // ATC device is in ATO driving mode
case DeviceStates.ATO: case DeviceStates.ATO:
train.ContinuousProtection = true; train.ContinuousProtection = true;
train.VigilanceOverride = true; train.VigilanceOverride = true;
CurrentSpeedCode = GetSpeedCodeFromAspect(SignalCodes, CurrentSignalAspect);
break; break;
} }
@ -265,8 +312,6 @@ namespace OpenbveFcmbTrainPlugin
if (train.PhysicalHandles.Reverser == 1 && TrackState > TrackStates.Unprotected) if (train.PhysicalHandles.Reverser == 1 && TrackState > TrackStates.Unprotected)
{ {
DeviceState = DeviceStates.ATP; 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) if (train.PhysicalHandles.Reverser == 1 && TrackState > TrackStates.Unprotected)
{ {
DeviceState = DeviceStates.ATO; 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 // Override device if possible
if (stopped && train.PhysicalHandles.PowerNotch == 0) if (stopped && train.PhysicalHandles.PowerNotch == 0)
{ {
CurrentSpeedCode = new SpeedCode(new Speed(0), new Speed(0));
DeviceState = DeviceStates.Override; DeviceState = DeviceStates.Override;
} }
break; break;
@ -331,12 +375,7 @@ namespace OpenbveFcmbTrainPlugin
/// <param name="signal">The signal data.</param> /// <param name="signal">The signal data.</param>
internal override void SetSignal(SignalData[] signal) internal override void SetSignal(SignalData[] signal)
{ {
Signals = signal; CurrentSignalAspect = signal[0].Aspect;
if (DeviceState == DeviceStates.ATP || DeviceState == DeviceStates.ATO)
{
CurrentSpeedCode = new SpeedCode(AspectLimit[signal[0].Aspect], AspectLimit[signal[0].Aspect]);
}
} }
/// <summary>Is called when a beacon is passed.</summary> /// <summary>Is called when a beacon is passed.</summary>
@ -364,13 +403,16 @@ namespace OpenbveFcmbTrainPlugin
/// <param name="route">The current route.</param> /// <param name="route">The current route.</param>
internal override void PerformAI(AIData data, Train train, Route 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; double speed = train.State.Speed.KilometersPerHour;
bool stopped = speed < 0.05; bool stopped = speed < 0.05;
// Set reverser to forward
if (data.Handles.Reverser != 1)
{
data.Handles.Reverser = 1;
data.Response = AIResponse.Short;
}
switch (DeviceState) switch (DeviceState)
{ {
case DeviceStates.NoMode: case DeviceStates.NoMode:
@ -386,7 +428,53 @@ namespace OpenbveFcmbTrainPlugin
} }
break; break;
case DeviceStates.YARD: 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: case DeviceStates.ATP:
// 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)
{
switch (TrackState)
{
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;
}
}
/// <summary>Calculates the ideal notches for the AI driver.</summary>
/// <param name="data">The AI data.</param>
/// <param name="train">The current train.</param>
/// <param name="route">The current route.</param>
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 // Calculate ideal notch for AI when approaching the speed limit
if (speed > CurrentSpeedCode.WarningOn.KilometersPerHour - 2 && train.Acceleration > 0.1) if (speed > CurrentSpeedCode.WarningOn.KilometersPerHour - 2 && train.Acceleration > 0.1)
{ {
@ -394,10 +482,6 @@ namespace OpenbveFcmbTrainPlugin
{ {
AiPowerNotch -= 1; AiPowerNotch -= 1;
} }
else if (train.Specs.HasHoldBrake)
{
data.Handles.HoldBrake = true;
}
else else
{ {
AiBrakeNotch += 1; AiBrakeNotch += 1;
@ -424,8 +508,21 @@ namespace OpenbveFcmbTrainPlugin
data.Response = AIResponse.Medium; data.Response = AIResponse.Medium;
} }
} }
break; }
}
/// <summary>Generates the corresponding speed code from a signal aspect.</summary>
/// <param name="signalCodes">The list of signal codes.</param>
/// <param name="aspect">The current signal aspect.</param>
private SpeedCode GetSpeedCodeFromAspect(List<SignalCode> 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));
} }
} }
} }

View file

@ -1,4 +1,6 @@
using System; using System;
using System.Globalization;
using System.Collections.Generic;
using OpenBveApi.Runtime; using OpenBveApi.Runtime;
namespace OpenbveFcmbTrainPlugin namespace OpenbveFcmbTrainPlugin
@ -32,6 +34,7 @@ namespace OpenbveFcmbTrainPlugin
internal Speed AtcDimetronicYardSpeedLimit = new Speed(25 / 3.6); internal Speed AtcDimetronicYardSpeedLimit = new Speed(25 / 3.6);
internal bool AtcDimetronicAtoAvailable; internal bool AtcDimetronicAtoAvailable;
internal List<AtcDimetronic.SignalCode> AtcDimetronicSignalCodes = new List<AtcDimetronic.SignalCode>();
} }
/// <summary>Represents the plugin settings.</summary> /// <summary>Represents the plugin settings.</summary>
@ -207,6 +210,18 @@ namespace OpenbveFcmbTrainPlugin
case "atoavailable": case "atoavailable":
PluginSettings.AtcDimetronicAtoAvailable = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; PluginSettings.AtcDimetronicAtoAvailable = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0;
break; 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; break;
} }

View file

@ -98,7 +98,7 @@ namespace OpenbveFcmbTrainPlugin
} }
if (settings.AtcDimetronicDeviceEnabled) if (settings.AtcDimetronicDeviceEnabled)
{ {
Devices.Add(new AtcDimetronic(settings.AtcDimetronicYardSpeedLimit, settings.AtcDimetronicAtoAvailable)); Devices.Add(new AtcDimetronic(settings.AtcDimetronicYardSpeedLimit, settings.AtcDimetronicSignalCodes, settings.AtcDimetronicAtoAvailable));
} }
} }