using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Xml; using Aitex.Core.RT.Event; using Aitex.Core.RT.IOCore.Interlock; using Aitex.Core.RT.IOCore.Interlock.DataProvider; using Aitex.Core.RT.Log; using Aitex.Core.Util; using MECF.Framework.Common.Equipment; namespace Aitex.Core.RT.IOCore; public class InterlockDaemonManager : Singleton { #region Variables private readonly List _lstActions; private static List _diMap; private static List _doMap; private static List _aiMap; private static List _aoMap; /// /// 每个Module对应的DoAction集合。 /// private readonly Dictionary> _dicActionsPerModule; #endregion #region Constructors /// /// 互锁管理器的构造函数。 /// public InterlockDaemonManager() { _lstActions = new(); _dicActionsPerModule = new(); } #endregion #region Methods /// /// 初始化互锁管理器。 /// /// 互锁配置文件。 /// DO点表。 /// DI点表。 /// AO点表。 /// AI点表。 /// 初始化失败原因。 /// public bool Initialize(string interlockDaemonFile, Dictionary doMap, Dictionary diMap, Dictionary aiMap, Dictionary aoMap, out string reason) { reason = ""; if (string.IsNullOrEmpty(interlockDaemonFile)) { reason = "interlock daemon config file does not specified"; return false; } if (!File.Exists(interlockDaemonFile)) { reason = $"interlock daemon config file {interlockDaemonFile} does not exist"; return false; } _doMap = doMap.Values.Cast().ToList(); _diMap = diMap.Values.Cast().ToList(); _aiMap = aiMap.Values.Cast().ToList(); _aoMap = aoMap.Values.Cast().ToList(); var sbReason = new StringBuilder(); try { var doc = new XmlDocument(); doc.Load(interlockDaemonFile); var xmlNode = doc.SelectSingleNode("Daemon"); if (xmlNode == null) // 如果Interlock节点不存在 { var err = $"Failed to load interlock daemon file, the node 'Daemon' is not found in file {interlockDaemonFile}."; sbReason.AppendLine(err); LOG.Error(err); return false; } foreach (XmlNode childNode in xmlNode.ChildNodes) { // 遍历'Action'节点 if (childNode.NodeType == XmlNodeType.Comment || childNode is not XmlElement xmlElement) continue; if (xmlElement.Name != "Action") { if (xmlElement.NodeType != XmlNodeType.Comment) LOG.Write("interlock daemon file contains no comments content, " + xmlElement.InnerXml); continue; } // Action节点仅支持对DO进行配置 if (!xmlElement.HasAttribute("do") || !xmlElement.HasAttribute("value")) { sbReason.AppendLine("action node has no [do] or [value] attribute"); continue; } var doName = xmlElement.GetAttribute("do"); var doValue = Convert.ToBoolean(xmlElement.GetAttribute("value")); var tip = string.Empty; var dicTips = new Dictionary(); if (!doMap.ContainsKey(doName)) { sbReason.AppendLine("action node " + doName + " no such DO defined"); continue; } // 获取DO实例 var targetDo = doMap[doName]; if (targetDo == null) { // 如果DO不存在,则读取下一个Action节点 sbReason.AppendLine("action node " + doName + " no such DO defined"); continue; } if (xmlElement.HasAttribute("tip")) tip = xmlElement.GetAttribute("tip"); if (xmlElement.HasAttribute("tip.zh-CN")) dicTips["zh-CN"] = xmlElement.GetAttribute("tip.zh-CN"); if (xmlElement.HasAttribute("tip.en-US")) dicTips["en-US"] = xmlElement.GetAttribute("tip.en-US"); var moduleName = GetModuleFromIo(targetDo.Name); if (!_dicActionsPerModule.ContainsKey(moduleName)) _dicActionsPerModule[moduleName] = new List(); // 创建InterlockAction对象 var action = new InterlockDaemonAction(moduleName.ToString(), targetDo, doValue, tip, dicTips); // 遍历Action下的Limit节点,并创建InterlockLimit对象。 foreach (XmlElement node in xmlElement.ChildNodes) { if (node.Name.ToLower() == "or") { var groupOr = CreateOrLimitGroup(node, out var err0); action.AddLogicOrGroup(groupOr); } else { // 获取InterlockLimit对象。 var limit = CreateInterlockLimit(node, out var err); if (limit != null) action.AddLimit(limit); else sbReason.AppendLine(err); } } _dicActionsPerModule[moduleName].Add(action); _lstActions.Add(action); } Debug.Assert(!_dicActionsPerModule.ContainsKey(ModuleName.UnDefined), $"{nameof(InterlockDaemonManager)} {nameof(_dicActionsPerModule)} contains key {ModuleName.UnDefined}"); } catch (Exception ex) { sbReason.AppendLine(ex.Message); } finally { if (sbReason.Length > 0) reason = sbReason.ToString().TrimEnd('\n', '\r'); } return sbReason.Length == 0; } /// /// 背景扫描线程执行的任务。 /// public void Monitor() { // 按Module扫描Interlock Limit foreach (var moduleName in _dicActionsPerModule.Keys.ToList()) { Debug.Assert(moduleName != ModuleName.UnDefined, $"Interlock Manager CanSetDo() undesired module name {ModuleName.UnDefined}"); foreach (var action in _dicActionsPerModule[moduleName].ToList()) action.Monitor(); } } /// /// 通过XML配置创建互锁限制条件对象。 /// /// 包含Limit定义的Xml节点。 /// 创建失败并返回null的原因。 /// private IInterlockLimit CreateInterlockLimit(XmlElement xmlNodeLimit, out string reason) { reason = ""; // 检查节点名称是否为Limit if (xmlNodeLimit.Name != "Limit") // 节点名称不是Limit { reason = "the name of xml node is not 'Limit'"; if (xmlNodeLimit.NodeType != XmlNodeType.Comment) { reason = "interlock config file contains no comments content, " + xmlNodeLimit.InnerXml; LOG.Write(reason); } return null; } string limitValue; if (xmlNodeLimit.HasAttribute("value")) limitValue = xmlNodeLimit.GetAttribute("value"); else { reason = "limit node lack of value attribute"; return null; } var tip = string.Empty; var dicLimitTips = new Dictionary(); if (xmlNodeLimit.HasAttribute("tip")) tip = xmlNodeLimit.GetAttribute("tip"); if (xmlNodeLimit.HasAttribute("tip.zh-CN")) { dicLimitTips["zh-CN"] = xmlNodeLimit.GetAttribute("tip.zh-CN"); if (string.IsNullOrEmpty(tip)) tip = dicLimitTips["zh-CN"]; } if (xmlNodeLimit.HasAttribute("tip.en-US")) { dicLimitTips["en-US"] = xmlNodeLimit.GetAttribute("tip.en-US"); if (string.IsNullOrEmpty(tip)) tip = dicLimitTips["en-US"]; } // 节点不包含‘di’、‘do’、‘ai’、‘ao’,或者不包含‘value’属性 IInterlockLimit limit; if (xmlNodeLimit.HasAttribute("di")) { var limitName = xmlNodeLimit.GetAttribute("di"); var io = GetIoByIoType(IOType.DI, limitName); var provider = new DiValueProvider((DIAccessor)io); limit = new DiLimit(provider, limitValue, tip, dicLimitTips); } else if (xmlNodeLimit.HasAttribute("do")) { var limitName = xmlNodeLimit.GetAttribute("do"); var io = GetIoByIoType(IOType.DO, limitName); var provider = new DoValueProvider((DOAccessor)io); limit = new DoLimit(provider, limitValue, tip, dicLimitTips); } else if (xmlNodeLimit.HasAttribute("ai")) { var limitName = xmlNodeLimit.GetAttribute("ai"); var io = GetIoByIoType(IOType.AI, limitName); var provider = new AiValueProvider((AIAccessor)io); limit = new AiLimit(provider, limitValue, tip, dicLimitTips); } else if (xmlNodeLimit.HasAttribute("ao")) { var limitName = xmlNodeLimit.GetAttribute("ao"); var io = GetIoByIoType(IOType.AO, limitName); var provider = new AoValueProvider((AOAccessor)io); limit = new AoLimit(provider, limitValue, tip, dicLimitTips); } else if (xmlNodeLimit.HasAttribute("polldouble")) { var limitName = xmlNodeLimit.GetAttribute("polldouble"); var provider = new DoubleDataPollProvider(limitName); limit = new DoubleDataPollLimit(provider, limitValue, tip, dicLimitTips); } else if (xmlNodeLimit.HasAttribute("pollbool")) { var limitName = xmlNodeLimit.GetAttribute("pollbool"); var provider = new BoolDataPollProvider(limitName); limit = new BoolDataPollLimit(provider, limitValue, tip, dicLimitTips); } else { reason = "limit node lack of di/do/ai/ao/io/pollbool/polldouble attribute"; return null; } return limit; } /// /// 创建“OR”定义的Limit组。 /// /// /// /// private List CreateOrLimitGroup(XmlNode xmlNodeOr, out string reason) { reason = ""; var limitList = new List(); foreach (XmlElement childNode in xmlNodeOr.ChildNodes) { var limit = CreateInterlockLimit(childNode, out reason); if (limit != null) limitList.Add(limit); else return null; } return limitList; } /// /// 根据给定的IO类型和IO名称,从IO列表中获取IO对象实例。 /// /// IO类型,请参考。 /// IO名称。 /// /// 无效的IO类型。 /// 未找到IO。 private static IIOAccessor GetIoByIoType(IOType type, string ioName) { List dictMap; switch (type) { case IOType.DI: dictMap = _diMap; break; case IOType.DO: dictMap = _doMap; break; case IOType.AI: dictMap = _aiMap; break; case IOType.AO: dictMap = _aoMap; break; default: throw new InvalidIoTypeExeption(); } var io = dictMap.FirstOrDefault(x => x.Name == ioName); if (io != null) return io; throw new IoNotFoundException(type, ioName); } /// /// 从指定的IO中解析该IO所属的模组。 /// /// /// private static ModuleName GetModuleFromIo(string ioName) { Debug.Assert(!string.IsNullOrEmpty(ioName), "the IO name can not be empty."); if (string.IsNullOrEmpty(ioName)) { LOG.Error($"InterlockManager GetModuleFromIo failed, the parameter ioName is empty"); return ModuleName.UnDefined; } // Simulator中TM的Io表配置中没有"TM."前缀; // 为兼容此问题,如果没有找到Module前缀,则用System代替 var posDot = ioName.IndexOf('.'); if (posDot <= 0) return ModuleName.System; var moduleName = ioName.Substring(0, posDot); return ModuleHelper.Converter(moduleName); } #endregion }