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 { /// 1s 扫全场车,对齐反编译 DefaultPlugin.ExcuteThreadMethod + StarSignManage。 public sealed class CarScanWorker : IDisposable { private readonly Func> _runtimes; private readonly int _tickMs; private readonly object _configLock = new object(); private Dictionary _handshake = new Dictionary(); private Dictionary _release = new Dictionary(); private readonly Dictionary _rateFlowLog = new Dictionary(); private readonly Dictionary _chargeStarted = new Dictionary(); private readonly Dictionary _unknownTypeLog = new Dictionary(); private CancellationTokenSource _cts; private Thread _thread; private int _tickCount; public CarScanWorker(Func> runtimes, int tickMs) { _runtimes = runtimes ?? throw new ArgumentNullException(nameof(runtimes)); _tickMs = Math.Max(100, tickMs); } public int TickCount => _tickCount; public void ReplaceConfig(IEnumerable handshake, IEnumerable release) { lock (_configLock) { _handshake = (handshake ?? Array.Empty()) .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()) .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 handshake; Dictionary release; lock (_configLock) { handshake = _handshake; release = _release; } PlcStationRuntime Find(string name) { if (string.IsNullOrWhiteSpace(name)) return null; var list = _runtimes() ?? Array.Empty(); 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 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 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); } } }