using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Windows; using System.Xml; using Aitex.Core.RT.DataCenter; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using MECF.Framework.Common.Equipment; using MECF.Framework.Common.Event; namespace Aitex.Core.RT.IOCore { public class InterlockManager : Singleton, IAlarmHandler { private List _actions = new List(); private Dictionary> _dicLimit = new Dictionary>();//and condition private Dictionary> _dicActionOrLimit = new Dictionary>(); //private Dictionary> _dicUserDefineLimit = new Dictionary>();//and condition //private Dictionary> _dicUserDefineActionOrLimit = new Dictionary>(); private string _interlockFile; #region userDefineInterlock private List _userDefineActions = new List(); private List _userDefineFlagActions = new List(); private Dictionary _userDefineFlagCurrentValues = new Dictionary(); private Dictionary _userDefineKeepTimer = new Dictionary(); private Dictionary _userDefineDelayTimer = new Dictionary(); private Dictionary _userDefineConditionTrig = new Dictionary(); private string _interlockUserDefineFile; private object _locker = new object(); public event Func UserDefineInterlockHandler; public event Func UserDefineInterlocks; #endregion public string InterlockAlarmKey { get; set; } public string UserDefineInterlockAlarmKey { get; set; } public Dictionary InterlockAlarms { get; set; } = new Dictionary(); public event Action OnDeviceAlarmStateChanged; private DeviceTimer _userDefineActionTimer = new DeviceTimer(); public bool Initialize(string interlockFile, Dictionary doMap, Dictionary diMap, out string reason) { reason = string.Empty; try { _interlockFile = interlockFile; XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(_interlockFile); // // // // //InterlockAlarms = new Dictionary(); XmlNode nodeInterlock = xmlConfig.SelectSingleNode("Interlock"); foreach (XmlNode item in nodeInterlock.ChildNodes) { if (item.NodeType == XmlNodeType.Comment) continue; XmlElement actionNode = item as XmlElement; if (actionNode == null) continue; if (actionNode.Name != "Action") { if (actionNode.NodeType != XmlNodeType.Comment) { LOG.Write("interlock config file contains no comments content, " + actionNode.InnerXml); } continue; } if (!actionNode.HasAttribute("do") || !actionNode.HasAttribute("value")) { reason += "action node has no [do] or [value] attribute \r\n"; continue; } string doName = actionNode.GetAttribute("do"); bool value = Convert.ToBoolean(actionNode.GetAttribute("value")); string tip = string.Empty; Dictionary cultureTip = new Dictionary(); List limits = new List(); bool isAndCondition = true; bool isReverse = true; if (actionNode.HasAttribute("reverse")) isReverse = Convert.ToBoolean(actionNode.GetAttribute("reverse")); if (!doMap.ContainsKey(doName)) { reason += "action node " + doName + " no such DO defined \r\n"; continue; } DOAccessor doItem = doMap[doName] as DOAccessor; if (doItem == null) { reason += "action node " + doName + " no such DO defined \r\n"; continue; } if (actionNode.HasAttribute("tip")) tip = actionNode.GetAttribute("tip"); else tip = doName; if (actionNode.HasAttribute("tip.zh-CN")) cultureTip["zh-CN"] = actionNode.GetAttribute("tip.zh-CN"); if (actionNode.HasAttribute("tip.en-US")) cultureTip["en-US"] = actionNode.GetAttribute("tip.en-US"); if (actionNode.HasAttribute("condition")) isAndCondition = actionNode.GetAttribute("condition").ToLower() == "and"; foreach (XmlElement limitNode in actionNode.ChildNodes) { if (limitNode.Name != "Limit") { if (limitNode.NodeType != XmlNodeType.Comment) { LOG.Write("interlock config file contains no comments content, " + limitNode.InnerXml); } continue; } if (!(limitNode.HasAttribute("di") || limitNode.HasAttribute("do") || limitNode.HasAttribute("poll")) || !limitNode.HasAttribute("value")) { reason += "limit node lack of di/do or value attribute \r\n"; continue; } string stringValue = limitNode.GetAttribute("value"); if (stringValue.Contains("*")) { continue; } string limitValueS = limitNode.GetAttribute("value"); string limitTip = string.Empty; string condition = "AND"; Dictionary limitCultureTip = new Dictionary(); if (limitNode.HasAttribute("tip")) limitTip = limitNode.GetAttribute("tip"); if (limitNode.HasAttribute("tip.zh-CN")) limitCultureTip["zh-CN"] = limitNode.GetAttribute("tip.zh-CN"); if (limitNode.HasAttribute("tip.en-US")) limitCultureTip["en-US"] = limitNode.GetAttribute("tip.en-US"); if (limitNode.HasAttribute("condition")) condition = limitNode.GetAttribute("condition"); if (limitNode.HasAttribute("di")) { string diName = limitNode.GetAttribute("di"); if (string.IsNullOrEmpty(limitTip)) limitTip = diName; if (!diMap.ContainsKey(diName)) { reason += "limit node " + diName + " no such DI defined \r\n"; continue; } DIAccessor diItem = diMap[diName] as DIAccessor; if (diItem == null) { reason += "limit node " + diName + " no such DI defined \r\n"; continue; } bool.TryParse(limitValueS, out bool limitValue); limits.Add(new DiLimit(diItem, limitValue, limitTip, limitCultureTip, condition)); } else if (limitNode.HasAttribute("do")) { string ioName = limitNode.GetAttribute("do"); if (string.IsNullOrEmpty(limitTip)) limitTip = ioName; if (!doMap.ContainsKey(ioName)) { reason += "limit node " + ioName + " no such DO defined \r\n"; continue; } DOAccessor ioItem = doMap[ioName]; if (ioItem == null) { reason += "limit node " + ioName + " no such DO defined \r\n"; continue; } bool.TryParse(limitValueS, out bool limitValue); limits.Add(new DoLimit(ioItem, limitValue, limitTip, limitCultureTip, condition)); } else if (limitNode.HasAttribute("poll")) { string name = limitNode.GetAttribute("poll"); if (string.IsNullOrEmpty(limitTip)) limitTip = name; if (DATA.Poll(name) == null) { reason += "limit node " + name + " no such poll defined \r\n"; continue; } limits.Add(new DataPollLimit(name, limitValueS, limitNode.GetAttribute("condition"))); } } InterlockAction action = new InterlockAction(doItem, value, tip, cultureTip, limits, isReverse); _actions.Add(action); if (isAndCondition) { foreach (var interlockLimit in limits) { bool isExist = false; foreach (var limit in _dicLimit.Keys) { if (interlockLimit.IsSame(limit)) { _dicLimit[limit].Add(action); isExist = true; break; } } if (!isExist) { _dicLimit[interlockLimit] = new List(); _dicLimit[interlockLimit].Add(action); } } } else { if (!_dicActionOrLimit.ContainsKey(action)) { _dicActionOrLimit.Add(action, limits); } } } } catch (Exception ex) { reason += ex.Message; } if (!string.IsNullOrEmpty(reason)) return false; return true; } public bool InitializeUserDefine(string interlockUserDefineFile, Dictionary doMap, Dictionary diMap, Dictionary aiMap, Dictionary aoMap, out string reason) { reason = string.Empty; try { _interlockUserDefineFile = interlockUserDefineFile; XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(_interlockUserDefineFile); // // // // //InterlockAlarms = new Dictionary(); XmlNode nodeInterlock = xmlConfig.SelectSingleNode("Interlock"); bool interlockParserEnd = false; //bool interlockParserMark = false; foreach (XmlNode item in nodeInterlock.ChildNodes) { if (interlockParserEnd) continue; if (item.NodeType == XmlNodeType.Comment) continue; XmlElement actionNode = item as XmlElement; if (actionNode == null) continue; if (actionNode.Name != "Action") { if (actionNode.NodeType != XmlNodeType.Comment) { LOG.Write($"interlock config file contains no comments content, {actionNode.InnerXml}"); } continue; } if (!actionNode.HasAttribute("Name") || !actionNode.HasAttribute("Value")) { LOG.Write($"action node has no [Name] or [Value] attribute, {actionNode.InnerXml}"); continue; } string actionName = actionNode.GetAttribute("Name"); string value = actionNode.GetAttribute("Value"); if (string.IsNullOrEmpty(value) || (!value.ToUpper().Contains("ON")) && !value.ToUpper().Contains("OFF") && !value.ToUpper().Contains("OPEN") && !value.ToUpper().Contains("CLOSE")) { LOG.Write($"action node has no [Name] or [Value] attribute, {actionNode.InnerXml}"); continue; } string tip = string.Empty; List limits = new List(); bool isAndCondition = true; if (UserDefineInterlocks != null && UserDefineInterlocks(actionName)) { //do nothing } else if (!doMap.ContainsKey(actionName) && !actionName.ToUpper().StartsWith("FLAG")) { reason += "action node " + actionName + " no such DO defined \r\n"; continue; } if (UserDefineInterlocks != null && !UserDefineInterlocks(actionName) && !actionName.ToUpper().StartsWith("FLAG")) { DOAccessor doItem; doItem = doMap[actionName] as DOAccessor; if (doItem == null) { reason += "action node " + actionName + " no such DO defined \r\n"; continue; } } if (actionNode.HasAttribute("condition")) isAndCondition = actionNode.GetAttribute("condition").ToLower() == "and"; bool isLimitInvalid = false; foreach (XmlElement limitNode in actionNode.ChildNodes) { if (limitNode.Name != "Limit") { if (limitNode.NodeType != XmlNodeType.Comment) { LOG.Write("interlock config file contains no comments content, " + limitNode.InnerXml); } isLimitInvalid = true; break; } if (!(limitNode.HasAttribute("Name") || limitNode.HasAttribute("Value")) || !limitNode.HasAttribute("Condition")) { LOG.Write("limit node lack of Name/Condition or Value attribute, " + limitNode.InnerXml); isLimitInvalid = true; break; } if (limitNode.GetAttribute("Condition").ToUpper() != "AND" && limitNode.GetAttribute("Condition").ToUpper() != "OR" && limitNode.GetAttribute("Condition").ToUpper() != "EXOR" && limitNode.GetAttribute("Condition").ToUpper() != "M-START" && limitNode.GetAttribute("Condition").ToUpper() != "M-END" && limitNode.GetAttribute("Condition").ToUpper() != "END" && limitNode.GetAttribute("Condition").ToUpper() != "BLANK") { LOG.Write("limit Condition attribute is invalid, " + limitNode.InnerXml); isLimitInvalid = true; break; } if (limitNode.GetAttribute("Condition").ToUpper() == "BLANK") continue; if (limitNode.GetAttribute("Condition").ToUpper() == "END") { interlockParserEnd = true; break; } //if (limitNode.GetAttribute("Condition").ToUpper() == "M-START") //{ // interlockParserMark = true; // continue; //} //if (limitNode.GetAttribute("Condition").ToUpper() == "M-END") //{ // interlockParserMark = false; //} //if (interlockParserMark) // continue; string stringValue = limitNode.GetAttribute("Value"); if (stringValue.Contains("*")) { LOG.Write($"limit Value attribute is invalid, {limitNode.InnerXml}"); isLimitInvalid = true; break; } string limitValue = limitNode.GetAttribute("Value"); string limitTip = string.Empty; Dictionary limitCultureTip = new Dictionary(); if (limitNode.HasAttribute("Name")) { string Name = limitNode.GetAttribute("Name"); if (Name.ToUpper().StartsWith("FLAG")) { limits.Add(new UserDefineLimit(Name, limitValue.ToUpper().Contains("ON") ? true : false, limitNode.GetAttribute("Condition"))); continue; } if (Name.Split('.')[1].ToLower().StartsWith("di")) { if (!diMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such DI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } DIAccessor diItem = diMap[Name] as DIAccessor; if (diItem == null) { LOG.Write($"limit node {Name} no such DI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } //limits.Add(new DiLimit(diItem, Convert.ToBoolean(limitValue), limitNode.GetAttribute("Condition"))); limits.Add(new DiLimit(diItem, limitValue.ToUpper().Contains("ON") ? true : false, limitNode.GetAttribute("Condition"))); } else if (Name.Split('.')[1].ToLower().StartsWith("do")) { if (!doMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such DO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } DOAccessor ioItem = doMap[Name]; if (ioItem == null) { LOG.Write($"limit node {Name} no such DO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } //limits.Add(new DoLimit(ioItem, Convert.ToBoolean(limitValue), limitNode.GetAttribute("Condition"))); limits.Add(new DoLimit(ioItem, limitValue.ToUpper().Contains("ON") ? true : false, limitNode.GetAttribute("Condition"))); } else if (Name.Split('.')[1].ToLower().StartsWith("ao")) { if (!aoMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such AO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } AOAccessor ioItem = aoMap[Name]; if (ioItem == null) { LOG.Write($"limit node {Name} no such AO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } limits.Add(new AoLimit(ioItem, limitValue, limitNode.GetAttribute("Condition"))); } else if (Name.Split('.')[1].ToLower().StartsWith("ai")) { if (!aiMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such AI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } AIAccessor ioItem = aiMap[Name]; if (ioItem == null) { LOG.Write($"limit node {Name} no such AI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } limits.Add(new AiLimit(ioItem, limitValue, limitNode.GetAttribute("Condition"))); } else { if (DATA.Poll(Name) == null) { LOG.Write($"limit node {Name} no such DATA.Poll defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } limits.Add(new DataPollLimit(Name, limitValue, limitNode.GetAttribute("Condition"))); } } } if (isLimitInvalid) continue; if (limits.Count == 0) continue; var paras = value.Split(';');//KEEP30;ON;Auto string condition = ""; float conditionTime = 0.0f; bool actionValue; bool actionAuto = true; if (paras.Length > 2) { if (paras[0].ToUpper().Contains("KEEP")) { condition = "KEEP"; float.TryParse(paras[0].ToUpper().Replace("KEEP", ""), out conditionTime); } if (paras[0].ToUpper().Contains("DELAY")) { condition = "DELAY"; float.TryParse(paras[0].ToUpper().Replace("DELAY", ""), out conditionTime); } actionValue = paras[1].ToUpper().Contains("ON") || paras[1].ToUpper() == "OPEN" ? true : false; actionAuto = paras[2].ToUpper().Contains("AUTO") ? true : false; } else if (paras.Length == 2) { if (value.ToUpper().Contains("KEEP") || value.ToUpper().Contains("DELAY")) { if (paras[0].ToUpper().Contains("KEEP")) { condition = "KEEP"; float.TryParse(paras[0].ToUpper().Replace("KEEP", ""), out conditionTime); } if (paras[0].ToUpper().Contains("DELAY")) { condition = "DELAY"; float.TryParse(paras[0].ToUpper().Replace("DELAY", ""), out conditionTime); } actionValue = paras[1].ToUpper().Contains("ON") || paras[1].ToUpper() == "OPEN" ? true : false; } else { actionValue = paras[0].ToUpper().Contains("ON") || paras[1].ToUpper() == "OPEN" ? true : false; actionAuto = paras[1].ToUpper().Contains("AUTO") ? true : false; } } else { actionValue = value.ToUpper().Contains("ON") || value.ToUpper() == "OPEN" ? true : false; } if (actionName.ToUpper().StartsWith("FLAG")) { InterlockAction action = new InterlockAction(actionName, actionValue, actionNode.OuterXml, condition, conditionTime, limits, actionAuto); _userDefineFlagActions.Add(action); if (!_userDefineFlagCurrentValues.ContainsKey(actionName.ToUpper())) _userDefineFlagCurrentValues.Add(actionName.ToUpper(), false); } else if (UserDefineInterlocks != null && UserDefineInterlocks(actionName)) { InterlockAction action = new InterlockAction(actionName, actionValue, actionNode.OuterXml, condition, conditionTime, limits, actionAuto); _userDefineActions.Add(action); } else { DOAccessor doItem; doItem = doMap[actionName] as DOAccessor; InterlockAction action = new InterlockAction(doItem, actionValue, actionNode.OuterXml, condition, conditionTime, limits, actionAuto); _userDefineActions.Add(action); } } } catch (Exception ex) { reason += ex.Message; } if (!string.IsNullOrEmpty(reason)) return false; _userDefineActionTimer.Start(3000); return true; } public void Monitor() { //AND condition foreach (var limit in _dicLimit) { if (SC.ContainsItem("System.BypassInterlock") && SC.GetValue("System.BypassInterlock")) continue; if (!limit.Key.IsTriggered()) continue; string info = string.Empty; string module = "System"; InterlockAction actionTemp = null; foreach (var action in limit.Value) { string reason = ""; if (action.IsReverse && !action.Reverse(out reason)) continue; if (!action.IsReverse && action.ActionDo.Value != action.ActionValue) continue; if (string.IsNullOrEmpty(info)) { info = string.Format("Due to the {0}, {1}=[{2}]\n", limit.Key.Tip, limit.Key.Name, !limit.Key.LimitValue); } var nameArray = action.ActionName.Split('.'); if (nameArray != null && nameArray.Length > 1) { switch (nameArray[0].ToUpper()) { case "SPN1": module = "PM2_1"; break; case "SPN2": module = "PM2_2"; break; case "SPN3": module = "PM2_3"; break; case "SPN4": module = "PM2_4"; break; case "SPN5": module = "PM3_1"; break; case "SPN6": module = "PM3_2"; break; case "SPN7": module = "PM3_3"; break; case "SPN8": module = "PM3_4"; break; default: if (nameArray[0].ToUpper() != "MF" && ModuleHelper.IsPm(nameArray[0])) module = nameArray[0]; break; } } info += reason + "\n"; actionTemp = action; } if (!string.IsNullOrEmpty(info) && actionTemp != null) { if (!InterlockAlarms.ContainsKey(actionTemp.ActionName) && !InterlockAlarms.ContainsKey(InterlockAlarmKey)) { LOG.Write(info.TrimEnd('\n')); } else { if (InterlockAlarms.ContainsKey(actionTemp.ActionName)) { InterlockAlarms[actionTemp.ActionName].Set(info.TrimEnd('\n')); } else if (InterlockAlarms.ContainsKey(InterlockAlarmKey)) { InterlockAlarms[InterlockAlarmKey].Set(info.TrimEnd('\n')); } } } } //OR condition foreach (var action in _dicActionOrLimit.Keys) { if (SC.ContainsItem("System.BypassInterlock") && SC.GetValue("System.BypassInterlock")) continue; var limits = _dicActionOrLimit[action]; if (limits.Any(x => x.IsTriggered())) { string info = string.Empty; string reason = string.Empty; string module = "System"; InterlockAction actionTemp = null; if (!limits.Any(x => x.CanDo(out info))) { if (!action.Reverse(out reason)) continue; foreach (var interlockLimit in limits) { if (!interlockLimit.CanDo(out info)) { if (reason.Length > 0) reason += "\n"; info = string.Format("Due to the {0}, {1}=[{2}]", interlockLimit.Tip, interlockLimit.Name, !interlockLimit.LimitValue); reason += info; actionTemp = action; } } } if (!string.IsNullOrEmpty(info) && actionTemp != null) { if (!InterlockAlarms.ContainsKey(actionTemp.ActionName) && !InterlockAlarms.ContainsKey(InterlockAlarmKey)) { LOG.Write(info.TrimEnd('\n')); } else { if (InterlockAlarms.ContainsKey(actionTemp.ActionName)) { InterlockAlarms[actionTemp.ActionName].Set(info.TrimEnd('\n')); } else if (InterlockAlarms.ContainsKey(InterlockAlarmKey)) { InterlockAlarms[InterlockAlarmKey].Set(info.TrimEnd('\n')); } } } } } if (_userDefineActionTimer.IsTimeout()) MonitorUserDefineInterlock(); } public bool CanSetDo(string doName, bool onOff, out string reason) { reason = string.Empty; try { if (SC.ContainsItem("System.BypassInterlock") && SC.GetValue("System.BypassInterlock")) return true; bool canSetDo = true; foreach (var interlockAction in _actions.ToArray()) { if (interlockAction.IsSame(doName, onOff)) { canSetDo = interlockAction.CanDo(out reason); break; } } if (!canSetDo) { if (InterlockAlarms.ContainsKey(InterlockAlarmKey)) { InterlockAlarms[InterlockAlarmKey].Set(reason); } return canSetDo; } foreach (var userDefineAction in _userDefineActions.ToArray()) { if (!userDefineAction.IsAuto && userDefineAction.IsSame(doName, onOff)) { canSetDo = GetUserDefineInterlockTrigResult(userDefineAction.Limits); if (!canSetDo) { reason = $"Interlock triggered, {doName} can not be {(onOff ? "ON" : "OFF")}"; if (InterlockAlarms.ContainsKey(UserDefineInterlockAlarmKey)) { InterlockAlarms[UserDefineInterlockAlarmKey].Set(reason); } } break; } } return canSetDo; } catch (Exception ex) { reason = ex.Message; LOG.Write($"{ex.Message}"); return false; } } public string GetInterlockConfigContent() { XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(_interlockFile); return xmlConfig.InnerXml; } public string GetInterlockUserDefineConfigContent() { XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(_interlockUserDefineFile); return xmlConfig.InnerXml; } public void SetInterlockConfigContent(string content) { if (File.Exists(_interlockUserDefineFile)) { XmlDocument xmlData = new XmlDocument(); xmlData.LoadXml(content); XmlTextWriter writer = new XmlTextWriter(_interlockUserDefineFile, Encoding.UTF8); writer.Formatting = Formatting.Indented; xmlData.Save(writer); writer.Close(); } } public bool UpdateUserDefine(Dictionary doMap, Dictionary diMap, Dictionary aiMap, Dictionary aoMap, out string reason) { reason = string.Empty; try { XmlDocument xmlConfig = new XmlDocument(); xmlConfig.Load(_interlockUserDefineFile); lock (_locker) { _userDefineActions.Clear(); _userDefineFlagActions.Clear(); _userDefineFlagCurrentValues.Clear(); } // // // // //InterlockAlarms = new Dictionary(); XmlNode nodeInterlock = xmlConfig.SelectSingleNode("Interlock"); bool interlockParserEnd = false; //bool interlockParserMark = false; foreach (XmlNode item in nodeInterlock.ChildNodes) { if (interlockParserEnd) continue; if (item.NodeType == XmlNodeType.Comment) continue; XmlElement actionNode = item as XmlElement; if (actionNode == null) continue; if (actionNode.Name != "Action") { if (actionNode.NodeType != XmlNodeType.Comment) { LOG.Write($"interlock config file contains no comments content, {actionNode.InnerXml}"); } continue; } if (!actionNode.HasAttribute("Name") || !actionNode.HasAttribute("Value")) { LOG.Write($"action node has no [Name] or [Value] attribute, {actionNode.InnerXml}"); continue; } string actionName = actionNode.GetAttribute("Name"); string value = actionNode.GetAttribute("Value"); if (string.IsNullOrEmpty(value) || (!value.ToUpper().Contains("ON")) && !value.ToUpper().Contains("OFF") && !value.ToUpper().Contains("OPEN") && !value.ToUpper().Contains("CLOSE")) { LOG.Write($"action node has no [Name] or [Value] attribute, {actionNode.InnerXml}"); continue; } string tip = string.Empty; List limits = new List(); bool isAndCondition = true; if (UserDefineInterlocks != null && UserDefineInterlocks(actionName)) { //do nothing } else if (!doMap.ContainsKey(actionName) && !actionName.ToUpper().StartsWith("FLAG")) { reason += "action node " + actionName + " no such DO defined \r\n"; continue; } if (UserDefineInterlocks != null && !UserDefineInterlocks(actionName) && !actionName.ToUpper().StartsWith("FLAG")) { DOAccessor doItem; doItem = doMap[actionName] as DOAccessor; if (doItem == null) { reason += "action node " + actionName + " no such DO defined \r\n"; continue; } } if (actionNode.HasAttribute("condition")) isAndCondition = actionNode.GetAttribute("condition").ToLower() == "and"; bool isLimitInvalid = false; foreach (XmlElement limitNode in actionNode.ChildNodes) { if (limitNode.Name != "Limit") { if (limitNode.NodeType != XmlNodeType.Comment) { LOG.Write("interlock config file contains no comments content, " + limitNode.InnerXml); } isLimitInvalid = true; break; } if (!(limitNode.HasAttribute("Name") || limitNode.HasAttribute("Value")) || !limitNode.HasAttribute("Condition")) { LOG.Write("limit node lack of Name/Condition or Value attribute, " + limitNode.InnerXml); isLimitInvalid = true; break; } if (limitNode.GetAttribute("Condition").ToUpper() != "AND" && limitNode.GetAttribute("Condition").ToUpper() != "OR" && limitNode.GetAttribute("Condition").ToUpper() != "EXOR" && limitNode.GetAttribute("Condition").ToUpper() != "M-START" && limitNode.GetAttribute("Condition").ToUpper() != "M-END" && limitNode.GetAttribute("Condition").ToUpper() != "END" && limitNode.GetAttribute("Condition").ToUpper() != "BLANK") { LOG.Write("limit Condition attribute is invalid, " + limitNode.InnerXml); isLimitInvalid = true; break; } if (limitNode.GetAttribute("Condition").ToUpper() == "BLANK") continue; if (limitNode.GetAttribute("Condition").ToUpper() == "END") { interlockParserEnd = true; break; } //if (limitNode.GetAttribute("Condition").ToUpper() == "M-START") //{ // interlockParserMark = true; // continue; //} //if (limitNode.GetAttribute("Condition").ToUpper() == "M-END") //{ // interlockParserMark = false; //} //if (interlockParserMark) // continue; string stringValue = limitNode.GetAttribute("Value"); if (stringValue.Contains("*")) { LOG.Write($"limit Value attribute is invalid, {limitNode.InnerXml}"); isLimitInvalid = true; break; } string limitValue = limitNode.GetAttribute("Value"); string limitTip = string.Empty; Dictionary limitCultureTip = new Dictionary(); if (limitNode.HasAttribute("Name")) { string Name = limitNode.GetAttribute("Name"); if (Name.ToUpper().StartsWith("FLAG")) { limits.Add(new UserDefineLimit(Name, limitValue.ToUpper().Contains("ON") ? true : false, limitNode.GetAttribute("Condition"))); continue; } if (Name.Split('.')[1].ToLower().StartsWith("di")) { if (!diMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such DI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } DIAccessor diItem = diMap[Name] as DIAccessor; if (diItem == null) { LOG.Write($"limit node {Name} no such DI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } //limits.Add(new DiLimit(diItem, Convert.ToBoolean(limitValue), limitNode.GetAttribute("Condition"))); limits.Add(new DiLimit(diItem, limitValue.ToUpper().Contains("ON") ? true : false, limitNode.GetAttribute("Condition"))); } else if (Name.Split('.')[1].ToLower().StartsWith("do")) { if (!doMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such DO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } DOAccessor ioItem = doMap[Name]; if (ioItem == null) { LOG.Write($"limit node {Name} no such DO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } //limits.Add(new DoLimit(ioItem, Convert.ToBoolean(limitValue), limitNode.GetAttribute("Condition"))); limits.Add(new DoLimit(ioItem, limitValue.ToUpper().Contains("ON") ? true : false, limitNode.GetAttribute("Condition"))); } else if (Name.Split('.')[1].ToLower().StartsWith("ao")) { if (!aoMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such AO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } AOAccessor ioItem = aoMap[Name]; if (ioItem == null) { LOG.Write($"limit node {Name} no such AO defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } limits.Add(new AoLimit(ioItem, limitValue, limitNode.GetAttribute("Condition"))); } else if (Name.Split('.')[1].ToLower().StartsWith("ai")) { if (!aiMap.ContainsKey(Name)) { LOG.Write($"limit node {Name} no such AI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } AIAccessor ioItem = aiMap[Name]; if (ioItem == null) { LOG.Write($"limit node {Name} no such AI defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } limits.Add(new AiLimit(ioItem, limitValue, limitNode.GetAttribute("Condition"))); } else { if (DATA.Poll(Name) == null) { LOG.Write($"limit node {Name} no such DATA.Poll defined, {limitNode.InnerXml}"); isLimitInvalid = true; break; } limits.Add(new DataPollLimit(Name, limitValue, limitNode.GetAttribute("Condition"))); } } } if (isLimitInvalid) continue; if (limits.Count == 0) continue; var paras = value.Split(';');//KEEP30;ON string condition = ""; float conditionTime = 0.0f; bool actionValue; bool actionAuto = true; if (paras.Length > 2) { if (paras[0].ToUpper().Contains("KEEP")) { condition = "KEEP"; float.TryParse(paras[0].ToUpper().Replace("KEEP", ""), out conditionTime); } if (paras[0].ToUpper().Contains("DELAY")) { condition = "DELAY"; float.TryParse(paras[0].ToUpper().Replace("DELAY", ""), out conditionTime); } actionValue = paras[1].ToUpper().Contains("ON") || paras[1].ToUpper() == "OPEN" ? true : false; actionAuto = paras[2].ToUpper().Contains("AUTO") ? true : false; } else if (paras.Length == 2) { if (value.ToUpper().Contains("KEEP") || value.ToUpper().Contains("DELAY")) { if (paras[0].ToUpper().Contains("KEEP")) { condition = "KEEP"; float.TryParse(paras[0].ToUpper().Replace("KEEP", ""), out conditionTime); } if (paras[0].ToUpper().Contains("DELAY")) { condition = "DELAY"; float.TryParse(paras[0].ToUpper().Replace("DELAY", ""), out conditionTime); } actionValue = paras[1].ToUpper().Contains("ON") || paras[1].ToUpper() == "OPEN" ? true : false; } else { actionValue = paras[0].ToUpper().Contains("ON") || paras[1].ToUpper() == "OPEN" ? true : false; actionAuto = paras[1].ToUpper().Contains("AUTO") ? true : false; } } else { actionValue = value.ToUpper().Contains("ON") || value.ToUpper() == "OPEN" ? true : false; } if (actionName.ToUpper().StartsWith("FLAG")) { InterlockAction action = new InterlockAction(actionName, actionValue, actionNode.OuterXml, condition, conditionTime, limits, actionAuto); lock (_locker) { _userDefineFlagActions.Add(action); if (!_userDefineFlagCurrentValues.ContainsKey(actionName.ToUpper())) _userDefineFlagCurrentValues.Add(actionName.ToUpper(), false); } } else if (UserDefineInterlocks != null && UserDefineInterlocks(actionName)) { InterlockAction action = new InterlockAction(actionName, actionValue, actionNode.OuterXml, condition, conditionTime, limits, actionAuto); _userDefineActions.Add(action); } else { DOAccessor doItem; doItem = doMap[actionName] as DOAccessor; InterlockAction action = new InterlockAction(doItem, actionValue, actionNode.OuterXml, condition, conditionTime, limits); lock (_locker) { _userDefineActions.Add(action); } } } } catch (Exception ex) { reason += ex.Message; } if (!string.IsNullOrEmpty(reason)) return false; return true; } private AlarmEventItem SubscribeAlarm(string name, string description, Func resetChecker, EventLevel level = EventLevel.Alarm) { AlarmEventItem item = new AlarmEventItem(name.Split('.')[0], name, description, resetChecker, this); item.Level = level; EV.Subscribe(item); return item; } public void AlarmStateChanged(AlarmEventItem args) { if (OnDeviceAlarmStateChanged != null) { OnDeviceAlarmStateChanged($"{args.Id}{args.Description}", args); } } private void MonitorUserDefineInterlock() { if (SC.ContainsItem("System.BypassInterlock") && SC.GetValue("System.BypassInterlock")) return; if (_userDefineFlagActions.Count != 0) { foreach (var action in _userDefineFlagActions) { if (action == null || action.Limits.Count == 0) continue; SetUserDefineActionValue(action, _userDefineFlagActions); } } if (_userDefineActions.Count > 0) { foreach (var action in _userDefineActions) { if (!action.IsAuto) continue; if (action == null || action.Limits.Count == 0) continue; SetUserDefineActionValue(action, _userDefineActions); } } } private bool GetUserDefineInterlockTrigResult(List limits) { bool isTrig = false; if (limits == null || limits.Count == 0) return false; if (limits.Count > 1) { for (int i = 0; i < limits.Count; i++) { bool currentValue = false; if (_userDefineFlagCurrentValues.ContainsKey(limits[i].Name.ToUpper())) currentValue = _userDefineFlagCurrentValues[limits[i].Name.ToUpper()]; else currentValue = limits[i].CurrentValue; bool currentCondition; if (limits[i].IsFloatType) { //AI,AO类型 currentCondition = currentValue; } else { //DI,DO类型 currentCondition = currentValue == limits[i].LimitValue; } if (i == 0) { isTrig = currentCondition; continue; } switch (limits[i].Condition.ToUpper()) { case "AND": isTrig = isTrig && currentCondition; break; case "OR": isTrig = isTrig || currentCondition; break; case "BLANK": break; } } } else { bool currentValue = false; if (_userDefineFlagCurrentValues.ContainsKey(limits[0].Name.ToUpper())) currentValue = _userDefineFlagCurrentValues[limits[0].Name.ToUpper()]; else currentValue = limits[0].CurrentValue; if (limits[0].IsFloatType) { //AI,AO类型 isTrig = currentValue; } else { //DI,DO类型 isTrig = currentValue == limits[0].LimitValue; } } return isTrig; } private void SetUserDefineActionValue(InterlockAction action, List actionLst) { var limits = action.Limits; bool isTrig = GetUserDefineInterlockTrigResult(action.Limits); var trigKey = $"{action.ActionName.ToUpper()}-{actionLst.IndexOf(action)}";//ActionName可能有多条 if (!_userDefineConditionTrig.ContainsKey(trigKey)) _userDefineConditionTrig.Add(trigKey, new RD_TRIG()); _userDefineConditionTrig[trigKey].CLK = isTrig; if (_userDefineConditionTrig[trigKey].R) { if (action.Condition.ToUpper() == "KEEP" && action.ConditionTime > 0) { if (!_userDefineKeepTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}")) _userDefineKeepTimer.Add($"{action.ActionName.ToUpper()}.{action.ActionValue}", new Stopwatch()); // 满足keep acition才计时keep,否则不keep if (_userDefineFlagCurrentValues.ContainsKey(action.ActionName.ToUpper())) { //FLAG类型的值 if (_userDefineFlagCurrentValues[action.ActionName.ToUpper()] == action.ActionValue) _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Restart(); } else { if (action.ActionValue == action.ActionDo.Value) { LOG.Write($"{action.ActionName} keep value to {action.ActionValue} for interlock trig : '{action.Tip}'"); //if (action.ActionDo.SetValue(action.ActionValue, out string reason)) // LOG.Write($"{action.ActionName} set value to {action.ActionValue} for interlock trig : '{action.Tip}'"); //else // LOG.Write($"{action.ActionName} set interlock trig '{action.Tip}' value to {action.ActionValue} failed for {reason}"); _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Restart(); } } } else if (action.Condition.ToUpper() == "DELAY" && action.ConditionTime > 0) { if (!_userDefineDelayTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}")) _userDefineDelayTimer.Add($"{action.ActionName.ToUpper()}.{action.ActionValue}", new Stopwatch()); _userDefineDelayTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Restart(); } else { if (_userDefineFlagCurrentValues.ContainsKey(action.ActionName.ToUpper())) { //FLAG类型的值 _userDefineFlagCurrentValues[action.ActionName.ToUpper()] = action.ActionValue; } else { if (UserDefineInterlockHandler != null && UserDefineInterlockHandler(action.ActionName, action.ActionValue)) LOG.Write($"{action.ActionName} set value to {action.ActionValue} for interlock trig : '{action.Tip}'"); else if (action.ActionDo.SetValue(action.ActionValue, out string reason)) LOG.Write($"{action.ActionName} set value to {action.ActionValue} for interlock trig : '{action.Tip}'"); else LOG.Write($"{action.ActionName} set interlock trig '{action.Tip}' value to {action.ActionValue} failed for {reason}"); } } } if (_userDefineConditionTrig[trigKey].T) { if (action.Condition.ToUpper() == "KEEP" && action.ConditionTime > 0) { if (!_userDefineKeepTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}")) _userDefineKeepTimer.Add($"{action.ActionName.ToUpper()}.{action.ActionValue}", new Stopwatch()); //trig条件在keep时间内不能保持不变,则要把定时器stop _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Stop(); } else if (action.Condition.ToUpper() == "DELAY" && action.ConditionTime > 0) { if (!_userDefineDelayTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}")) _userDefineDelayTimer.Add($"{action.ActionName.ToUpper()}.{action.ActionValue}", new Stopwatch()); //trig条件在delay时间内不能保持不变,则要把定时器stop _userDefineDelayTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Stop(); } } if (_userDefineConditionTrig[trigKey].M) { if (action.Condition.ToUpper() == "KEEP" && action.ConditionTime > 0) { if (!_userDefineKeepTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}")) _userDefineKeepTimer.Add($"{action.ActionName.ToUpper()}.{action.ActionValue}", new Stopwatch()); //在keep指定时间后,把定时器stop if (_userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].IsRunning && _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].ElapsedMilliseconds > action.ConditionTime * 1000) { _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Stop(); } else if (_userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].IsRunning) { //在keep时间内,被keep的状态变了,抛出alarm if (_userDefineFlagCurrentValues.ContainsKey(action.ActionName.ToUpper())) { if (_userDefineFlagCurrentValues[action.ActionName.ToUpper()] != action.ActionValue) { _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Stop(); if (InterlockAlarms.ContainsKey(UserDefineInterlockAlarmKey)) { InterlockAlarms[UserDefineInterlockAlarmKey].Set($"Due to the {action.ActionName} can not keep {action.ActionValue.ToString()} for {action.ConditionTime}s"); } } } else { if (action.ActionValue != action.ActionDo.Value) { _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Stop(); if (InterlockAlarms.ContainsKey(UserDefineInterlockAlarmKey)) { InterlockAlarms[UserDefineInterlockAlarmKey].Set($"Due to the {action.ActionName} can not keep {action.ActionValue.ToString()} for {action.ConditionTime}s"); } } } } } else if (action.Condition.ToUpper() == "DELAY" && action.ConditionTime > 0) { if (!_userDefineDelayTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}")) _userDefineDelayTimer.Add($"{action.ActionName.ToUpper()}.{action.ActionValue}", new Stopwatch()); if (_userDefineKeepTimer.ContainsKey($"{action.ActionName.ToUpper()}.{action.ActionValue}") && _userDefineKeepTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].IsRunning) { //还处于keep时间内,啥也不干 } else { //在delay指定时间后,把定时器stop,同时赋值 if (_userDefineDelayTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].IsRunning && _userDefineDelayTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].ElapsedMilliseconds > action.ConditionTime * 1000) { _userDefineDelayTimer[$"{action.ActionName.ToUpper()}.{action.ActionValue}"].Stop(); if (_userDefineFlagCurrentValues.ContainsKey(action.ActionName.ToUpper())) { //FLAG类型的值 _userDefineFlagCurrentValues[action.ActionName.ToUpper()] = action.ActionValue; } else { if (UserDefineInterlockHandler != null && UserDefineInterlockHandler(action.ActionName, action.ActionValue)) LOG.Write($"{action.ActionName} set value to {action.ActionValue} for interlock trig : '{action.Tip}'"); else if (action.ActionDo.SetValue(action.ActionValue, out string reason)) LOG.Write($"{action.ActionName} set value to {action.ActionValue} for interlock trig : '{action.Tip}'"); else LOG.Write($"{action.ActionName} set interlock trig '{action.Tip}' value to {action.ActionValue} failed for {reason}"); } } } } else { if (!_userDefineFlagCurrentValues.ContainsKey(action.ActionName.ToUpper())) { if (UserDefineInterlockHandler != null)//alarm reset,实际还是满足条件,需要继续报 UserDefineInterlockHandler(action.ActionName, action.ActionValue); } } } } } }