public void Start() { if (_cts != null) { return; } _cts = new CancellationTokenSource(); _tkn = _cts.Token; Task = new Task(() => { // ********************************************************************** // ** Route Thread // ********************************************************************** bool isRouteSet = false; // flag initialization of route's handling thread Locomotive locObject = null; // the current locomotive on the route List <ItemData> routeData = new List <ItemData>(); // s88 and switches on the route for (;;) { var s = SrcBlock.ToString().Replace(" ", ""); var d = DestBlock.ToString().Replace(" ", ""); if (!isRouteSet) { Route.IsBusy = true; isRouteSet = true; Autoplayer?.SetRoute(Route, true); int locObjectId = -1; if (Autoplayer != null) { locObjectId = Autoplayer.GetLocObjectIdOfRoute(Route); } locObject = Model.Dispatcher.GetDataProvider().GetObjectBy(locObjectId) as Locomotive; DestBlock.SetLocomotivePreviewObjectId(locObjectId); if (locObject != null) { Model?.LogAutoplay($"{Prefix} Locomotive: {locObject.Name}"); Trace.WriteLine($"{Prefix} Locomotive: {locObject.Name}"); } TrackWeaveItems weaverItems = new TrackWeaveItems(); var weaveFilepath = Path.Combine(Model.Project.Dirpath, Model.Project.Track.Weave); if (!weaverItems.Load(weaveFilepath)) { throw new Exception("Reading weave file failed."); } Dictionary <TrackInfo, List <IItem> > trackObjects = new Dictionary <TrackInfo, List <IItem> >(); foreach (var pt in Route) { if (pt == null) { continue; } var item = Model.TrackEntity.Track.Get(pt.X, pt.Y); if (item == null) { continue; } var itemObjects = Model.Dispatcher.Weaver.GetObject(item); if (itemObjects.Count == 0) { continue; } if (trackObjects.ContainsKey(item)) { trackObjects[item].AddRange(itemObjects); } else { trackObjects.Add(item, itemObjects); } } #region DEBUG route's track info Trace.WriteLine($"{Prefix} Route's track infos:"); foreach (var info in trackObjects.Keys) { var objs = trackObjects[info]; Trace.Write($"{Prefix} {info}: "); foreach (var o in objs) { Trace.Write($"{o.ObjectId}, "); } Trace.WriteLine("||"); } #endregion Dictionary <TrackInfo, S88> s88TrackObjects = new Dictionary <TrackInfo, S88>(); Dictionary <TrackInfo, TrackInformation.Switch> switchTrackObjects = new Dictionary <TrackInfo, TrackInformation.Switch>(); #region prepare route data foreach (var trackInfo in trackObjects.Keys) { var itemObjects = trackObjects[trackInfo]; if (itemObjects.Count == 0) { continue; } foreach (var obj in itemObjects) { if (obj == null) { continue; } if (obj is S88) { s88TrackObjects.Add(trackInfo, obj as S88); } if (obj is TrackInformation.Switch) { switchTrackObjects.Add(trackInfo, obj as TrackInformation.Switch); } } } foreach (var trackInfo in s88TrackObjects.Keys) { var s88Obj = s88TrackObjects[trackInfo]; var fnc = Weaver.GetCheckFnc(s88Obj, trackInfo); if (fnc == null) { Model.LogError($"S88-Checker is missing: {s88Obj}"); } var data = new ItemData { Route = Route, Info = trackInfo, Item = s88Obj, S88Checker = fnc }; routeData.Add(data); } foreach (var trackInfo in switchTrackObjects.Keys) { var switchObj = switchTrackObjects[trackInfo]; var data = new ItemData { Route = Route, Info = trackInfo, Item = switchObj }; routeData.Add(data); } var sensorsAndEvents = GetBlockData(DestBlock); if (sensorsAndEvents != null) { foreach (var sensorName in sensorsAndEvents.Keys) { var eventName = sensorsAndEvents[sensorName]; TrackInfo sensorTrackInfo = null; foreach (var item in Model.TrackEntity.Track) { if (item == null || string.IsNullOrEmpty(item.Name)) { continue; } if (item.Name.Equals(sensorName)) { sensorTrackInfo = item; break; } } if (sensorTrackInfo != null) { var it = Get(routeData, sensorTrackInfo.X, sensorTrackInfo.Y); if (it == null) { var s88Obj = Helper.GetObject(Model.Dispatcher, Model.TrackEntity.Track, sensorTrackInfo.X, sensorTrackInfo.Y); var fnc = Weaver.GetCheckFnc(s88Obj, sensorTrackInfo); if (fnc == null) { Model.LogError($"S88-Checker is missing: {s88Obj}"); } var data = new ItemData { Route = Route, Info = sensorTrackInfo, Item = s88Obj, DestBlockEvent = eventName, S88Checker = fnc }; routeData.Add(data); } else { it.DestBlockEvent = eventName; } Trace.WriteLine($"{Prefix} Sensor({sensorName}) with Event({eventName})"); } } } #endregion #region set switches to let the locomotive pass the route foreach (var data in routeData) { if (data == null || !data.IsSwitch || data.ItemSwitch == null) { continue; } var sw = data.ItemSwitch; var v = data.HasSwitchTurn ? 0 : 1; if (sw.InvertCommand) { if (v == 1) { v = 0; } else { v = 1; } } var vs = v == 1 ? "TURN" : "STRAIGHT"; Trace.WriteLine($"{Prefix} Switch '{sw.Name1}' change to '{vs}'"); sw.ChangeDirection(v); } #endregion if (locObject != null) { locObject.ChangeDirection(false); locObject.ChangeSpeed(locObject.MaxSpeedPercentage); } Model?.LogAutoplay($"{Prefix} {s} TO {d}"); Trace.WriteLine($"{Prefix} {s} TO {d}"); Model.UiSyncCtx?.Send((x) => { Model.TrackEntity.UpdateAllVisualBlocks(); }, null); } foreach (var s88Data in routeData) { if (s88Data == null || !s88Data.IsS88 || s88Data.ItemS88 == null) { continue; } if (s88Data.S88HasBeenHandled) { continue; } bool state = false; try { var bb = s88Data.S88Checker(); if (bb != null && bb.State != null) { state = bb.State.Value; } } catch (Exception ex) { Model?.LogAutoplay($"{Prefix} {ex.Message}"); Trace.WriteLine($"{Prefix} {ex.Message}"); } if (state) { s88Data.S88HasBeenHandled = true; //Model?.LogAutoplay($"{Prefix} {s88Data.Info} {s88Data.ItemS88} state '{state}' -> {s88Data.DestBlockEvent}"); string evName = s88Data.DestBlockEvent; if (!string.IsNullOrEmpty(evName)) { var stopped = false; if (evName.Equals("enter", StringComparison.OrdinalIgnoreCase)) { if (locObject != null) { var blockSpeed = locObject.BlockSpeedPercentage; if (blockSpeed >= locObject.MaxSpeedPercentage) { blockSpeed = -1; } if (blockSpeed <= 0) { var currentSpeed = locObject.Speed; currentSpeed -= (int)(currentSpeed / 2.0f); locObject.ChangeSpeed(currentSpeed); } else { locObject.ChangeSpeed(blockSpeed); } //locObject.ChangeSpeed(Locomotive.SpeedBlockEntered); } Trace.WriteLine($"{Prefix} New speed {Locomotive.SpeedBlockEntered} for {locObject.Name}"); } else if (evName.Equals("enter in", StringComparison.OrdinalIgnoreCase)) { if (locObject != null) { var currentSpeed = locObject.Speed; var secondsToStop = 3; int speedSteps = currentSpeed / secondsToStop; var o = locObject; var task = Task.Run(() => { currentSpeed -= speedSteps; if (currentSpeed <= 0) { o?.ChangeSpeed(Locomotive.SpeedStop); } else { o?.ChangeSpeed(currentSpeed); } Trace.WriteLine($"{Prefix} Loc speed {locObject.Name} is {currentSpeed}"); Thread.Sleep(1 * 1000); }); task.Wait(); stopped = true; } } else if (evName.Equals("in", StringComparison.OrdinalIgnoreCase)) { if (locObject != null) { locObject.ChangeSpeed(Locomotive.SpeedStop); } Trace.WriteLine($"{Prefix} STOP {Locomotive.SpeedStop}"); stopped = true; } if (stopped) { // move loc from source block to destination block // reset current route // be ready for next routing decision SrcBlock.SetLocomotiveObjectId(-1); DestBlock.SetLocomotiveObjectId(locObject.ObjectId); DestBlock.SetLocomotivePreviewObjectId(-1); Model.UiSyncCtx?.Send(x => { Autoplayer.SetRoute(Route, false); Model.TrackEntity.UpdateAllVisualBlocks(); Model.Save(); }, null); Route.IsBusy = false; Route.StartBusiness = DateTime.MaxValue; Route.StopBusiness = DateTime.Now; if (_cts != null) { try { _cts.Dispose(); _cts = null; } catch { // ignore } } return; } } } } #region Thread stuff Thread.Sleep(1 * 125); if (_tkn.IsCancellationRequested) { Trace.WriteLine($"{Prefix} Stop requested..."); Route.IsBusy = false; return; } #endregion } // ********************************************************************** // ** END Route Thread // ********************************************************************** }, _tkn); Task?.Start(); }