184 lines
6.5 KiB
C#
184 lines
6.5 KiB
C#
using System;
|
|||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Threading;
|
||
|
|
using SimpleCore.Library;
|
||
|
|
using SimpleLite.RCS.CarTypes;
|
||
|
|
using StandardScene.Signal.Model;
|
||
|
|
using StandardScene.Signal.Plc;
|
||
|
|
|
||
|
|
namespace StandardScene.Signal.Logic
|
||
|
|
{
|
||
|
|
/// <summary>1s 扫全场车,对齐反编译 DefaultPlugin.ExcuteThreadMethod + StarSignManage。</summary>
|
||
|
|
public sealed class CarScanWorker : IDisposable
|
||
|
|
{
|
||
|
|
private readonly Func<IReadOnlyList<PlcStationRuntime>> _runtimes;
|
||
|
|
private readonly int _tickMs;
|
||
|
|
private readonly object _configLock = new object();
|
||
|
|
private Dictionary<int, HandshakePointModel> _handshake = new Dictionary<int, HandshakePointModel>();
|
||
|
|
private Dictionary<int, ReleasePointModel> _release = new Dictionary<int, ReleasePointModel>();
|
||
|
|
private readonly Dictionary<string, int> _rateFlowLog = new Dictionary<string, int>();
|
||
|
|
private readonly Dictionary<int, DateTime> _chargeStarted = new Dictionary<int, DateTime>();
|
||
|
|
private readonly Dictionary<int, DateTime> _unknownTypeLog = new Dictionary<int, DateTime>();
|
||
|
|
private CancellationTokenSource _cts;
|
||
|
|
private Thread _thread;
|
||
|
|
private int _tickCount;
|
||
|
|
|
||
|
|
public CarScanWorker(Func<IReadOnlyList<PlcStationRuntime>> runtimes, int tickMs)
|
||
|
|
{
|
||
|
|
_runtimes = runtimes ?? throw new ArgumentNullException(nameof(runtimes));
|
||
|
|
_tickMs = Math.Max(100, tickMs);
|
||
|
|
}
|
||
|
|
|
||
|
|
public int TickCount => _tickCount;
|
||
|
|
|
||
|
|
public void ReplaceConfig(IEnumerable<HandshakePointModel> handshake, IEnumerable<ReleasePointModel> release)
|
||
|
|
{
|
||
|
|
lock (_configLock)
|
||
|
|
{
|
||
|
|
_handshake = (handshake ?? Array.Empty<HandshakePointModel>())
|
||
|
|
.Where(x => x != null && x.Sit > 0 && !string.IsNullOrWhiteSpace(x.SignalType))
|
||
|
|
.GroupBy(x => x.Sit)
|
||
|
|
.ToDictionary(g => g.Key, g => g.First());
|
||
|
|
_release = (release ?? Array.Empty<ReleasePointModel>())
|
||
|
|
.Where(x => x != null && x.Sit > 0)
|
||
|
|
.GroupBy(x => x.Sit)
|
||
|
|
.ToDictionary(g => g.Key, g => g.First());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Start()
|
||
|
|
{
|
||
|
|
if (_thread != null && _thread.IsAlive) return;
|
||
|
|
_cts = new CancellationTokenSource();
|
||
|
|
_thread = new Thread(Loop)
|
||
|
|
{
|
||
|
|
IsBackground = true,
|
||
|
|
Name = "SignalScan"
|
||
|
|
};
|
||
|
|
_thread.Start();
|
||
|
|
Diagnosis.Log("扫车握手线程已启动", "Signal", true);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Stop()
|
||
|
|
{
|
||
|
|
try { _cts?.Cancel(); }
|
||
|
|
catch { }
|
||
|
|
try { _thread?.Join(2000); }
|
||
|
|
catch { }
|
||
|
|
_thread = null;
|
||
|
|
Diagnosis.Log("扫车握手线程已停止", "Signal", true);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Dispose() => Stop();
|
||
|
|
|
||
|
|
private void Loop()
|
||
|
|
{
|
||
|
|
var token = _cts.Token;
|
||
|
|
while (!token.IsCancellationRequested)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
ScanOnce();
|
||
|
|
Interlocked.Increment(ref _tickCount);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Diagnosis.Log($"检测信号停止的AGV是否可以放行异常:{ex.Message}", "Signal", true);
|
||
|
|
}
|
||
|
|
|
||
|
|
try { Thread.Sleep(_tickMs); }
|
||
|
|
catch (ThreadInterruptedException) { break; }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
internal void ScanOnce()
|
||
|
|
{
|
||
|
|
Dictionary<int, HandshakePointModel> handshake;
|
||
|
|
Dictionary<int, ReleasePointModel> release;
|
||
|
|
lock (_configLock)
|
||
|
|
{
|
||
|
|
handshake = _handshake;
|
||
|
|
release = _release;
|
||
|
|
}
|
||
|
|
|
||
|
|
PlcStationRuntime Find(string name)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(name))
|
||
|
|
return null;
|
||
|
|
var list = _runtimes() ?? Array.Empty<PlcStationRuntime>();
|
||
|
|
return list.FirstOrDefault(x =>
|
||
|
|
string.Equals(x.Config.JgName, name, StringComparison.OrdinalIgnoreCase));
|
||
|
|
}
|
||
|
|
|
||
|
|
foreach (var car in SignalCarAdapter.All())
|
||
|
|
{
|
||
|
|
if (car == null) continue;
|
||
|
|
var sit = SignalCarAdapter.PositionId(car);
|
||
|
|
var spec = ResolveSpec(sit, handshake);
|
||
|
|
|
||
|
|
if (SignalCarAdapter.IsStopped(car))
|
||
|
|
{
|
||
|
|
if (spec != null)
|
||
|
|
DispatchStopped(car, spec, Find);
|
||
|
|
ReleasePointHandler.OnStopped(car, sit, release);
|
||
|
|
}
|
||
|
|
else if (spec != null && spec.Is("JGControl"))
|
||
|
|
{
|
||
|
|
JgControlHandler.OnMoving(car, spec, Find);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private SignalStopSpec ResolveSpec(int sit, IReadOnlyDictionary<int, HandshakePointModel> handshake)
|
||
|
|
{
|
||
|
|
if (sit <= 0) return null;
|
||
|
|
|
||
|
|
if (SignalCarAdapter.TryReadSignalStop(sit, out var type, out var parms))
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(type))
|
||
|
|
return null;
|
||
|
|
return SignalStopSpec.Parse(type, parms);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (handshake != null && handshake.TryGetValue(sit, out var model))
|
||
|
|
return SignalStopSpec.From(model);
|
||
|
|
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void DispatchStopped(Car car, SignalStopSpec spec, Func<string, PlcStationRuntime> find)
|
||
|
|
{
|
||
|
|
if (spec.Is("TimedCharging"))
|
||
|
|
{
|
||
|
|
TimedChargeHandler.OnStopped(car, spec, _chargeStarted);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (spec.Is("JGControl"))
|
||
|
|
{
|
||
|
|
JgControlHandler.OnStopped(car, spec, find, _rateFlowLog);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (spec.Is("PointControl"))
|
||
|
|
{
|
||
|
|
PointControlHandler.OnStopped(car, spec, find);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (spec.Is("RateFlowControl"))
|
||
|
|
{
|
||
|
|
RateFlowHandler.OnStopped(car, spec, _rateFlowLog);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var sit = SignalCarAdapter.PositionId(car);
|
||
|
|
if (_unknownTypeLog.TryGetValue(sit, out var last) && (DateTime.Now - last).TotalSeconds < 10)
|
||
|
|
return;
|
||
|
|
_unknownTypeLog[sit] = DateTime.Now;
|
||
|
|
Diagnosis.Log($"信号类型【{spec.SignalType}】未找到匹配的业务处理模块", "Signal", true);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|