public MSTSNotch(float v, int s, string type, STFReader stf) { Value = v; Smooth = s == 0 ? false : true; Type = ControllerState.Dummy; string lower = type.ToLower(); if (lower.StartsWith("trainbrakescontroller")) { lower = lower.Substring(21); } if (lower.StartsWith("enginebrakescontroller")) { lower = lower.Substring(22); } switch (lower) { case "dummy": break; case ")": break; case "releasestart": Type = ControllerState.Release; break; case "fullquickreleasestart": Type = ControllerState.FullQuickRelease; break; case "runningstart": Type = ControllerState.Running; break; case "selflapstart": Type = ControllerState.SelfLap; break; case "holdstart": Type = ControllerState.Lap; break; case "holdlappedstart": Type = ControllerState.Lap; break; case "neutralhandleoffstart": Type = ControllerState.Neutral; break; case "graduatedselflaplimitedstart": Type = ControllerState.GSelfLap; break; case "graduatedselflaplimitedholdingstart": Type = ControllerState.GSelfLapH; break; case "applystart": Type = ControllerState.Apply; break; case "continuousservicestart": Type = ControllerState.ContServ; break; case "suppressionstart": Type = ControllerState.Suppression; break; case "fullservicestart": Type = ControllerState.FullServ; break; case "emergencystart": Type = ControllerState.Emergency; break; case "epapplystart": Type = ControllerState.EPApply; break; case "epholdstart": Type = ControllerState.Lap; break; case "minimalreductionstart": Type = ControllerState.Lap; break; default: STFException.TraceInformation(stf, "Skipped unknown notch type " + type); break; } }
private Dictionary <string, SignalDrawState> ReadDrawStates(STFReader stf) { stf.MustMatchBlockStart(); int count = stf.ReadInt(null); Dictionary <string, SignalDrawState> drawStates = new Dictionary <string, SignalDrawState>(count); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signaldrawstate", () => { if (drawStates.Count >= count) { STFException.TraceWarning(stf, "Skipped extra SignalDrawState"); } else { SignalDrawState drawState = new SignalDrawState(stf); if (drawStates.ContainsKey(drawState.Name)) { string newState = $"DST{drawStates.Count}"; drawStates.Add(newState, drawState); STFException.TraceInformation(stf, $"Duplicate SignalDrawState name \'{drawState.Name}\', using name \'{newState}\' instead"); } else { drawStates.Add(drawState.Name, drawState); } } }), }); if (drawStates.Count < count) { STFException.TraceWarning(stf, (count - drawStates.Count).ToString() + " missing SignalDrawState(s)"); } return(drawStates); }
public CabViewControls(STFReader stf, string basePath) { stf.MustMatchBlockStart(); int count = stf.ReadInt(null); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("dial", () => { Add(new CabViewDialControl(stf, basePath)); }), new STFReader.TokenProcessor("gauge", () => { Add(new CabViewGaugeControl(stf, basePath)); }), new STFReader.TokenProcessor("lever", () => { Add(new CabViewDiscreteControl(stf, basePath)); }), new STFReader.TokenProcessor("twostate", () => { Add(new CabViewDiscreteControl(stf, basePath)); }), new STFReader.TokenProcessor("tristate", () => { Add(new CabViewDiscreteControl(stf, basePath)); }), new STFReader.TokenProcessor("multistate", () => { Add(new CabViewDiscreteControl(stf, basePath)); }), new STFReader.TokenProcessor("multistatedisplay", () => { Add(new CabViewMultiStateDisplayControl(stf, basePath)); }), new STFReader.TokenProcessor("cabsignaldisplay", () => { Add(new CabViewSignalControl(stf, basePath)); }), new STFReader.TokenProcessor("digital", () => { Add(new CabViewDigitalControl(stf, basePath)); }), new STFReader.TokenProcessor("combinedcontrol", () => { Add(new CabViewDiscreteControl(stf, basePath)); }), new STFReader.TokenProcessor("firebox", () => { Add(new CabViewFireboxControl(stf, basePath)); }), new STFReader.TokenProcessor("dialclock", () => { ProcessDialClock(stf, basePath); }), new STFReader.TokenProcessor("digitalclock", () => { Add(new CabViewDigitalClockControl(stf, basePath)); }) }); if (count != Count) { STFException.TraceInformation(stf, $"CabViewControl count mismatch - expected {count} but found {Count} controls."); } }
// SigSubJnLinkIf is not supported /// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalSubObj(STFReader stf) { SignalSubType = -1; // not (yet) specified stf.MustMatch("("); Index = stf.ReadInt(null); MatrixName = stf.ReadString().ToUpper(); Description = stf.ReadString(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("sigsubtype", () => { SignalSubType = SignalSubTypes.IndexOf(stf.ReadStringBlock(null).ToUpper()); }), new STFReader.TokenProcessor("sigsubstype", () => { SignalSubSignalType = stf.ReadStringBlock(null).ToLowerInvariant(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "optional": Optional = true; break; case "default": Default = true; break; case "back_facing": BackFacing = true; break; case "jn_link": JunctionLink = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalSubObj flag " + stf.ReadString()); break; } } }), }); }
static IDictionary <string, SignalDrawState> ReadDrawStates(STFReader stf) { stf.MustMatch("("); int count = stf.ReadInt(null); Dictionary <string, SignalDrawState> drawStates = new Dictionary <string, SignalDrawState>(count); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signaldrawstate", () => { if (drawStates.Count >= count) { STFException.TraceWarning(stf, "Skipped extra SignalDrawState"); } else { SignalDrawState drawState = new SignalDrawState(stf); if (drawStates.ContainsKey(drawState.Name)) { string TempNew = String.Copy("DST"); TempNew = String.Concat(TempNew, drawStates.Count.ToString()); drawStates.Add(TempNew, drawState); STFException.TraceInformation(stf, "Duplicate SignalDrawState name \'" + drawState.Name + "\', using name \'" + TempNew + "\' instead"); } else { drawStates.Add(drawState.Name, drawState); } } }), }); if (drawStates.Count < count) { STFException.TraceWarning(stf, (count - drawStates.Count).ToString() + " missing SignalDrawState(s)"); } return(drawStates); }
internal ActivitySound(STFReader stf) { stf.MustMatchBlockStart(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("ortsactsoundfile", () => { stf.MustMatchBlockStart(); string soundFile = stf.ReadString(); SoundFile = Path.Combine(FolderStructure.RouteFromActivity(stf.FileName).SoundFile(soundFile)); if (!EnumExtension.GetValue(stf.ReadString(), out OrtsActivitySoundFileType soundFileType)) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown activity sound file type " + stf.ReadString()); SoundFileType = OrtsActivitySoundFileType.None; } else { SoundFileType = soundFileType; } stf.MustMatchBlockEnd(); }), new STFReader.TokenProcessor("ortssoundlocation", () => { stf.MustMatchBlockStart(); location = new WorldLocation(stf.ReadInt(null), stf.ReadInt(null), stf.ReadFloat(STFReader.Units.None, null), stf.ReadFloat(STFReader.Units.None, null), stf.ReadFloat(STFReader.Units.None, null)); stf.MustMatchBlockEnd(); }), });
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalLight(STFReader stf) { stf.MustMatchBlockStart(); Index = stf.ReadInt(null); Name = stf.ReadString().ToLowerInvariant(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("radius", () => { Radius = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("position", () => { stf.MustMatchBlockStart(); position = new Vector3(stf.ReadFloat(null), stf.ReadFloat(null), stf.ReadFloat(null)); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "semaphore_change": SemaphoreChange = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalLight flag " + stf.ReadString()); break; } } }), }); }
private SignalFunction ReadFunctionType(STFReader stf) { string signalType = stf.ReadStringBlock(null); if (!EnumExtension.GetValue(signalType, out SignalFunction result)) { STFException.TraceInformation(stf, $"Skipped unknown SignalFnType {signalType}"); return(SignalFunction.Info); } return(result); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> /// <param name="ORTSMode">Process SignalType for ORTS mode (always set NumClearAhead_ORTS only)</param> public SignalType(STFReader stf, bool ORTSMode) : this() { stf.MustMatch("("); Name = stf.ReadString().ToLowerInvariant(); int numClearAhead = -2; int numdefs = 0; stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signalfntype", () => { FnType = ReadFnType(stf); }), //[Rob Roeterdink] value was not passed new STFReader.TokenProcessor("signallighttex", () => { LightTextureName = stf.ReadStringBlock("").ToLowerInvariant(); }), new STFReader.TokenProcessor("signallights", () => { Lights = ReadLights(stf); }), new STFReader.TokenProcessor("signaldrawstates", () => { DrawStates = ReadDrawStates(stf); }), new STFReader.TokenProcessor("signalaspects", () => { Aspects = ReadAspects(stf); }), new STFReader.TokenProcessor("approachcontrolsettings", () => { ApproachControlDetails = ReadApproachControlDetails(stf); }), new STFReader.TokenProcessor("signalnumclearahead", () => { numClearAhead = numClearAhead >= -1 ? numClearAhead : stf.ReadIntBlock(null); numdefs++; }), new STFReader.TokenProcessor("semaphoreinfo", () => { SemaphoreInfo = stf.ReadFloatBlock(STFReader.UNITS.None, null); }), new STFReader.TokenProcessor("sigflashduration", () => { stf.MustMatch("("); FlashTimeOn = stf.ReadFloat(STFReader.UNITS.None, null); FlashTimeOff = stf.ReadFloat(STFReader.UNITS.None, null); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "abs": Abs = true; break; case "no_gantry": NoGantry = true; break; case "semaphore": Semaphore = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalType flag " + stf.ReadString()); break; } } }), }); if (ORTSMode) { // In ORTS mode : always set value for NumClearAhead_ORTS NumClearAhead_MSTS = -2; NumClearAhead_ORTS = numClearAhead; } else { // In MSTS mode : if one line for SignalNumClearAhead defined, set value for NumClearAhead_MSTS, otherwise set value for NumClearAhead_ORTS NumClearAhead_MSTS = numdefs == 1 ? numClearAhead : -2; NumClearAhead_ORTS = numdefs == 2 ? numClearAhead : -2; } }
protected void ParseType(STFReader stf) { stf.MustMatchBlockStart(); if (!EnumExtension.GetValue(stf.ReadString(), out CabViewControlType type)) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown ControlType " + stf.ReadString()); ControlType = CabViewControlType.None; } ControlType = type; //stf.ReadItem(); // Skip repeated Class Type stf.SkipRestOfBlock(); }
private string ReadOrtsFunctionType(STFReader stf) { string type = stf.ReadStringBlock(null); if (OrSignalTypes.Instance.FunctionTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) { return(type); } else { STFException.TraceInformation(stf, "Skipped unknown ORTSSignalFnType " + type); return(SignalFunction.Info.ToString()); } }
protected void ParseUnits(STFReader stf) { stf.MustMatchBlockStart(); string units = stf.ReadItem().Replace('/', '_'); if (!EnumExtension.GetValue(units, out CabViewControlUnit unit)) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown ControlUnit " + stf.ReadItem()); ControlUnit = CabViewControlUnit.None; } ControlUnit = unit; stf.SkipRestOfBlock(); }
static FnTypes ReadFnType(STFReader stf) { string type = stf.ReadStringBlock(null); try { return((FnTypes)Enum.Parse(typeof(FnTypes), type, true)); } catch (ArgumentException) { STFException.TraceInformation(stf, "Skipped unknown SignalFnType " + type); return(FnTypes.Info); } }
static string ReadOrtsNormalSubType(STFReader stf) { string type = stf.ReadStringBlock(null); if (OrSignalTypes.Instance.NormalSubTypes.Contains(type, StringComparer.OrdinalIgnoreCase)) { return(type); } else { STFException.TraceInformation(stf, "Skipped unknown ORTSNormalSubtype " + type); return(String.Empty); } }
protected void ParseType(STFReader stf) { stf.MustMatch("("); try { ControlType = (CABViewControlTypes)Enum.Parse(typeof(CABViewControlTypes), stf.ReadString()); } catch (ArgumentException) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown ControlType " + stf.ReadString()); ControlType = CABViewControlTypes.NONE; } //stf.ReadItem(); // Skip repeated Class Type stf.SkipRestOfBlock(); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalAspect(STFReader stf) { SpeedLimit = -1; stf.MustMatchBlockStart(); string aspectName = stf.ReadString(); if (!EnumExtension.GetValue(aspectName, out SignalAspectState aspect)) { STFException.TraceInformation(stf, "Skipped unknown signal aspect " + aspectName); Aspect = SignalAspectState.Unknown; } else { Aspect = aspect; } DrawStateName = stf.ReadString().ToLowerInvariant(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("speedmph", () => { SpeedLimit = (float)Speed.MeterPerSecond.FromMpH(stf.ReadFloatBlock(STFReader.Units.None, 0)); }), new STFReader.TokenProcessor("speedkph", () => { SpeedLimit = (float)Speed.MeterPerSecond.FromKpH(stf.ReadFloatBlock(STFReader.Units.None, 0)); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "asap": Asap = true; break; case "or_speedreset": Reset = true; break; case "or_nospeedreduction": NoSpeedReduction = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
private protected void OrtsActivitySoundProcessor(STFReader stf) { stf.MustMatchBlockStart(); string soundFile = stf.ReadString(); SoundFile = FolderStructure.RouteFromActivity(stf.FileName).SoundFile(soundFile); if (!EnumExtension.GetValue(stf.ReadString(), out OrtsActivitySoundFileType soundFileType)) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown activity sound file type " + stf.ReadString()); SoundFileType = OrtsActivitySoundFileType.None; } else { SoundFileType = soundFileType; } stf.MustMatchBlockEnd(); }
protected void ParseUnits(STFReader stf) { stf.MustMatch("("); try { string sUnits = stf.ReadItem(); // sUnits = sUnits.Replace('/', '?'); sUnits = sUnits.Replace('/', '_'); Units = (CABViewControlUnits)Enum.Parse(typeof(CABViewControlUnits), sUnits); } catch (ArgumentException) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown ControlStyle " + stf.ReadItem()); Units = CABViewControlUnits.NONE; } stf.SkipRestOfBlock(); }
protected void ParseStyle(STFReader stf) { stf.MustMatchBlockStart(); string styleTemp = stf.ReadString(); if (char.IsDigit(styleTemp[0])) { styleTemp = (styleTemp + styleTemp.Substring(0, 2)).Remove(0, 2); } if (!EnumExtension.GetValue(styleTemp, out CabViewControlStyle style)) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown ControlStyle " + stf.ReadString()); ControlStyle = CabViewControlStyle.None; } ControlStyle = style; stf.SkipRestOfBlock(); }
// SigSubJnLinkIf is not supported /// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> internal SignalSubObject(STFReader stf) { SignalSubType = SignalSubType.None; stf.MustMatchBlockStart(); Index = stf.ReadInt(null); MatrixName = stf.ReadString().ToUpperInvariant(); Description = stf.ReadString(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("sigsubtype", () => { if (EnumExtension.GetValue(stf.ReadStringBlock(null), out SignalSubType subType)) { SignalSubType = subType; } }), new STFReader.TokenProcessor("sigsubstype", () => { SignalSubSignalType = stf.ReadStringBlock(null); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToUpperInvariant()) { case "OPTIONAL": Optional = true; break; case "DEFAULT": Default = true; break; case "BACK_FACING": BackFacing = true; break; case "JN_LINK": JunctionLink = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalSubObj flag " + stf.ReadString()); break; } } }), });
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalDrawLight(STFReader stf) { stf.MustMatch("("); LightIndex = stf.ReadUInt(null); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "flashing": Flashing = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
protected void ParseStyle(STFReader stf) { stf.MustMatch("("); try { string sStyle = stf.ReadString(); int checkNumeric = 0; if (int.TryParse(sStyle.Substring(0, 1), out checkNumeric) == true) { sStyle = sStyle.Insert(0, "_"); } ControlStyle = (CABViewControlStyles)Enum.Parse(typeof(CABViewControlStyles), sStyle); } catch (ArgumentException) { stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown ControlStyle " + stf.ReadString()); ControlStyle = CABViewControlStyles.NONE; } stf.SkipRestOfBlock(); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> public SignalAspect(STFReader stf) { SpeedMpS = -1; stf.MustMatch("("); string aspectName = stf.ReadString(); try { Aspect = (MstsSignalAspect)Enum.Parse(typeof(MstsSignalAspect), aspectName, true); } catch (ArgumentException) { STFException.TraceInformation(stf, "Skipped unknown signal aspect " + aspectName); Aspect = MstsSignalAspect.UNKNOWN; } DrawStateName = stf.ReadString().ToLowerInvariant(); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("speedmph", () => { SpeedMpS = MpS.FromMpH(stf.ReadFloatBlock(STFReader.UNITS.None, 0)); }), new STFReader.TokenProcessor("speedkph", () => { SpeedMpS = MpS.FromKpH(stf.ReadFloatBlock(STFReader.UNITS.None, 0)); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatch("("); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "asap": Asap = true; break; case "or_speedreset": Reset = true; break; case "or_nospeedreduction": NoSpeedReduction = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> internal SignalDrawLight(STFReader stf) { stf.MustMatchBlockStart(); Index = stf.ReadInt(null); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToUpperInvariant()) { case "FLASHING": Flashing = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown DrawLight flag " + stf.ReadString()); break; } } }), }); }
public MSTSNotch(float v, int s, string type, STFReader stf) { Value = v; Smooth = s == 0 ? false : true; NotchStateType = ControllerState.Dummy; // Default to a dummy controller state if no valid alternative state used string lower = type.ToLower(); if (lower.StartsWith("trainbrakescontroller")) { lower = lower.Substring(21); } if (lower.StartsWith("enginebrakescontroller")) { lower = lower.Substring(22); } if (lower.StartsWith("brakemanbrakescontroller")) { lower = lower.Substring(24); } switch (lower) { case "dummy": break; case ")": break; case "releasestart": NotchStateType = ControllerState.Release; break; case "fullquickreleasestart": NotchStateType = ControllerState.FullQuickRelease; break; case "runningstart": NotchStateType = ControllerState.Running; break; case "selflapstart": NotchStateType = ControllerState.SelfLap; break; case "holdstart": NotchStateType = ControllerState.Hold; break; case "holdlappedstart": NotchStateType = ControllerState.Lap; break; case "neutralhandleoffstart": NotchStateType = ControllerState.Neutral; break; case "graduatedselflaplimitedstart": NotchStateType = ControllerState.GSelfLap; break; case "graduatedselflaplimitedholdingstart": NotchStateType = ControllerState.GSelfLapH; break; case "applystart": NotchStateType = ControllerState.Apply; break; case "continuousservicestart": NotchStateType = ControllerState.ContServ; break; case "suppressionstart": NotchStateType = ControllerState.Suppression; break; case "fullservicestart": NotchStateType = ControllerState.FullServ; break; case "emergencystart": NotchStateType = ControllerState.Emergency; break; case "minimalreductionstart": NotchStateType = ControllerState.MinimalReduction; break; case "epapplystart": NotchStateType = ControllerState.EPApply; break; case "epholdstart": NotchStateType = ControllerState.SelfLap; break; case "vacuumcontinuousservicestart": NotchStateType = ControllerState.VacContServ; break; case "vacuumapplycontinuousservicestart": NotchStateType = ControllerState.VacApplyContServ; break; case "manualbrakingstart": NotchStateType = ControllerState.ManualBraking; break; case "brakenotchstart": NotchStateType = ControllerState.BrakeNotch; break; case "overchargestart": NotchStateType = ControllerState.Overcharge; break; case "slowservicestart": NotchStateType = ControllerState.SlowService; break; default: STFException.TraceInformation(stf, "Skipped unknown notch type " + type); break; } }
public CVCDiscrete(STFReader stf, string basepath) { // try { stf.MustMatch("("); stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("type", () => { ParseType(stf); }), new STFReader.TokenProcessor("position", () => { ParsePosition(stf); }), new STFReader.TokenProcessor("scalerange", () => { ParseScaleRange(stf); }), new STFReader.TokenProcessor("graphic", () => { ParseGraphic(stf, basepath); }), new STFReader.TokenProcessor("style", () => { ParseStyle(stf); }), new STFReader.TokenProcessor("units", () => { ParseUnits(stf); }), new STFReader.TokenProcessor("mousecontrol", () => { MouseControl = stf.ReadBoolBlock(false); }), new STFReader.TokenProcessor("orientation", () => { Orientation = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("dirincrease", () => { Direction = stf.ReadIntBlock(null); }), new STFReader.TokenProcessor("numframes", () => { stf.MustMatch("("); FramesCount = stf.ReadInt(null); FramesX = stf.ReadInt(null); FramesY = stf.ReadInt(null); stf.SkipRestOfBlock(); }), // <CJComment> Would like to revise this, as it is difficult to follow and debug. // Can't do that until interaction of ScaleRange, NumFrames, NumPositions and NumValues is more fully specified. // What is needed is samples of data that must be accommodated. // Some decisions appear unwise but they might be a pragmatic solution to a real problem. </CJComment> // // Code accommodates: // - NumValues before NumPositions or the other way round. // - More NumValues than NumPositions and the other way round - perhaps unwise. // - The count of NumFrames, NumValues and NumPositions is ignored - perhaps unwise. // - Abbreviated definitions so that values at intermediate unspecified positions can be omitted. // Strangely, these values are set to 0 and worked out later when drawing. // Max and min NumValues which don't match the ScaleRange are ignored - perhaps unwise. new STFReader.TokenProcessor("numpositions", () => { stf.MustMatch("("); // If Positions are not filled before by Values bool shouldFill = (Positions.Count == 0); numPositions = stf.ReadInt(null); // Number of Positions var minPosition = 0; var positionsRead = 0; while (!stf.EndOfBlock()) { int p = stf.ReadInt(null); minPosition = positionsRead == 0 ? p : Math.Min(minPosition, p); // used to get correct offset positionsRead++; // If Positions are not filled before by Values if (shouldFill) { Positions.Add(p); } } // If positions do not start at 0, add offset to shift them all so they do. // An example of this is RENFE 400 (from http://www.trensim.com/lib/msts/index.php?act=view&id=186) // which has a COMBINED_CONTROL with: // NumPositions ( 21 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 ) // Also handles definitions with position in reverse order, e.g. // NumPositions ( 5 8 7 2 1 0 ) positionsRead++; if (minPosition < 0) { for (int iPos = 0; iPos <= Positions.Count - 1; iPos++) { Positions[iPos] -= minPosition; } } // This is a hack for SLI locomotives which have the positions listed as "1056964608 0 0 0 ...". if (Positions.Any(p => p > 0xFFFF)) { STFException.TraceInformation(stf, "Renumbering cab control positions from zero due to value > 0xFFFF"); for (var i = 0; i < Positions.Count; i++) { Positions[i] = i; } } // Check if eligible for filling if (Positions.Count > 1 && Positions[0] != 0) { canFill = false; } else { for (var iPos = 1; iPos <= Positions.Count - 1; iPos++) { if (Positions[iPos] > Positions[iPos - 1]) { continue; } canFill = false; break; } } // This is a protection against GP40 locomotives that erroneously have positions pointing beyond frame count limit. if (Positions.Count > 1 && canFill && Positions.Count < FramesCount && Positions[Positions.Count - 1] >= FramesCount && Positions[0] == 0) { STFException.TraceInformation(stf, "Some NumPositions entries refer to non-exisiting frames, trying to renumber"); Positions[Positions.Count - 1] = FramesCount - 1; for (var iPos = Positions.Count - 2; iPos >= 1; iPos--) { if ((Positions[iPos] >= FramesCount || Positions[iPos] >= Positions[iPos + 1])) { Positions[iPos] = Positions[iPos + 1] - 1; } else { break; } } } }), new STFReader.TokenProcessor("numvalues", () => { stf.MustMatch("("); var numValues = stf.ReadDouble(null); // Number of Values while (!stf.EndOfBlock()) { double v = stf.ReadDouble(null); // If the Positions are less than expected add new Position(s) while (Positions.Count <= _ValuesRead) { Positions.Add(_ValuesRead); } // Avoid later repositioning, put every value to its Position // But before resize Values if needed if (numValues != numPositions) { while (Values.Count <= Positions[_ValuesRead]) { Values.Add(0); } // Avoid later repositioning, put every value to its Position Values[Positions[_ValuesRead]] = v; } Values.Add(v); _ValuesRead++; } }), }); // If no ACE, just don't need any fixup // Because Values are tied to the image Frame to be shown if (string.IsNullOrEmpty(ACEFile)) { return; } // Now, we have an ACE. // If read any Values, or the control requires Values to control // The twostate, tristate, signal displays are not in these // Need check the Values collection for validity if (_ValuesRead > 0 || ControlStyle == CABViewControlStyles.SPRUNG || ControlStyle == CABViewControlStyles.NOT_SPRUNG || FramesCount > 0 || (FramesX > 0 && FramesY > 0)) { // Check max number of Frames if (FramesCount == 0) { // Check valid Frame information if (FramesX == 0 || FramesY == 0) { // Give up, it won't work // Because later we won't know how to display frames from that Trace.TraceWarning("Invalid Frames information given for ACE {0} in {1}", ACEFile, stf.FileName); ACEFile = ""; return; } // Valid frames info, set FramesCount FramesCount = FramesX * FramesY; } // Now we have an ACE and Frames for it. // Only shuffle data in following cases if (Values.Count != Positions.Count || (Values.Count < FramesCount & canFill) || (Values.Count > 0 && Values[0] == Values[Values.Count - 1] && Values[0] == 0)) { // Fixup Positions and Values collections first // If the read Positions and Values are not match // Or we didn't read Values but have Frames to draw // Do not test if FramesCount equals Values count, we trust in the creator - // maybe did not want to display all Frames // (If there are more Values than Frames it will checked at draw time) // Need to fix the whole Values if (Positions.Count != _ValuesRead || (FramesCount > 0 && (Values.Count == 0 || Values.Count == 1))) { //This if clause covers among others following cases: // Case 1 (e.g. engine brake lever of Dash 9): //NumFrames ( 22 11 2 ) //NumPositions ( 1 0 ) //NumValues ( 1 0 ) //Orientation ( 1 ) //DirIncrease ( 1 ) //ScaleRange ( 0 1 ) // // Case 2 (e.g. throttle lever of Acela): //NumFrames ( 25 5 5 ) //NumPositions ( 0 ) //NumValues ( 0 ) //Orientation ( 1 ) //DirIncrease ( 1 ) //ScaleRange ( 0 1 ) // // Clear existing Positions.Clear(); Values.Clear(); // Add the two sure positions, the two ends Positions.Add(0); // We will need the FramesCount later! // We use Positions only here Positions.Add(FramesCount); // Fill empty Values for (int i = 0; i < FramesCount; i++) { Values.Add(0); } Values[0] = MinValue; Values.Add(MaxValue); } else if (Values.Count == 2 && Values[0] == 0 && Values[1] < MaxValue && Positions[0] == 0 && Positions[1] == 1 && Values.Count < FramesCount) { //This if clause covers among others following cases: // Case 1 (e.g. engine brake lever of gp38): //NumFrames ( 18 2 9 ) //NumPositions ( 2 0 1 ) //NumValues ( 2 0 0.3 ) //Orientation ( 0 ) //DirIncrease ( 0 ) //ScaleRange ( 0 1 ) Positions.Add(FramesCount); // Fill empty Values for (int i = Values.Count; i < FramesCount; i++) { Values.Add(Values[1]); } Values.Add(MaxValue); } else { //This if clause covers among others following cases: // Case 1 (e.g. train brake lever of Acela): //NumFrames ( 12 4 3 ) //NumPositions ( 5 0 1 9 10 11 ) //NumValues ( 5 0 0.2 0.85 0.9 0.95 ) //Orientation ( 1 ) //DirIncrease ( 1 ) //ScaleRange ( 0 1 ) // // Fill empty Values int iValues = 1; for (int i = 1; i < FramesCount && i <= Positions.Count - 1 && Values.Count < FramesCount; i++) { var deltaPos = Positions[i] - Positions[i - 1]; while (deltaPos > 1 && Values.Count < FramesCount) { Values.Insert(iValues, 0); iValues++; deltaPos--; } iValues++; } // Add the maximums to the end, the Value will be removed // We use Positions only here if (Values.Count > 0 && Values[0] <= Values[Values.Count - 1]) { Values.Add(MaxValue); } else if (Values.Count > 0 && Values[0] > Values[Values.Count - 1]) { Values.Add(MinValue); } } // OK, we have a valid size of Positions and Values // Now it is the time for checking holes in the given data if ((Positions.Count < FramesCount - 1 && Values[0] <= Values[Values.Count - 1]) || (Values.Count > 1 && Values[0] == Values[Values.Count - 2] && Values[0] == 0)) { int j = 1; int p = 0; // Skip the 0 element, that is the default MinValue for (int i = 1; i < Positions.Count; i++) { // Found a hole if (Positions[i] != p + 1) { // Iterate to the next valid data and fill the hole for (j = p + 1; j < Positions[i]; j++) { // Extrapolate into the hole Values[j] = MathHelper.Lerp((float)Values[p], (float)Values[Positions[i]], (float)j / (float)Positions[i]); } } p = Positions[i]; } } // Don't need the MaxValue added before, remove it Values.RemoveAt(Values.Count - 1); } } // MSTS ignores/overrides various settings by the following exceptional cases: if (ControlType == CABViewControlTypes.CP_HANDLE) { ControlStyle = CABViewControlStyles.NOT_SPRUNG; } if (ControlType == CABViewControlTypes.PANTOGRAPH || ControlType == CABViewControlTypes.PANTOGRAPH2) { ControlStyle = CABViewControlStyles.ONOFF; } if (ControlType == CABViewControlTypes.HORN || ControlType == CABViewControlTypes.SANDERS || ControlType == CABViewControlTypes.BELL || ControlType == CABViewControlTypes.RESET) { ControlStyle = CABViewControlStyles.WHILE_PRESSED; } if (ControlType == CABViewControlTypes.DIRECTION && Orientation == 0) { Direction = 1 - Direction; } } // catch (Exception error) // { // if (error is STFException) // Parsing error, so pass it on // throw; // else // Unexpected error, so provide a hint // throw new STFException(stf, "Problem with NumPositions/NumValues/NumFrames/ScaleRange"); // } // End of Need check the Values collection for validity } // End of Constructor
/// <summary> /// Reads RailDriver calibration data from a ModernCalibration.rdm file /// This file is not in the usual STF format, but the STFReader can handle it okay. /// </summary> /// <param name="basePath"></param> void ReadCalibrationData(string basePath) { string file = basePath + "\\ModernCalibration.rdm"; if (!File.Exists(file)) { RegistryKey RK = Registry.LocalMachine.OpenSubKey("SOFTWARE\\PI Engineering\\PIBUS"); if (RK != null) { string dir = (string)RK.GetValue("RailDriver", null, RegistryValueOptions.None); if (dir != null) { file = dir + "\\..\\controller\\ModernCalibration.rdm"; } } if (!File.Exists(file)) { SetLEDs(0, 0, 0); Trace.TraceWarning("Cannot find RailDriver calibration file {0}", file); return; } } // TODO: This is... kinda weird and cool at the same time. STF parsing being used on RailDriver's calebration file. Probably should be a dedicated parser, though. STFReader reader = new STFReader(file, false); while (!reader.Eof) { string token = reader.ReadItem(); if (token == "Position") { string name = reader.ReadItem(); int min = -1; int max = -1; while (token != "}") { token = reader.ReadItem(); if (token == "Min") { min = reader.ReadInt(-1); } else if (token == "Max") { max = reader.ReadInt(-1); } } if (min >= 0 && max >= 0) { float v = .5f * (min + max); switch (name) { case "Full Reversed": FullReversed = v; break; case "Neutral": Neutral = v; break; case "Full Forward": FullForward = v; break; case "Full Throttle": FullThrottle = v; break; case "Throttle Idle": ThrottleIdle = v; break; case "Dynamic Brake": DynamicBrake = v; break; case "Dynamic Brake Setup": DynamicBrakeSetup = v; break; case "Auto Brake Released": AutoBrakeRelease = v; break; case "Full Auto Brake (CS)": FullAutoBrake = v; break; case "Emergency Brake (EMG)": EmergencyBrake = v; break; case "Independent Brake Released": IndependentBrakeRelease = v; break; case "Bail Off Engaged (in Released position)": BailOffEngagedRelease = v; break; case "Independent Brake Full": IndependentBrakeFull = v; break; case "Bail Off Engaged (in Full position)": BailOffEngagedFull = v; break; case "Bail Off Disengaged (in Released position)": BailOffDisengagedRelease = v; break; case "Bail Off Disengaged (in Full position)": BailOffDisengagedFull = v; break; case "Rotary Switch 1-Position 1(OFF)": Rotary1Position1 = v; break; case "Rotary Switch 1-Position 2(SLOW)": Rotary1Position2 = v; break; case "Rotary Switch 1-Position 3(FULL)": Rotary1Position3 = v; break; case "Rotary Switch 2-Position 1(OFF)": Rotary2Position1 = v; break; case "Rotary Switch 2-Position 2(DIM)": Rotary2Position2 = v; break; case "Rotary Switch 2-Position 3(FULL)": Rotary2Position3 = v; break; default: STFException.TraceInformation(reader, "Skipped unknown calibration value " + name); break; } } } } }
public override void TraceInformation(string message) { STFException.TraceInformation(stf, message); }
/// <summary> /// Default constructor used during file parsing. /// </summary> /// <param name="stf">The STFreader containing the file stream</param> /// <param name="orMode">Process SignalType for ORTS mode (always set NumClearAhead_ORTS only)</param> public SignalType(STFReader stf, bool orMode) : this() { stf.MustMatchBlockStart(); Name = stf.ReadString().ToLowerInvariant(); int numClearAhead = -2; int numdefs = 0; string ortsFunctionType = string.Empty; string ortsNormalSubType = string.Empty; stf.ParseBlock(new STFReader.TokenProcessor[] { new STFReader.TokenProcessor("ortsscript", () => { Script = stf.ReadStringBlock("").ToLowerInvariant(); }), new STFReader.TokenProcessor("signalfntype", () => { if (orMode) { ortsFunctionType = ReadOrtsFunctionType(stf); } else { FunctionType = ReadFunctionType(stf); } }), new STFReader.TokenProcessor("signallighttex", () => { LightTextureName = stf.ReadStringBlock("").ToLowerInvariant(); }), new STFReader.TokenProcessor("signallights", () => { Lights = ReadLights(stf); }), new STFReader.TokenProcessor("signaldrawstates", () => { DrawStates = ReadDrawStates(stf); }), new STFReader.TokenProcessor("signalaspects", () => { Aspects = ReadAspects(stf); }), new STFReader.TokenProcessor("approachcontrolsettings", () => { ApproachControlDetails = ReadApproachControlDetails(stf); }), new STFReader.TokenProcessor("signalnumclearahead", () => { numClearAhead = numClearAhead >= -1 ? numClearAhead : stf.ReadIntBlock(null); numdefs++; }), new STFReader.TokenProcessor("semaphoreinfo", () => { SemaphoreInfo = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("ortsdayglow", () => { DayGlow = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("ortsnightglow", () => { NightGlow = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("ortsdaylight", () => { DayLight = stf.ReadBoolBlock(true); }), new STFReader.TokenProcessor("ortsnormalsubtype", () => { ortsNormalSubType = ReadOrtsNormalSubType(stf); }), new STFReader.TokenProcessor("ortsonofftime", () => { TransitionTime = stf.ReadFloatBlock(STFReader.Units.None, null); }), new STFReader.TokenProcessor("sigflashduration", () => { stf.MustMatchBlockStart(); FlashTimeOn = stf.ReadFloat(STFReader.Units.None, null); FlashTimeOff = stf.ReadFloat(STFReader.Units.None, null); stf.SkipRestOfBlock(); }), new STFReader.TokenProcessor("signalflags", () => { stf.MustMatchBlockStart(); while (!stf.EndOfBlock()) { switch (stf.ReadString().ToLower()) { case "abs": Abs = true; break; case "no_gantry": NoGantry = true; break; case "semaphore": Semaphore = true; break; default: stf.StepBackOneItem(); STFException.TraceInformation(stf, "Skipped unknown SignalType flag " + stf.ReadString()); break; } } }), }); if (orMode) { // set related MSTS function type OrtsFunctionTypeIndex = OrSignalTypes.Instance.FunctionTypes.FindIndex(i => StringComparer.OrdinalIgnoreCase.Equals(i, ortsFunctionType)); if (!EnumExtension.GetValue(ortsFunctionType, out SignalFunction functionType)) { FunctionType = SignalFunction.Info; } else { FunctionType = functionType; } // set index for Normal Subtype OrtsNormalSubTypeIndex = OrSignalTypes.Instance.NormalSubTypes.FindIndex(i => StringComparer.OrdinalIgnoreCase.Equals(i, ortsNormalSubType)); // set SNCA NumClearAhead_MSTS = -2; NumClearAhead_ORTS = numClearAhead; } else { // set defaulted OR function type OrtsFunctionTypeIndex = (int)FunctionType; // set SNCA NumClearAhead_MSTS = numdefs == 1 ? numClearAhead : -2; NumClearAhead_ORTS = numdefs == 2 ? numClearAhead : -2; } }