ATC Dimetronic: Implement double signal codes

This commit is contained in:
Marc Riera 2024-12-23 01:25:25 +01:00
parent 77869e9c1e
commit 5477d7389c
3 changed files with 70 additions and 28 deletions

View file

@ -51,15 +51,15 @@ 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 current position of the train.</summary>
private double TrainLocation;
/// <summary>The length of the train.</summary>
private readonly double TrainLength;
/// <summary>The maximum speed in YARD mode.</summary> /// <summary>The maximum speed in YARD mode.</summary>
private readonly Speed YardMaximumSpeed; private readonly Speed YardMaximumSpeed;
/// <summary>The speed limit for each signal aspect.</summary>
private readonly Speed[] AspectLimit = { new Speed(0), new Speed(25 / 3.6), new Speed(25 / 3.6), new Speed(25 / 3.6), new Speed(25 / 3.6), new Speed(45 / 3.6), new Speed(45 / 3.6), new Speed(45 / 3.6), new Speed(45 / 3.6), new Speed(70 / 3.6), new Speed(70 / 3.6), new Speed(70 / 3.6), new Speed(70 / 3.6) };
/// <summary>The target speed limit for each signal aspect.</summary>
private readonly Speed[] AspectTargetLimit = { new Speed(0), new Speed(0), new Speed(0), new Speed(25 / 3.6), new Speed(25 / 3.6), new Speed(0), new Speed(0), new Speed(45 / 3.6), new Speed(45 / 3.6), new Speed(0), new Speed(0), new Speed(70 / 3.6), new Speed(70 / 3.6) };
/// <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;
@ -113,26 +113,38 @@ namespace OpenbveFcmbTrainPlugin
internal int Aspect; internal int Aspect;
internal Speed Limit; internal Speed Limit;
internal Speed Target; internal Speed Target;
internal Speed EntryTarget;
internal SignalCode(int aspect, string data) internal SignalCode(int aspect, string data)
{ {
Aspect = aspect; Aspect = aspect;
Limit = new Speed(0); Limit = new Speed(0);
Target = new Speed(0); Target = new Speed(0);
EntryTarget = new Speed(0);
if (data.Contains("/")) if (data.Contains("/"))
{ {
int separator = data.IndexOf('/'); string[] speeds = data.Split('/');
string a = data.Substring(0, separator); if (speeds.Length > 1)
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) string a = speeds[0];
string b = speeds[1];
if (double.TryParse(a, NumberStyles.Float, CultureInfo.InvariantCulture, out double an) && double.TryParse(b, NumberStyles.Float, CultureInfo.InvariantCulture, out double bn))
{ {
Limit = new Speed(an / 3.6); if (an >= bn && an >= 0)
} {
if (bn <= an && bn >= 0) Limit = new Speed(an / 3.6);
{ }
Target = new Speed(bn / 3.6); if (bn <= an && bn >= 0)
{
Target = new Speed(bn / 3.6);
}
if (speeds.Length > 2)
{
if (double.TryParse(speeds[2], NumberStyles.Float, CultureInfo.InvariantCulture, out double cn))
{
EntryTarget = new Speed(cn / 3.6);
}
}
} }
} }
} }
@ -145,6 +157,12 @@ namespace OpenbveFcmbTrainPlugin
/// <summary>The aspect of the current signal in the route.</summary> /// <summary>The aspect of the current signal in the route.</summary>
private int CurrentSignalAspect; private int CurrentSignalAspect;
/// <summary>The start position of the current signal.</summary>
private double CurrentSignalStartLocation;
/// <summary>The end position of the current signal.</summary>
private double CurrentSignalEndLocation;
/// <summary>The ideal power notch for the AI.</summary> /// <summary>The ideal power notch for the AI.</summary>
private int AiPowerNotch; private int AiPowerNotch;
@ -155,8 +173,9 @@ namespace OpenbveFcmbTrainPlugin
/// <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="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, List<SignalCode> signalCodes, bool atoAvailable) internal AtcDimetronic(double trainLength, Speed yardMaxSpeed, List<SignalCode> signalCodes, bool atoAvailable)
{ {
TrainLength = trainLength;
YardMaximumSpeed = yardMaxSpeed; YardMaximumSpeed = yardMaxSpeed;
SignalCodes = signalCodes; SignalCodes = signalCodes;
AtoAvailable = atoAvailable; AtoAvailable = atoAvailable;
@ -175,6 +194,10 @@ namespace OpenbveFcmbTrainPlugin
DeviceState = DeviceStates.NoMode; DeviceState = DeviceStates.NoMode;
} }
// Update train location
// TODO: implement runback/rollfoward protection
TrainLocation = train.State.Location;
double speed = train.State.Speed.KilometersPerHour; double speed = train.State.Speed.KilometersPerHour;
bool stopped = speed < 0.05; bool stopped = speed < 0.05;
@ -252,13 +275,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); CurrentSpeedCode = GetSpeedCodeFromAspect();
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); CurrentSpeedCode = GetSpeedCodeFromAspect();
break; break;
} }
@ -376,6 +399,8 @@ namespace OpenbveFcmbTrainPlugin
internal override void SetSignal(SignalData[] signal) internal override void SetSignal(SignalData[] signal)
{ {
CurrentSignalAspect = signal[0].Aspect; CurrentSignalAspect = signal[0].Aspect;
CurrentSignalStartLocation = signal[0].Distance + TrainLocation;
CurrentSignalEndLocation = signal.Length > 1 ? signal[1].Distance + TrainLocation : double.MaxValue;
} }
/// <summary>Is called when a beacon is passed.</summary> /// <summary>Is called when a beacon is passed.</summary>
@ -511,15 +536,18 @@ namespace OpenbveFcmbTrainPlugin
} }
/// <summary>Generates the corresponding speed code from a signal aspect.</summary> /// <summary>Generates the corresponding speed code from a signal aspect.</summary>
/// <param name="signalCodes">The list of signal codes.</param> private SpeedCode GetSpeedCodeFromAspect()
/// <param name="aspect">The current signal aspect.</param>
private SpeedCode GetSpeedCodeFromAspect(List<SignalCode> signalCodes, int aspect)
{ {
foreach (SignalCode signal in signalCodes) foreach (SignalCode signal in SignalCodes)
{ {
if (signal.Aspect == aspect) if (signal.Aspect == CurrentSignalAspect)
{ {
return new SpeedCode(signal.Limit, signal.Target); if (TrainLocation - TrainLength < CurrentSignalStartLocation && signal.EntryTarget.MetersPerSecond > 0)
{
// The train has not fully entered the signal section, use the entry target speed
return new SpeedCode(signal.Limit, signal.EntryTarget, CurrentSignalEndLocation);
}
return new SpeedCode(signal.Limit, signal.Target, CurrentSignalEndLocation);
} }
} }
return new SpeedCode(new Speed(0), new Speed(0)); return new SpeedCode(new Speed(0), new Speed(0));

View file

@ -11,6 +11,8 @@ namespace OpenbveFcmbTrainPlugin
/// <summary>Represents a collection of plugin settings.</summary> /// <summary>Represents a collection of plugin settings.</summary>
internal class SettingsCollection internal class SettingsCollection
{ {
internal double TrainLength;
internal bool DoorSelectionDeviceEnabled; internal bool DoorSelectionDeviceEnabled;
internal bool DoorClosingSoundDeviceEnabled; internal bool DoorClosingSoundDeviceEnabled;
internal bool DoorTractionCutDeviceEnabled; internal bool DoorTractionCutDeviceEnabled;
@ -75,6 +77,19 @@ namespace OpenbveFcmbTrainPlugin
} }
switch (Section) switch (Section)
{ {
case "train":
switch (Key)
{
case "length":
{
if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double a))
{
PluginSettings.TrainLength = a;
}
}
break;
}
break;
case "doorselection": case "doorselection":
switch (Key) switch (Key)
{ {
@ -211,8 +226,7 @@ namespace OpenbveFcmbTrainPlugin
PluginSettings.AtcDimetronicAtoAvailable = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0; PluginSettings.AtcDimetronicAtoAvailable = string.Compare(Value, "false", StringComparison.OrdinalIgnoreCase) != 0;
break; break;
default: default:
int aspect; if (int.TryParse(Key, NumberStyles.Integer, CultureInfo.InvariantCulture, out int aspect))
if (int.TryParse(Key, NumberStyles.Integer, CultureInfo.InvariantCulture, out aspect))
{ {
AtcDimetronic.SignalCode signalCode = new AtcDimetronic.SignalCode(aspect, Value); AtcDimetronic.SignalCode signalCode = new AtcDimetronic.SignalCode(aspect, Value);
if (signalCode != null) if (signalCode != null)

View file

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