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; public bool IsILKOK = true; [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, IsILKOK = this.IsILKOK, 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; public bool IsILKNot { get; set; } = false; 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 _interlockDelayOnTime; private SCConfigItem _interlockDelayOffTime; private SCConfigItem _interlockILKTime; private float _interlockDelayTimeInMS; private float _ilkDelayTimeInMS; private Stopwatch _interlockDelayTimer = new Stopwatch(); private Stopwatch _ilkDelayTimer = 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); var attIsILKNot = node.GetAttribute("IsILKNot"); IsILKNot = string.IsNullOrEmpty(attIsILKNot) ? false : Convert.ToBoolean(attIsILKNot); _uniqueName = $"{Module}.{Name}"; InterlockAlarm= SubscribeAlarm(new AlarmEventItem() { EventEnum = $"{Module}.{Name} InterlockAlarm", Description = $"{Name} Interlock alarm", Solution = "No information available. Press[Clear] to delete alarm message.", Explaination = "No information available.", AutoRecovery = false, Level = EventLevel.Alarm, Action = EventAction.Clear, Category = "TubeAlarm", }, () => { Reset(); return true; }); } 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"); _interlockDelayOnTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayOnTime"); _interlockDelayOffTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.DelayOffTime"); _interlockILKTime = SC.GetConfigItem($"PM1.RecipeEditParameter.InterLock.{valveIndex}.ILKTime"); 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; } private bool _openOperation; public void Monitor() { if (!string.IsNullOrEmpty(_writeLog)) { LOG.Write(_writeLog); _writeLog = ""; } if (_interlockDelayTimer.IsRunning) { if (_interlockDelayTimer.ElapsedMilliseconds >= _interlockDelayTimeInMS) { _interlockDelayTimer.Stop(); _interlockDelayTimer.Reset(); bool bValue = _operation; _openOperation = _operation; if (_doOpen != null) { if (!_doOpen.Check(bValue, out var reason)) { if (!_doOpen.SetValue(bValue, out reason, false)) { EV.PostWarningLog("System", $"interlock set DO {Module} to {bValue}:{reason}, {reason}"); } // EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(_operation ? "Open" : "Close")}", ":Failed for interlock " + reason); _operation = SetPoint; //还原状态 防止定时检查状态再次写日志 return; } } if (_doClose != null) { if (!_doClose.Check(bValue, out var reason)) { if (!_doClose.SetValue(bValue, out reason, false)) { EV.PostWarningLog("System", $"interlock set DO {Module} to {bValue}:{reason}, {reason}"); } //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(_operation ? "Open" : "Close")}", ":Failed for interlock " + reason); ; _operation = SetPoint;//还原状态 防止定时检查状态再次写日志 return; } } SetPoint = _isNc ? _operation : !_operation; } 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); } if (ILKDiSensor == null) { IsILKOK = true; } else { if (!_isNc) { IsILKOK = true; return; } if (ILKDiSensor.Value) { IsILKOK = true; } else if (!ILKDiSensor.Value) { if (!IsILKNot) { IsILKOK = false; } } } //ILK判断 if (_ilkDelayTimer.IsRunning) { if (_ilkDelayTimer.ElapsedMilliseconds >= _ilkDelayTimeInMS) { _ilkDelayTimer.Stop(); _ilkDelayTimer.Reset(); if (_doOpen != null) { if (!ILKDiSensor.Value) { InterlockAlarm.Set($"interlock delay ends. The value of the {Name} valve is incorrect."); // EV.PostWarningLog(Module, $"interlock delay ends. The value of the {Name} valve is incorrect."); return; } } } else { return; } } } public bool TurnValve(bool isOn, out string reason) { bool bValue = _isNc ? isOn : !isOn; var stringOnOff = isOn ? "On" : "Off"; //EV.PostInfoLog(Module, $"Turn {Display} {stringOnOff}"); _writeLog = $"Turn {Display} {stringOnOff}"; reason = ""; _interlockDelayTimeInMS = 0; _ilkDelayTimeInMS = 0; //开始计时ILK if (_interlockNoneOrExist != null && _interlockILKTime != null && _interlockNoneOrExist.BoolValue && bValue) { var timeParas = _interlockILKTime.StringValue.Split(':');//00:00.0 if (timeParas.Length > 1) { float.TryParse(timeParas[0], out float min); float.TryParse(timeParas[1], out float sec); _ilkDelayTimeInMS = min * 60 * 1000 + sec * 1000; } _ilkDelayTimer.Restart(); _operation = bValue; if (_ilkDelayTimeInMS > 0) { _writeLog += $"ilk delay time={_ilkDelayTimeInMS} ms"; _timer.Start(2000 + _ilkDelayTimeInMS); } } if (_interlockNoneOrExist != null && _interlockDelayOnTime != null && _interlockNoneOrExist.BoolValue && bValue) { var timeParas = _interlockDelayOnTime.StringValue.Split(':');//00:00.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; } _interlockDelayTimer.Restart(); _operation = bValue; if (_interlockDelayTimeInMS > 0) { _writeLog += $" delay time={_interlockDelayTimeInMS} ms"; _timer.Start(2000 + _interlockDelayTimeInMS); } else { _timer.Start(2000); //2 seconds to monitor } } else if (_interlockNoneOrExist != null && _interlockDelayOffTime != null && _interlockNoneOrExist.BoolValue && !bValue) { var timeParas = _interlockDelayOffTime.StringValue.Split(':');//00:00.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; } _interlockDelayTimer.Restart(); _operation = bValue; if (_interlockDelayTimeInMS > 0) { _writeLog += $" delay time={_interlockDelayTimeInMS} ms"; _timer.Start(2000 + _interlockDelayTimeInMS); } else { _timer.Start(2000); //2 seconds to monitor } } else { if (_doOpen != null) { if (!_doOpen.Check(bValue, out reason)) { if (!_isNc && !IsILKNot) { IsILKOK = false; } _doOpen.SetValue(bValue, out reason, false); //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)) { _doClose.SetValue(bValue, out reason, false); //EV.PostMessage(Module, EventEnum.ValveOperationFail, Module, Display, $"{(isOn ? "Open" : "Close")}", ":Failed for interlock " + reason); ; return false; } } SetPoint = isOn; _operation = isOn; _timer.Start(2000); //2 seconds to monitor } return true; } public void Reset() { eventTrigger.RST = true; } } }