using System; using System.Xml; using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Device; using Aitex.Core.RT.Event; using Aitex.Core.RT.IOCore; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.Util; namespace VirgoRT.Devices { public class IoCylinder : BaseDevice, IDevice { private readonly DIAccessor _diON; private readonly DIAccessor _diOFF; private readonly DOAccessor _doON; private readonly DOAccessor _doOFF; private CylinderState _operation; private readonly DeviceTimer _timer = new DeviceTimer(); private readonly R_TRIG _trigReset = new R_TRIG(); private readonly R_TRIG _trigError = new R_TRIG(); public bool EnableOpen { get; set; } public bool EnableClose { get; set; } private string _error = null; public int SetPoint { get { if (_doON.Value && _doOFF.Value) return (int)CylinderState.Error; if (_doON.Value && !_doOFF.Value) return (int)CylinderState.Open; if (!_doON.Value && _doOFF.Value) return (int)CylinderState.Close; if (!_doON.Value && !_doOFF.Value) return (int)CylinderState.Unknown; return (int)CylinderState.Unknown; } } public CylinderState State { get { if (_diON != null && _diOFF != null) { if (ONFeedback && _diOFF.Value) return CylinderState.Error; if (ONFeedback && !_diOFF.Value) return CylinderState.Open; if (!ONFeedback && _diOFF.Value) return CylinderState.Close; if (!ONFeedback && !_diOFF.Value) return CylinderState.Unknown; } return CylinderState.Unknown; } } public bool ONFeedback { get { return _diON != null && _diON.Value; } } public bool OFFFeedback { get { return _diOFF != null && _diOFF.Value; } } public bool ONSetPoint { get { return _doON != null && _doON.Value; } private set { if (_doON != null ) { if (!_doON.Check(value, out _error)) { LOG.Write(_error); } else { _doON.Value = value; } } } } public bool OFFSetPoint { get { return _doOFF != null && _doOFF.Value; } private set { if (_doOFF != null ) { if (!_doOFF.Check(value, out _error)) { LOG.Write(_error); } else { _doOFF.Value = value; } } } } private AITCylinderData DeviceData { get { return new AITCylinderData { Module = Module, DeviceName = Name, DeviceSchematicId = DeviceID, DisplayName = Display, OpenFeedback = ONFeedback, CloseFeedback = OFFFeedback, OpenSetPoint = ONSetPoint, CloseSetPoint = OFFSetPoint }; } } private object _locker = new object(); public IoCylinder(string module, XmlElement node, string ioModule = "") { base.Module = module; base.Name = node.GetAttribute("id"); base.Display = node.GetAttribute("display"); base.DeviceID = node.GetAttribute("schematicId"); _operation = CylinderState.Unknown; _diON = ParseDiNode("diON", node, ioModule); _diOFF = ParseDiNode("diOFF", node, ioModule); _doON = ParseDoNode("doON", node, ioModule); _doOFF = ParseDoNode("doOFF", node, ioModule); } public bool Initialize() { DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData); OP.Subscribe($"{Module}.{Name}.{AITCylinderOperation.Open}", (s, objects) => SetCylinder(true, out _)); OP.Subscribe($"{Module}.{Name}.{AITCylinderOperation.Close}", (s, objects) => SetCylinder(false, out _)); OP.Subscribe($"{Module}.{Name}.{AITCylinderOperation.SetState}", (out string reason, int time, object[] param) => { bool isUp = (string)param[0] == "Up" ? true : false; SetCylinder(isUp, out reason); return true; }); DEVICE.Register($"{Module}.{Name}.{AITCylinderOperation.Open}", (out string reason, int time, object[] param) => SetCylinder(true, out reason)); DEVICE.Register($"{Module}.{Name}.{AITCylinderOperation.Close}", (out string reason, int time, object[] param) => SetCylinder(false, out reason)); return true; } public void Terminate() { OFFSetPoint = false; ONSetPoint = false; } public bool SetCylinder(bool isOpen, out string reason) { reason = ""; //if (isOpen && State == CylinderState.Open) //{ // EV.PostInfoLog(Module, $"检查到气缸 {Name} 已经开"); // return true; //} //if (!isOpen && State == CylinderState.Close) //{ // EV.PostInfoLog(Module, $"检查到气缸 {Name} 已经关"); // return true; //} lock (_locker) { _timer.Start(5 * 1000); ONSetPoint = isOpen; reason += _error; reason += " "; OFFSetPoint = !isOpen; reason += _error; _operation = isOpen ? CylinderState.Open : CylinderState.Close; } EV.PostInfoLog(Module, $"{(isOpen ? "打开" : "关闭")} 气缸 {Name}"); return true; } public void Monitor() { try { lock (_locker) { if (_timer.IsTimeout()) { _timer.Stop(); if (State != _operation) { if (_operation == CylinderState.Open) { if (!_doON.Check(true, out var reason)) EV.PostAlarmLog(Module, $"{Name} 气缸信号无法打开, interlock, " + reason); else EV.PostAlarmLog(Module, $"{Name} 气缸信号仍然关闭"); } else { if (!_doON.Check(false, out var reason)) EV.PostAlarmLog(Module, $"{Name} 气缸信号无法关闭, interlock, " + reason); else EV.PostAlarmLog(Module, $"{Name} 气缸信号仍然打开"); } } _operation = (CylinderState)SetPoint; } else if (_timer.IsIdle()) { _trigReset.CLK = SetPoint != (int)_operation; // fire event only check at first, SetPoint set by interlock if (_trigReset.Q) { if (_operation == CylinderState.Open) { EV.PostAlarmLog(Module, !_doON.Check(true, out var reason) ? $"气缸信号 {Display} was Close,Reason:{reason}" : $"气缸信号 {Display} was Close,Reason PLC kept"); } else { EV.PostAlarmLog(Module, !_doON.Check(false, out var reason) ? $"气缸信号 {Display} was Open,Reason:{reason}" : $"气缸信号 {Display} was Open,Reason PLC Kept"); } _operation = (CylinderState)SetPoint; } } } _trigError.CLK = State == CylinderState.Error; if (_trigError.Q) { EV.PostAlarmLog(Module, $"{Name} 气缸状态错误"); } //if ((SetPoint == (int)State) && (SetPoint == (int)CylinderState.Open || SetPoint == (int)CylinderState.Close)) //{ // OFFSetPoint = false; // ONSetPoint = false; //} } catch (Exception ex) { LOG.Write(ex); } } public void Reset() { _trigReset.RST = true; _trigError.RST = true; } } }