2023-08-16 15:21:02 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
2023-08-17 15:44:38 +08:00
|
|
|
|
using System.ServiceModel.Configuration;
|
2023-08-16 15:21:02 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using Aitex.Core.RT.IOCore.Interlock.DataProvider;
|
2023-08-17 09:43:24 +08:00
|
|
|
|
using Aitex.Core.RT.IOCore.Interlock.Limits;
|
2023-08-16 15:21:02 +08:00
|
|
|
|
using Aitex.Core.RT.Log;
|
|
|
|
|
using MECF.Framework.Common.Equipment;
|
|
|
|
|
|
2023-08-17 09:43:24 +08:00
|
|
|
|
namespace Aitex.Core.RT.IOCore.Interlock;
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
public abstract class InterlockManagerBase<TAction>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
where TAction : IInterlockAction
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 01:10:47 +08:00
|
|
|
|
#region Variables
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
private static List<IIOAccessor> _diMap;
|
|
|
|
|
private static List<IIOAccessor> _doMap;
|
|
|
|
|
private static List<IIOAccessor> _aiMap;
|
|
|
|
|
private static List<IIOAccessor> _aoMap;
|
|
|
|
|
|
2023-08-17 01:10:47 +08:00
|
|
|
|
protected string RootNodeName;
|
|
|
|
|
|
|
|
|
|
protected readonly List<TAction> _lstActions;
|
2023-08-17 01:24:28 +08:00
|
|
|
|
|
2023-08-17 09:43:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 每个Limit对应的DoAction集合。
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly Dictionary<IInterlockLimit, List<IInterlockAction>> _dicLimitToActionMap;
|
|
|
|
|
|
2023-08-16 15:21:02 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 每个Module对应的DoAction集合。
|
|
|
|
|
/// </summary>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
protected readonly Dictionary<ModuleName, List<TAction>> _dicActionsPerModule;
|
2023-08-17 09:43:24 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 每个Module对应的Limit集合。
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly Dictionary<ModuleName, List<IInterlockLimit>> _dicLimitsPerModule;
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 互锁管理器的构造函数。
|
|
|
|
|
/// </summary>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
protected InterlockManagerBase()
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
|
|
|
|
_lstActions = new();
|
|
|
|
|
_dicActionsPerModule = new();
|
2023-08-17 09:43:24 +08:00
|
|
|
|
_dicLimitToActionMap = new ();
|
|
|
|
|
_dicLimitsPerModule = new ();
|
2023-08-16 15:21:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 初始化互锁管理器。
|
|
|
|
|
/// </summary>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
/// <param name="configFile">互锁配置文件。</param>
|
2023-08-16 15:21:02 +08:00
|
|
|
|
/// <param name="doMap">DO点表。</param>
|
|
|
|
|
/// <param name="diMap">DI点表。</param>
|
|
|
|
|
/// <param name="aoMap">AO点表。</param>
|
|
|
|
|
/// <param name="aiMap">AI点表。</param>
|
|
|
|
|
/// <param name="reason">初始化失败原因。</param>
|
|
|
|
|
/// <returns></returns>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
public virtual bool Initialize(string configFile,
|
2023-08-16 15:21:02 +08:00
|
|
|
|
Dictionary<string, DOAccessor> doMap,
|
|
|
|
|
Dictionary<string, DIAccessor> diMap,
|
|
|
|
|
Dictionary<string, AIAccessor> aiMap,
|
|
|
|
|
Dictionary<string, AOAccessor> aoMap,
|
|
|
|
|
out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = "";
|
|
|
|
|
|
2023-08-17 01:10:47 +08:00
|
|
|
|
if (string.IsNullOrEmpty(configFile))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
|
|
|
|
reason = "interlock daemon config file does not specified";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 01:10:47 +08:00
|
|
|
|
if (!File.Exists(configFile))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 01:10:47 +08:00
|
|
|
|
reason = $"interlock daemon config file {configFile} does not exist";
|
2023-08-16 15:21:02 +08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_doMap = doMap.Values.Cast<IIOAccessor>().ToList();
|
|
|
|
|
_diMap = diMap.Values.Cast<IIOAccessor>().ToList();
|
|
|
|
|
_aiMap = aiMap.Values.Cast<IIOAccessor>().ToList();
|
|
|
|
|
_aoMap = aoMap.Values.Cast<IIOAccessor>().ToList();
|
|
|
|
|
|
|
|
|
|
var sbReason = new StringBuilder();
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var doc = new XmlDocument();
|
2023-08-17 01:10:47 +08:00
|
|
|
|
doc.Load(configFile);
|
|
|
|
|
var xmlNode = doc.SelectSingleNode(RootNodeName);
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
if (xmlNode == null) // 如果Interlock节点不存在
|
|
|
|
|
{
|
|
|
|
|
var err =
|
2023-08-17 01:10:47 +08:00
|
|
|
|
$"Failed to load interlock daemon file, the node 'Daemon' is not found in file {configFile}.";
|
2023-08-16 15:21:02 +08:00
|
|
|
|
sbReason.AppendLine(err);
|
|
|
|
|
LOG.Error(err);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (XmlNode childNode in xmlNode.ChildNodes)
|
|
|
|
|
{
|
|
|
|
|
// 遍历'Action'节点
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (!CheckIsXmlElement(childNode, out var actionNode))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (actionNode.Name != "Action")
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (actionNode.NodeType != XmlNodeType.Comment)
|
|
|
|
|
LOG.Write("interlock daemon file contains no comments content, " + actionNode.InnerXml);
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Action节点仅支持对DO进行配置
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (!actionNode.HasAttribute("do") || !actionNode.HasAttribute("value"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
|
|
|
|
sbReason.AppendLine("action node has no [do] or [value] attribute");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var doName = actionNode.GetAttribute("do");
|
|
|
|
|
var doValue = Convert.ToBoolean(actionNode.GetAttribute("value"));
|
2023-08-16 15:21:02 +08:00
|
|
|
|
var tip = string.Empty;
|
|
|
|
|
var dicTips = new Dictionary<string, string>();
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (actionNode.HasAttribute("tip"))
|
|
|
|
|
tip = actionNode.GetAttribute("tip");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (actionNode.HasAttribute("tip.zh-CN"))
|
|
|
|
|
dicTips["zh-CN"] = actionNode.GetAttribute("tip.zh-CN");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (actionNode.HasAttribute("tip.en-US"))
|
|
|
|
|
dicTips["en-US"] = actionNode.GetAttribute("tip.en-US");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
var moduleName = GetModuleFromIo(targetDo.Name);
|
|
|
|
|
if (!_dicActionsPerModule.ContainsKey(moduleName))
|
|
|
|
|
_dicActionsPerModule[moduleName] = new List<TAction>();
|
|
|
|
|
|
|
|
|
|
// 创建InterlockAction对象
|
|
|
|
|
|
|
|
|
|
var action = (TAction)Activator.CreateInstance(typeof(TAction), moduleName.ToString(),
|
|
|
|
|
targetDo, doValue, tip, dicTips);
|
|
|
|
|
|
|
|
|
|
// 遍历Action下的Limit节点,并创建InterlockLimit对象。
|
2023-08-17 15:44:38 +08:00
|
|
|
|
foreach (XmlNode limitNode in actionNode.ChildNodes)
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (!CheckIsXmlElement(limitNode, out var node))
|
|
|
|
|
continue;
|
|
|
|
|
|
2023-08-16 15:21:02 +08:00
|
|
|
|
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);
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
|
2023-08-17 09:43:24 +08:00
|
|
|
|
foreach (var limit in action.Limits)
|
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
// 创建以Limit分组的Action字典
|
2023-08-17 09:43:24 +08:00
|
|
|
|
if (!_dicLimitToActionMap.ContainsKey(limit))
|
|
|
|
|
_dicLimitToActionMap[limit] = new List<IInterlockAction>();
|
|
|
|
|
|
|
|
|
|
_dicLimitToActionMap[limit].Add(action);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-16 15:21:02 +08:00
|
|
|
|
Debug.Assert(!_dicActionsPerModule.ContainsKey(ModuleName.UnDefined),
|
2023-08-17 09:43:24 +08:00
|
|
|
|
$"InterlockManager {nameof(_dicActionsPerModule)} contains key {ModuleName.UnDefined}");
|
|
|
|
|
|
|
|
|
|
Debug.Assert(!_dicLimitsPerModule.ContainsKey(ModuleName.UnDefined),
|
|
|
|
|
$"InterlockManager {nameof(_dicLimitsPerModule)} contains key {ModuleName.UnDefined}");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
sbReason.AppendLine(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (sbReason.Length > 0)
|
|
|
|
|
reason = sbReason.ToString().TrimEnd('\n', '\r');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sbReason.Length == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 背景扫描线程执行的任务。
|
|
|
|
|
/// </summary>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
public abstract void Monitor();
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 通过XML配置创建互锁限制条件对象。
|
|
|
|
|
/// </summary>
|
2023-08-17 15:44:38 +08:00
|
|
|
|
/// <param name="limitNode">包含Limit定义的Xml节点。</param>
|
2023-08-16 15:21:02 +08:00
|
|
|
|
/// <param name="reason">创建失败并返回null的原因。</param>
|
|
|
|
|
/// <returns></returns>
|
2023-08-17 15:44:38 +08:00
|
|
|
|
private IInterlockLimit CreateInterlockLimit(XmlElement limitNode, out string reason)
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
|
|
|
|
reason = "";
|
|
|
|
|
|
|
|
|
|
// 检查节点名称是否为Limit
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.Name != "Limit") // 节点名称不是Limit
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
|
|
|
|
reason = "the name of xml node is not 'Limit'";
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.NodeType != XmlNodeType.Comment)
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
reason = "interlock config file contains no comments content, " + limitNode.InnerXml;
|
2023-08-16 15:21:02 +08:00
|
|
|
|
LOG.Write(reason);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string limitValue;
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.HasAttribute("value"))
|
|
|
|
|
limitValue = limitNode.GetAttribute("value");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reason = "limit node lack of value attribute";
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var tip = string.Empty;
|
|
|
|
|
var dicLimitTips = new Dictionary<string, string>();
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.HasAttribute("tip"))
|
|
|
|
|
tip = limitNode.GetAttribute("tip");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.HasAttribute("tip.zh-CN"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
dicLimitTips["zh-CN"] = limitNode.GetAttribute("tip.zh-CN");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
if (string.IsNullOrEmpty(tip))
|
|
|
|
|
tip = dicLimitTips["zh-CN"];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.HasAttribute("tip.en-US"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
dicLimitTips["en-US"] = limitNode.GetAttribute("tip.en-US");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
if (string.IsNullOrEmpty(tip))
|
|
|
|
|
tip = dicLimitTips["en-US"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 节点不包含‘di’、‘do’、‘ai’、‘ao’,或者不包含‘value’属性
|
|
|
|
|
IInterlockLimit limit;
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (limitNode.HasAttribute("di"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var limitName = limitNode.GetAttribute("di");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
var io = GetIoByIoType(IOType.DI, limitName);
|
|
|
|
|
var provider = new DiValueProvider((DIAccessor)io);
|
|
|
|
|
limit = new DiLimit(provider, limitValue, tip, dicLimitTips);
|
|
|
|
|
}
|
2023-08-17 15:44:38 +08:00
|
|
|
|
else if (limitNode.HasAttribute("do"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var limitName = limitNode.GetAttribute("do");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
var io = GetIoByIoType(IOType.DO, limitName);
|
|
|
|
|
var provider = new DoValueProvider((DOAccessor)io);
|
|
|
|
|
limit = new DoLimit(provider, limitValue, tip, dicLimitTips);
|
|
|
|
|
}
|
2023-08-17 15:44:38 +08:00
|
|
|
|
else if (limitNode.HasAttribute("ai"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var limitName = limitNode.GetAttribute("ai");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
var io = GetIoByIoType(IOType.AI, limitName);
|
|
|
|
|
var provider = new AiValueProvider((AIAccessor)io);
|
|
|
|
|
limit = new AiLimit(provider, limitValue, tip, dicLimitTips);
|
|
|
|
|
}
|
2023-08-17 15:44:38 +08:00
|
|
|
|
else if (limitNode.HasAttribute("ao"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var limitName = limitNode.GetAttribute("ao");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
var io = GetIoByIoType(IOType.AO, limitName);
|
|
|
|
|
var provider = new AoValueProvider((AOAccessor)io);
|
|
|
|
|
limit = new AoLimit(provider, limitValue, tip, dicLimitTips);
|
|
|
|
|
}
|
2023-08-17 15:44:38 +08:00
|
|
|
|
else if (limitNode.HasAttribute("polldouble"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var limitName = limitNode.GetAttribute("polldouble");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
var provider = new DoubleDataPollProvider(limitName);
|
|
|
|
|
limit = new DoubleDataPollLimit(provider, limitValue, tip, dicLimitTips);
|
|
|
|
|
}
|
2023-08-17 15:44:38 +08:00
|
|
|
|
else if (limitNode.HasAttribute("pollbool"))
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
var limitName = limitNode.GetAttribute("pollbool");
|
2023-08-16 15:21:02 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
// 从_dicLimitsPerModule字典中查找当前创建的Limit是否已经存在;
|
|
|
|
|
// 如果存在,直接返回已存在的Limit实例;
|
|
|
|
|
// 否则,将当前创建的Limit放到字典中,然后返回实例;
|
|
|
|
|
var moduleName = GetModuleFromIo(limit.Name);
|
|
|
|
|
if (_dicLimitsPerModule.TryGetValue(moduleName, out var list))
|
|
|
|
|
{
|
|
|
|
|
var existLimit = list.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
|
|
|
|
|
if(existLimit != null)
|
|
|
|
|
limit = existLimit; // 返回字典中的Limit实例
|
|
|
|
|
else
|
|
|
|
|
list.Add(limit); // 将当前Limit加入字典
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 在字典中创建Module,并将Limit实例放入字典
|
|
|
|
|
_dicLimitsPerModule[moduleName] = new List<IInterlockLimit>(new[] { limit });
|
|
|
|
|
}
|
2023-08-17 01:24:28 +08:00
|
|
|
|
|
2023-08-16 15:21:02 +08:00
|
|
|
|
return limit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建“OR”定义的Limit组。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="xmlNodeOr"></param>
|
|
|
|
|
/// <param name="reason"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private List<IInterlockLimit> CreateOrLimitGroup(XmlNode xmlNodeOr, out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = "";
|
|
|
|
|
var limitList = new List<IInterlockLimit>();
|
2023-08-17 15:44:38 +08:00
|
|
|
|
foreach (XmlNode childNode in xmlNodeOr.ChildNodes)
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
2023-08-17 15:44:38 +08:00
|
|
|
|
if (!CheckIsXmlElement(childNode, out var node))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var limit = CreateInterlockLimit(node, out reason);
|
2023-08-16 15:21:02 +08:00
|
|
|
|
if (limit != null)
|
|
|
|
|
limitList.Add(limit);
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return limitList;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据给定的IO类型和IO名称,从IO列表中获取IO对象实例。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="type">IO类型,请参考<see cref="IOType"/>。</param>
|
|
|
|
|
/// <param name="ioName">IO名称。</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
/// <exception cref="InvalidIoTypeExeption">无效的IO类型。</exception>
|
|
|
|
|
/// <exception cref="IoNotFoundException">未找到IO。</exception>
|
|
|
|
|
private static IIOAccessor GetIoByIoType(IOType type, string ioName)
|
|
|
|
|
{
|
|
|
|
|
List<IIOAccessor> 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 从指定的IO中解析该IO所属的模组。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="ioName"></param>
|
|
|
|
|
/// <returns></returns>
|
2023-08-17 01:10:47 +08:00
|
|
|
|
protected static ModuleName GetModuleFromIo(string ioName)
|
2023-08-16 15:21:02 +08:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-17 15:44:38 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 检查制定的XmlNode是否为XmlElement.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="node"></param>
|
|
|
|
|
/// <param name="element"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private static bool CheckIsXmlElement(XmlNode node, out XmlElement element)
|
|
|
|
|
{
|
|
|
|
|
element = null;
|
|
|
|
|
if (node.NodeType != XmlNodeType.Comment && node is XmlElement)
|
|
|
|
|
{
|
|
|
|
|
element = (XmlElement)node;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-16 15:21:02 +08:00
|
|
|
|
#endregion
|
|
|
|
|
}
|