using SimpleCore.Traffic; using System; using System.Collections.Generic; using System.Linq; using SimpleLite.RCS; using SimpleLite.RCS.CarTypes; using SimpleCore.Library; using SimpleCore.PropType; using SimpleCore; using System.Threading; using SimpleLite.CADTools; using SimpleLite.Props; using SimpleLite.UI; using SimpleLite; using System.Windows.Forms; using Newtonsoft.Json; namespace StandardScene.InterLock { public class MyDictionary where TItem : new() { private readonly Dictionary dictionary = new(); public TItem this[TKey key] { get { if (dictionary.TryGetValue(key, out var item)) return item; var newItem = new TItem(); dictionary[key] = newItem; return newItem; } set => dictionary[key] = value; } public ICollection Keys { get => dictionary.Keys; } } public class AbstractInterlockMission : Mission { public bool GetAskEnter(int siteId) { if (!SiteFilter(siteId)) return false; lock (sync) return askEnter[siteId]; } public void SetAllowEnter(int siteId, bool value) { if (!SiteFilter(siteId)) return; lock (sync) allowEnter[siteId] = value; } public bool GetAskExit(int siteId) { if (!SiteFilter(siteId)) return false; lock (sync) return askExit[siteId]; } public void SetAllowExit(int siteId, bool value) { if (!SiteFilter(siteId)) return; lock (sync) allowExit[siteId] = value; } public bool GetReportLeave(int siteId) { if (!SiteFilter(siteId)) return false; lock (sync) return reportLeave[siteId]; } public void SetAcknowledgeLeave(int siteId, bool value) { if (!SiteFilter(siteId)) return; lock (sync) acknowledgeLeave[siteId] = value; } public int GetInSiteCarId(int siteId) { if (!SiteFilter(siteId)) return -1; lock (sync) return inSiteCarId[siteId]; } // 请求进站(写) [JsonIgnore] private readonly MyDictionary askEnter = new(); // 允许进站(读) [JsonIgnore] private readonly MyDictionary allowEnter = new(); // 请求离站(写) [JsonIgnore] private readonly MyDictionary askExit = new(); // 允许离站(读) [JsonIgnore] private readonly MyDictionary allowExit = new(); // 报告离开(写) [JsonIgnore] private readonly MyDictionary reportLeave = new(); // 确认离开(读) [JsonIgnore] private readonly MyDictionary acknowledgeLeave = new(); // 站点上所在的小车(状态) [JsonIgnore] private readonly MyDictionary inSiteCarId = new(); [JsonIgnore] private object sync = new(); public virtual string GetSiteDisplay(int siteId) { return $"{siteId}"; } /// /// 返回为true的站点,是当前互锁机制关注的站点 /// /// /// public virtual bool SiteFilter(int siteId) { return true; } /// /// 互锁站点进站条件 /// /// /// /// private bool EnterCondition(AbstractCar car, int siteId) { // todo: 是否考虑提前更多申请进入,加快节拍? if (car.status.pendingLocks.First() != siteId) return false; //DateTime startTime = DateTime.Now; //bool validTime = false; lock (sync) { // if (askEnter[siteId] == false) // { // Diagnosis.Post($"{car.name}({car.id}) 申请进站 {GetSiteDisplay(siteId)}", "intertime", true); // //startTime = DateTime.Now; // //validTime = true; // } askEnter[siteId] = true; if (reportLeave[siteId]) { Diagnosis.Post($"{car.name}({car.id})未被准许进站{GetSiteDisplay(siteId)},前车离开信号未被确认", "interlock", true); return false; } if (allowEnter[siteId]) { Diagnosis.Post($"{car.name}({car.id})准许进站{GetSiteDisplay(siteId)}", "interlock", true); // Diagnosis.Post($"{car.name}({car.id}) 准许进站 {GetSiteDisplay(siteId)}", "intertime", true); lock (sync) askEnter[siteId] = false; return true; } Diagnosis.Post($"{car.name}({car.id})未被准许进站{GetSiteDisplay(siteId)}", "interlock", true); return false; } } /// /// 互锁站点离站条件 /// /// /// /// private bool ExitCondition(AbstractCar car, int siteId) { lock (sync) { askExit[siteId] = true; if (allowExit[siteId]) { Diagnosis.Post($"{car.name}({car.id})准许离站{GetSiteDisplay(siteId)}", "interlock", true); askExit[siteId] = false; return true; } Diagnosis.Post($"{car.name}({car.id})未被准许离站{GetSiteDisplay(siteId)}", "interlock", true); return false; } } /// /// 告知对方设备小车离开,直到对方设备确认收到离开信号 /// /// /// private void LeaveEvent(AbstractCar car, int siteId) { new Thread(() => { while (true) { Thread.Sleep(100); lock (sync) { if (acknowledgeLeave[siteId]) // 已经确认小车离开 { Diagnosis.Post($"{GetSiteDisplay(siteId)}确认{car.name}({car.id})离开", "interlock", true); break; } } Diagnosis.Post($"{car.name}({car.id})告知离开{GetSiteDisplay(siteId)}", "interlock", true); lock (sync) reportLeave[siteId] = true; } lock (sync) reportLeave[siteId] = false; }).Start(); } [JsonIgnore] private bool enableSimulation = false; [JsonIgnore] public bool started = false; [JsonIgnore] public Thread showT; [MethodMember(Name = "启动进程", Description = "处理安全互锁")] public override void Execute() { if (started) return; // started = true; status.status = "已启动"; showT = new Thread(() => { while (true) { Thread.Sleep(300); try { if (fields.TryGetValue("enableSimulation", out var userEnableSimulation)) enableSimulation = bool.Parse(userEnableSimulation); foreach (var site in SimpleLib.GetAllSites().Where(ss => SiteFilter(ss.id))) { var carId = -1; foreach (var car in SimpleLib.GetAllCars()) { lock (TrafficControl.syncTrafficSequence) { if (car.status.holdingLocks.Length == 1 && car.status.holdingLocks[0] == site.id) { carId = car.id; break; } } } lock (sync) inSiteCarId[site.id] = carId; } // if (!onDisplay) continue; var painter = SimpleMonitor.getPainter("DemoInterlockMission"); painter.clear(); if (!onDisplay) continue; //string str; //lock (sync) //{ // var keys = askEnter.Keys.Concat(askExit.Keys).Concat(reportLeave.Keys).ToHashSet() // .OrderBy(kk => int.Parse(SimpleLib.GetSite(kk).fields["ASNub"])).ToList(); // str = $"站点\t\t请求进站\t允许进站\t请求离站\t允许离站\t上报离开\t确认离开\t小车id\n" + // $"{string.Join("\n", keys.Select(key => // $"{GetSiteDisplay(key)}\t{askEnter[key]}\t\t{allowEnter[key]}\t\t" + // $"{askExit[key]}\t\t{allowExit[key]}\t\t" + // $"{reportLeave[key]}\t\t{acknowledgeLeave[key]}\t\t{inSiteCarId[key]}"))}"; //} //painter.drawTextFixed(str, new SolidBrush(Color.Black), VirtualPainter.DrawPosition.RightTop, Color.AliceBlue); } catch (Exception ex) { Diagnosis.Post(ExceptionFormatter.FormatEx(ex), "interlock", true); } } }) { Name = "InterlockMission" }; showT.Start(); } [JsonIgnore] private bool onDisplay = true; [MethodMember(Name = "切换显示", Description = "是否在右下角显示")] public void SwitchAlwaysOnDisplay() { onDisplay = !onDisplay; } private async void SimButton(string title, MyDictionary toManipulate) { //if (!enableSimulation) return; G.pushStatus("请选择站点"); var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); var site = SimpleLib.GetSite(pt.site); if (SiteFilter(site.id)) { var vv = await Program.UI.Input("请输入true/false", title, "true"); if (vv == null || !bool.TryParse(vv, out var allow)) { MessageBox.Show("输入错误"); return; } lock (sync) toManipulate[site.id] = allow; } } [MethodMember(Name = "模拟准许进站")] public void SimAllowEnter() { SimButton("模拟准许进入状态", allowEnter); } [MethodMember(Name = "模拟准许离站")] public void SimAllowExit() { SimButton("模拟准许离站状态", allowExit); } [MethodMember(Name = "模拟确认离开")] public void SimLeaveNoted() { SimButton("模拟确认离开状态", acknowledgeLeave); } [MethodMember(Name = "模拟发送离开")] public async void SimReportLeave() { // if (!enableSimulation) return; G.pushStatus("请选择站点"); var pt = await Program.UI.getPoint(new UIOps.getPointOptions() { site = true }); var site = SimpleLib.GetSite(pt.site); if (SiteFilter(site.id)) { lock (sync) reportLeave[site.id] = true; } new Thread(() => { var t1 = DateTime.Now; while (true) { Thread.Sleep(100); lock (sync) { var deltaT = (DateTime.Now - t1).TotalSeconds; if (acknowledgeLeave[site.id]||deltaT>5) // 已经确认小车离开 { Diagnosis.Post($"{GetSiteDisplay(site.id)} 停止发送离开", "simlikai", true); break; } } Diagnosis.Post($"模拟告知离开{GetSiteDisplay(site.id)}", "simlikai", true); lock (sync) reportLeave[site.id] = true; } lock (sync) reportLeave[site.id] = false; }).Start(); // SimButton("模拟确认离开状态", reportLeave); } public AbstractInterlockMission() { Diagnosis.Post($"InterlockMechanism loaded", "interlock", true); TrafficControl.BeforeLock += (car, siteId) => { if (SiteFilter(siteId)) // EnterCondition return EnterCondition(car, siteId); var site = SimpleLib.GetSite(siteId); if (site.fields.ContainsKey("PreAskEnterSite") && int.TryParse(site.fields["PreAskEnterSite"], out int PreAskEnterSiteId)) { var presite = SimpleLib.GetSite(PreAskEnterSiteId); if (presite != null && SiteFilter(PreAskEnterSiteId)) { Diagnosis.Post($"{car.name}({car.id})在{siteId}-{GetSiteDisplay(siteId)},提前申请 {PreAskEnterSiteId} 的请求进入", "interlock", true); askEnter[PreAskEnterSiteId] = true; } } // ExitCondition lock (TrafficControl.syncTrafficSequence) if (!car.status.holdingLocks.Any(ss => SiteFilter(ss))) return true; var isNeighbor = false; var lockSiteId = -1; foreach (var trackId in site.relatedTracks) { var track = SimpleLib.GetTrack(trackId); if (SiteFilter(track.siteA)) { isNeighbor = true; lockSiteId = track.siteA; break; } if (SiteFilter(track.siteB)) { isNeighbor = true; lockSiteId = track.siteB; break; } } return !isNeighbor || ExitCondition(car, lockSiteId); }; TrafficControl.AfterLeave += (car, siteId) => { if (SiteFilter(siteId)) LeaveEvent(car, siteId); }; } } }