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;
            }
        }
示例#2
0
        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);
        }
示例#3
0
        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.");
            }
        }
示例#4
0
            // 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;
                            }
                        }
                    }),
                });
            }
示例#5
0
        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);
        }
示例#6
0
 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();
         }),
     });
示例#7
0
        /// <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;
                        }
                    }
                }),
            });
        }
示例#8
0
        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);
        }
示例#9
0
        /// <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;
            }
        }
示例#10
0
 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();
 }
示例#11
0
        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());
            }
        }
示例#12
0
        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();
        }
示例#13
0
        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);
            }
        }
示例#14
0
        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);
            }
        }
示例#15
0
 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();
 }
示例#16
0
        /// <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;
                        }
                    }
                }),
            });
        }
示例#17
0
        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();
        }
示例#18
0
 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();
 }
示例#19
0
        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();
        }
示例#20
0
            // 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;
                            }
                        }
                    }),
                });
示例#21
0
        /// <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;
                        }
                    }
                }),
            });
        }
示例#22
0
 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();
 }
示例#23
0
        /// <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;
                        }
                    }
                }),
            });
        }
示例#24
0
        /// <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;
                        }
                    }
                }),
            });
        }
示例#25
0
        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;
            }
        }
示例#26
0
        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
示例#27
0
        /// <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;
                        }
                    }
                }
            }
        }
示例#28
0
 public override void TraceInformation(string message)
 {
     STFException.TraceInformation(stf, message);
 }
示例#29
0
        /// <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;
            }
        }