using System; using System.Threading; using TeamAAS.Global.Devices; using TeamAAS.Motion.Models; namespace TeamAAS.Motion.Motions { /// /// 连接后轮询急停/停止 DI。急停边沿急停+持续下使能;停止边沿停轴并禁止新运动。 /// internal sealed class MotionSafetyGuard : IDisposable { private readonly Func _cfg; private readonly Func _readDi; private readonly Action _onEstopRise; private readonly Action _onEstopHold; private readonly Action _onStopRise; private readonly object _sync = new object(); private Timer _timer; private bool _estop; private bool _stop; public bool EstopActive { get { lock (_sync) return _estop; } } public bool StopActive { get { lock (_sync) return _stop; } } public event EventHandler Changed; public MotionSafetyGuard( Func cfg, Func readDi, Action onEstopRise, Action onEstopHold, Action onStopRise) { _cfg = cfg; _readDi = readDi; _onEstopRise = onEstopRise; _onEstopHold = onEstopHold; _onStopRise = onStopRise; } public void Start() { Stop(); _timer = new Timer(_ => Poll(), null, 30, 30); } public void Stop() { var t = _timer; _timer = null; t?.Dispose(); lock (_sync) { _estop = false; _stop = false; } } public bool AllowEnable(out string reason) { if (EstopActive) { reason = "急停有效:禁止使能"; return false; } reason = null; return true; } public bool AllowMotion(out string reason) { if (EstopActive) { reason = "急停有效:禁止运动"; return false; } if (StopActive) { reason = "停止信号有效:禁止运动"; return false; } reason = null; return true; } public void Dispose() => Stop(); private void Poll() { MotionDeviceConfig cfg; try { cfg = _cfg(); } catch { return; } if (cfg == null) return; bool estop = ReadActive(cfg.EstopDiBank, cfg.EstopDiIndex, cfg.EstopActiveHigh); bool stop = ReadActive(cfg.StopDiBank, cfg.StopDiIndex, cfg.StopActiveHigh); bool estopRise, stopRise, changed; lock (_sync) { estopRise = estop && !_estop; stopRise = stop && !_stop; changed = estop != _estop || stop != _stop; _estop = estop; _stop = stop; } try { if (estopRise) _onEstopRise?.Invoke(); if (estop) _onEstopHold?.Invoke(); else if (stopRise) _onStopRise?.Invoke(); } catch { } if (changed) { try { Changed?.Invoke(this, EventArgs.Empty); } catch { } } } private bool ReadActive(MotionIoBank bank, int di, bool activeHigh) { if (di < 0) return false; bool bit; try { bit = _readDi(bank, di); } catch { return false; } return activeHigh ? bit : !bit; } } }