using System; using System.Threading; using SimpleCore.Library; using StandardScene.Signal.Model; namespace StandardScene.Signal.Plc { /// /// 单机构 200ms 循环:心跳、读允许进出、回读写值、按需写入、自动离站。 /// 对齐反编译 PLCManager.ExcuteThreadMethod。 /// public sealed class PlcStationWorker : IDisposable { private readonly SiemensPlcSession _session; private readonly int _pollMs; private readonly Action _onTick; private CancellationTokenSource _cts; private Thread _thread; private DateTime _lastFailLog = DateTime.MinValue; public PlcStationWorker(PlcStationRuntime runtime, SiemensPlcSession session, int pollMs, Action onTick = null) { Runtime = runtime ?? throw new ArgumentNullException(nameof(runtime)); _session = session ?? throw new ArgumentNullException(nameof(session)); _pollMs = Math.Max(50, pollMs); _onTick = onTick; } public PlcStationRuntime Runtime { get; } public void Start() { if (_thread != null && _thread.IsAlive) return; _cts = new CancellationTokenSource(); _thread = new Thread(Loop) { IsBackground = true, Name = $"PlcStation:{Runtime.Config.JgName}" }; _thread.Start(); Diagnosis.Log($"{Runtime.Config.JgName} Worker 已启动 → {Runtime.Config.Ip}:{Runtime.Config.Port}", "PLC", true); } public void Stop() { try { _cts?.Cancel(); } catch { } try { _thread?.Join(2000); } catch { } _thread = null; _session.Close(); Runtime.IsConnected = false; Diagnosis.Log($"{Runtime.Config.JgName} Worker 已停止", "PLC", true); } public void Dispose() => Stop(); private void Loop() { var token = _cts.Token; while (!token.IsCancellationRequested) { try { lock (Runtime.Sync) Tick(); } catch (Exception ex) { Runtime.LastError = ex.Message; Runtime.IsConnected = false; LogFail($"{Runtime.Config.JgName} 周期异常 {ex.Message}"); } try { _onTick?.Invoke(); } catch { } try { Thread.Sleep(_pollMs); } catch (ThreadInterruptedException) { break; } } } internal void Tick() { var cfg = Runtime.Config; Runtime.Heart = !Runtime.Heart; if (!string.IsNullOrWhiteSpace(cfg.HeartDb)) { if (_session.TryReadBool(cfg.HeartDb, out var bit)) { _session.TryWriteBool(cfg.HeartDb, !bit); Thread.Sleep(20); } else { MarkDisconnected("心跳读写失败"); return; } } if (!string.IsNullOrWhiteSpace(cfg.ReadDb)) { if (_session.TryReadByte(cfg.ReadDb, out var rb)) Runtime.ReadSignal = PlcStationLogic.ConvertRead(rb); else { MarkDisconnected("读地址失败"); return; } } if (!string.IsNullOrWhiteSpace(cfg.WriteDb)) { if (_session.TryReadByte(cfg.WriteDb, out var wb)) { Runtime.ReadWriteSignal = PlcStationLogic.ConvertWrite(wb); if (Runtime.ReadWriteSignal == Runtime.WriteSignal) Runtime.WriteSuccess = true; } else { MarkDisconnected("写地址回读失败"); return; } if (Runtime.WriteSignal != Runtime.ReadWriteSignal && !Runtime.WriteSuccess) { Diagnosis.Log( $"AGV→机构 {cfg.JgName} 写入【{Runtime.WriteSignal}】请求进入{Runtime.RequestInAgvNo} 到达{Runtime.InAgvNo}", "JGControl", true); _session.TryWriteByte(cfg.WriteDb, (byte)Runtime.WriteSignal); } } Runtime.IsConnected = true; Runtime.LastError = ""; PlcStationLogic.AutoLeave(Runtime); } private void MarkDisconnected(string reason) { Runtime.IsConnected = false; Runtime.LastError = reason; LogFail($"{Runtime.Config.JgName} {reason}"); } private void LogFail(string msg) { if ((DateTime.Now - _lastFailLog).TotalSeconds < 5) return; _lastFailLog = DateTime.Now; Diagnosis.Log(msg, "PLC", true); } } }