using System; using System.Diagnostics; using System.Xml; using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Event; using Aitex.Core.RT.IOCore; using Aitex.Core.RT.Log; using Aitex.Core.RT.OperationCenter; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using MECF.Framework.Common.Event; namespace Aitex.Core.RT.Device.Unit { public interface IValve { bool TurnValve(bool isOn, out string reason); } public class IoValve : BaseDevice, IDevice, IValve { public string GVName { get { return Name; } } public string GVDeviceID { get { return DeviceID; } } public bool GVIsDefaultOpen { get { return _isDefaultOpen; } } public AlarmEventItem InterlockAlarm; public AlarmEventItem InterlockWarning; public DOAccessor DOOpen => _doOpen; public DOAccessor DOClose => _doClose; private bool _setPoint; [Subscription(AITValveDataPropertyName.SetPoint)] public bool SetPoint //True:open| False:close { get { return _setPoint; //if (_doOpen == null) // return false; //return _isNc ? _doOpen.Value : !_doOpen.Value; } set { if (_doOpen != null) { if (_doOpen.SetValue(_isNc ? value : !value, out _, false)) { _setPoint = value; } } if (_doClose != null) { if (_doClose.SetValue(_isNc ? !value : value, out _, false)) { _setPoint = value; } } } } [Subscription(AITValveDataPropertyName.Status)] public bool Status //True:open | False:close { get { if (_diOpenSensor != null && _diCloseSensor != null) return _diOpenSensor.Value && !_diCloseSensor.Value; if (_diCloseSensor != null) return !_diCloseSensor.Value; if (_diOpen != null) return _isNc ? _diOpen.Value : !_diOpen.Value; if (_doOpen != null) return _isNc ? _doOpen.Value : !_doOpen.Value; if (_doClose != null) return _isNc ? !_doClose.Value : _doClose.Value; return false; } } private AITValveData DeviceData { get { AITValveData data = new AITValveData() { UniqueName = _uniqueName, DeviceName = GVName, DefaultValue = GVIsDefaultOpen, DeviceSchematicId = DeviceID, DisplayName = Display, Feedback = Status, SetPoint = SetPoint, ILKDiValue = ILKDiSensor == null ? true : ILKDiSensor.Value, VirtualFeedback = VirtualStatus, }; return data; } } /// /// normal closed, 0 关闭,1打开 /// public bool _isNc; /// /// default open /// public bool _isDefaultOpen; private DIAccessor _diOpenSensor; private DIAccessor _diCloseSensor; private DIAccessor _diOpen; private DOAccessor _doOpen; private DOAccessor _doClose; private DIAccessor ILKDiSensor; private bool _operation; R_TRIG eventTrigger = new R_TRIG(); R_TRIG _mutexSignalTrigger = new R_TRIG(); DeviceTimer _timer = new DeviceTimer(); DeviceTimer _mutexSignalTimer = new DeviceTimer(); private string _uniqueName; public bool VirtualStatus; private string _writeLog = ""; private SCConfigItem _interlockNoneOrExist; private SCConfigItem _interlockNormaly0nOrOff; private SCConfigItem _interlockDelayTime; private float _interlockDelayTimeInMS; private Stopwatch _interlockDelayTimer = new Stopwatch(); public IoValve(string module, XmlElement node, string ioModule = "") { var attrModule = node.GetAttribute("module"); base.Module = string.IsNullOrEmpty(attrModule) ? module : attrModule; base.Name = node.GetAttribute("id"); base.Display = node.GetAttribute("display"); base.DeviceID = node.GetAttribute("schematicId"); _isNc = Convert.ToBoolean(node.GetAttribute("isNc")); _isDefaultOpen = Convert.ToBoolean(node.GetAttribute("isDefaultOpen")); _diOpenSensor = ParseDiNode("diOpenSensor", node, ioModule); _diCloseSensor = ParseDiNode("diCloseSensor", node, ioModule); _doOpen = ParseDoNode("doOpen", node, ioModule); _diOpen = ParseDiNode("diOpen", node, ioModule); _doClose = ParseDoNode("doClose", node, ioModule); ILKDiSensor = ParseDiNode("ILKDi", node, ioModule); _uniqueName = $"{Module}.{Name}"; } public bool Initialize() { _operation = _isNc ? false : true; if (_isNc == false && _isDefaultOpen == false) { TurnValve(_isDefaultOpen, out string tempReason); } DATA.Subscribe($"Device.{Module}.{GVName}", () => DeviceData); DATA.Subscribe($"{_uniqueName}.DeviceData", () => DeviceData); DATA.Subscribe($"{Module}.{Name}.SetPoint", () => SetPoint); DATA.Subscribe($"{Module}.{Name}.Feedback", () => Status); OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVTurnValve}", InvokeOpenCloseValve); OP.Subscribe($"{_uniqueName}.{AITValveOperation.GVVirtualTurnValve}", InvokeOpenCloseVirtualValve); DEVICE.Register(String.Format("{0}.{1}", Name, AITValveOperation.GVTurnValve), (out string reason, int time, object[] param) => { bool bOn = Convert.ToBoolean((string)param[0]); bool ret = TurnValve(bOn, out reason); if (ret) { reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close"); return true; } return false; }); //for recipe DEVICE.Register(String.Format("{0}", Name), (out string reason, int time, object[] param) => { bool bOn = Convert.ToBoolean((string)param[0]); bool ret = TurnValve(bOn, out reason); if (ret) { reason = string.Format("Valve {0}{1}", Name, bOn ? "Open" : "Close"); return true; } return false; }); //for recipe OP.Subscribe($"{Module}.{Name}", (out string reason, int time, object[] param) => { reason = string.Empty; if (param[0].ToString() == "Continue") { EV.PostInfoLog(Module, $"Turn {Display} Continue"); return true; } bool bOn = Convert.ToBoolean((string)param[0]); bool ret = TurnValve(bOn, out reason); if (!ret) { reason = string.Format("Valve {0}{1} failed", Name, bOn ? "Open" : "Close"); return false; } return true; }); var valveIndex = Name.Replace("ValveAV", ""); _interlockNoneOrExist = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.NoneOrExist"); _interlockNormaly0nOrOff = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.Normaly0nOrOff"); _interlockDelayTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayTime"); return true; } public void Terminate() { string reason; TurnValve(_isDefaultOpen, out reason); } public bool InvokeOpenCloseValve(string method, object[] args) { string reason; bool op = Convert.ToBoolean(args[0]); string name = op ? "Open" : "Close"; if (!TurnValve(op, out reason)) { if (InterlockWarning != null) { InterlockWarning.Description = $"{reason}"; InterlockWarning.Set(); } else { EV.PostWarningLog(Module, $"Can not {name} valve {Module}.{Name}, {reason}"); } return false; } EV.PostInfoLog(Module, $"{name} valve {Module}.{Name}"); return true; } public bool InvokeOpenCloseVirtualValve(string method, object[] args) { bool op = Convert.ToBoolean(args[0]); VirtualStatus = op; return true; } public void Monitor() { if (!string.IsNullOrEmpty(_writeLog)) { LOG.Write(_writeLog); _writeLog = ""; } if (_interlockDelayTimer.IsRunning) { if (_interlockDelayTimer.ElapsedMilliseconds >= _interlockDelayTimeInMS) { _interlockDelayTimer.Stop(); SetPoint = _setPoint; } else { return; } } try { if (_diOpenSensor != null && _diCloseSensor != null && _doOpen != null) { if (_diOpenSensor.Value != _diCloseSensor.Value) { _mutexSignalTimer.Start(2000); } _mutexSignalTrigger.CLK = _mutexSignalTimer.IsTimeout(); if (_mutexSignalTrigger.Q) { EV.PostWarningLog(Module, $"Valve {Name} was abnormal,Reason:diOpenSensor's value is {_diOpenSensor.Value} and diCloseSensor's value is {_diCloseSensor.Value} too."); } } if (_timer.IsTimeout() && _doOpen != null) { _timer.Stop(); if (Status != _operation) { if (_operation) { string reason; if (!_doOpen.Check(_isNc ? true : false, out reason)) EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", ":Failed for interlock " + reason); else EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Open", $":Valve {Name} keep closed "); } else { string reason; if (!_doOpen.Check(_isNc ? true : false, out reason)) EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", ":Failed for interlock " + reason); else EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, "Close", $":Valve {Name} keep open"); } } _operation = SetPoint; } else if (_timer.IsIdle() && _doOpen != null) { eventTrigger.CLK = SetPoint != _operation; // fire event only check at first, SetPoint set by interlock if (eventTrigger.Q) { if (_operation) { string reason; if (!_doOpen.Check(_isNc ? true : false, out reason)) EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason:{2}", Display, "Close", reason)); else EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason {2}", Display, "Close", "PLC kept")); } else { string reason; if (!_doOpen.Check(_isNc ? true : false, out reason)) EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason:{2}", Display, "Open", reason)); else EV.PostMessage(Module, EventEnum.SwInterlock, Module, string.Format("Valve {0} was {1},Reason {2}", Display, "Open", "PLC Kept")); } _operation = SetPoint; } } } catch (Exception ex) { LOG.Write(ex); } } public bool TurnValve(bool isOn, out string reason) { bool bValue = _isNc ? isOn : !isOn; if (_doOpen != null) { if (!_doOpen.Check(bValue, out reason)) { EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ; return false; } } if (_doClose != null) { if (!_doClose.Check(!bValue, out reason)) { EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ; return false; } } var stringOnOff = isOn ? "On" : "Off"; //EV.PostInfoLog(Module, $"Turn {Display} {stringOnOff}"); _writeLog = $"Turn {Display} {stringOnOff}"; reason = ""; if (_interlockNoneOrExist != null && _interlockDelayTime != null && _interlockNoneOrExist.BoolValue) { var timeParas = _interlockDelayTime.StringValue.Split(':');//00:00.0 _interlockDelayTimeInMS = 0; if (timeParas.Length > 1) { float.TryParse(timeParas[0], out float min); float.TryParse(timeParas[1], out float sec); _interlockDelayTimeInMS = min * 60 * 1000 + sec * 1000; } if (_interlockDelayTimeInMS > 0) { _writeLog += $" delay time={_interlockDelayTimeInMS} ms"; _interlockDelayTimer.Restart(); _operation = isOn; _timer.Start(2000 + _interlockDelayTimeInMS); //2 seconds to monitor } else { SetPoint = isOn; _operation = isOn; _timer.Start(2000); //2 seconds to monitor } } else { SetPoint = isOn; _operation = isOn; _timer.Start(2000); //2 seconds to monitor } return true; } public void Reset() { eventTrigger.RST = true; } } }