init commit
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
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<TKey, TItem> where TItem : new()
|
||||
{
|
||||
private readonly Dictionary<TKey, TItem> 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<TKey> 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<int, bool> askEnter = new();
|
||||
// 允许进站(读)
|
||||
[JsonIgnore] private readonly MyDictionary<int, bool> allowEnter = new();
|
||||
|
||||
// 请求离站(写)
|
||||
[JsonIgnore] private readonly MyDictionary<int, bool> askExit = new();
|
||||
// 允许离站(读)
|
||||
[JsonIgnore] private readonly MyDictionary<int, bool> allowExit = new();
|
||||
|
||||
// 报告离开(写)
|
||||
[JsonIgnore] private readonly MyDictionary<int, bool> reportLeave = new();
|
||||
// 确认离开(读)
|
||||
[JsonIgnore] private readonly MyDictionary<int, bool> acknowledgeLeave = new();
|
||||
|
||||
// 站点上所在的小车(状态)
|
||||
[JsonIgnore] private readonly MyDictionary<int, int> inSiteCarId = new();
|
||||
|
||||
[JsonIgnore] private object sync = new();
|
||||
|
||||
public virtual string GetSiteDisplay(int siteId)
|
||||
{
|
||||
return $"{siteId}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回为true的站点,是当前互锁机制关注的站点
|
||||
/// </summary>
|
||||
/// <param name="siteId"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool SiteFilter(int siteId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 互锁站点进站条件
|
||||
/// </summary>
|
||||
/// <param name="car"></param>
|
||||
/// <param name="siteId"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 互锁站点离站条件
|
||||
/// </summary>
|
||||
/// <param name="car"></param>
|
||||
/// <param name="siteId"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 告知对方设备小车离开,直到对方设备确认收到离开信号
|
||||
/// </summary>
|
||||
/// <param name="car"></param>
|
||||
/// <param name="siteId"></param>
|
||||
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<int, bool> 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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user