2023-04-23 16:43:08 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2023-04-24 11:53:56 +08:00
|
|
|
|
using System.Diagnostics;
|
2023-08-15 18:10:11 +08:00
|
|
|
|
using System.IO;
|
2023-04-23 16:43:08 +08:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using Aitex.Core.RT.Event;
|
2023-08-10 16:44:46 +08:00
|
|
|
|
using Aitex.Core.RT.IOCore.Interlock;
|
|
|
|
|
using Aitex.Core.RT.IOCore.Interlock.DataProvider;
|
2023-04-23 16:43:08 +08:00
|
|
|
|
using Aitex.Core.RT.Log;
|
|
|
|
|
using Aitex.Core.RT.SCCore;
|
|
|
|
|
using Aitex.Core.Util;
|
|
|
|
|
using MECF.Framework.Common.Equipment;
|
|
|
|
|
|
|
|
|
|
namespace Aitex.Core.RT.IOCore
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 互锁管理器。
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 互锁管理器作为独立工作的设备,被系统的后台循环调度。
|
|
|
|
|
/// <br/>
|
|
|
|
|
/// 当监测到某个InterlockLimit被触发时,和该Limit相关的所有Action中定义的DO均被
|
|
|
|
|
/// 置为Action节点Value属性中定义电平的反向电平。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class InterlockManager : Singleton<InterlockManager>
|
|
|
|
|
{
|
|
|
|
|
#region Variables
|
2023-05-13 10:13:55 +08:00
|
|
|
|
|
|
|
|
|
private readonly List<InterlockAction> _lstActions;
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
2023-04-23 16:43:08 +08:00
|
|
|
|
private static List<IIOAccessor> _diMap;
|
|
|
|
|
private static List<IIOAccessor> _doMap;
|
|
|
|
|
private static List<IIOAccessor> _aiMap;
|
|
|
|
|
private static List<IIOAccessor> _aoMap;
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 以Module为单位,当设置该Module所属的DO并触发互锁时,是否输出Info而不是Warning。
|
|
|
|
|
/// </summary>
|
|
|
|
|
private Dictionary<ModuleName, bool> _dicModulePostInfo;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 每个Module对应的Limit集合。
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly Dictionary<ModuleName, List<IInterlockLimit>> _dicLimitsPerModule;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 每个Module对应的DoAction集合。
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly Dictionary<ModuleName, List<InterlockAction>> _dicActionsPerModule;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 每个Limit对应的DoAction集合。
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly Dictionary<IInterlockLimit, List<InterlockAction>> _dicLimitToActionMap;
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 互锁管理器的构造函数。
|
|
|
|
|
/// </summary>
|
2023-05-13 10:13:55 +08:00
|
|
|
|
public InterlockManager()
|
2023-04-23 16:43:08 +08:00
|
|
|
|
{
|
2023-08-09 17:57:50 +08:00
|
|
|
|
_lstActions = new();
|
|
|
|
|
_dicLimitToActionMap = new ();
|
|
|
|
|
_dicActionsPerModule = new();
|
|
|
|
|
_dicLimitsPerModule = new ();
|
|
|
|
|
_dicModulePostInfo = new();
|
2023-04-23 16:43:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 初始化互锁管理器。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="interlockFile">互锁配置文件。</param>
|
|
|
|
|
/// <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>
|
|
|
|
|
public bool Initialize(string interlockFile,
|
|
|
|
|
Dictionary<string, DOAccessor> doMap,
|
|
|
|
|
Dictionary<string, DIAccessor> diMap,
|
|
|
|
|
Dictionary<string, AIAccessor> aiMap,
|
|
|
|
|
Dictionary<string, AOAccessor> aoMap,
|
|
|
|
|
out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = "";
|
2023-08-15 18:10:11 +08:00
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(interlockFile))
|
|
|
|
|
{
|
|
|
|
|
reason = "interlock config file does not specified";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!File.Exists(interlockFile))
|
|
|
|
|
{
|
|
|
|
|
reason = $"interlock config file {interlockFile} does not exist";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_doMap = doMap.Values.Cast<IIOAccessor>().ToList();
|
2023-04-23 16:43:08 +08:00
|
|
|
|
_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();
|
|
|
|
|
doc.Load(interlockFile);
|
|
|
|
|
var xmlNode = doc.SelectSingleNode("Interlock");
|
|
|
|
|
|
|
|
|
|
if(xmlNode == null) // 如果Interlock节点不存在
|
|
|
|
|
{
|
|
|
|
|
var err =
|
|
|
|
|
$"Failed to load interlock file, the node 'Interlock' is not found in file {interlockFile}.";
|
|
|
|
|
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 config 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<string, string>();
|
2023-08-09 17:57:50 +08:00
|
|
|
|
var doActionLimits = new List<IInterlockLimit>();
|
2023-04-23 16:43:08 +08:00
|
|
|
|
if (!doMap.ContainsKey(doName))
|
|
|
|
|
{
|
|
|
|
|
sbReason.AppendLine("action node " + doName + " no such DO defined");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取DO实例
|
2023-08-09 17:57:50 +08:00
|
|
|
|
var targetDo = doMap[doName];
|
|
|
|
|
if (targetDo == null)
|
2023-04-23 16:43:08 +08:00
|
|
|
|
{
|
|
|
|
|
// 如果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");
|
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
// 遍历Action下的Limit节点,并创建InterlockLimit对象。
|
2023-04-23 16:43:08 +08:00
|
|
|
|
foreach (XmlElement nodeLimit in xmlElement.ChildNodes)
|
|
|
|
|
{
|
|
|
|
|
// 获取InterlockLimit对象。
|
|
|
|
|
var limit = CreateInterlockLimit(nodeLimit, out var err);
|
2023-08-09 17:57:50 +08:00
|
|
|
|
if (limit != null)
|
|
|
|
|
{
|
|
|
|
|
doActionLimits.Add(limit);
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
else
|
|
|
|
|
sbReason.AppendLine(err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建InterlockAction对象
|
2023-08-09 17:57:50 +08:00
|
|
|
|
var action = new InterlockAction(targetDo, doValue, tip, dicTips, doActionLimits);
|
|
|
|
|
_lstActions.Add(action);
|
|
|
|
|
|
|
|
|
|
var moduleName = GetModuleFromIo(targetDo.Name);
|
|
|
|
|
if (!_dicActionsPerModule.ContainsKey(moduleName))
|
|
|
|
|
_dicActionsPerModule[moduleName] = new List<InterlockAction>();
|
|
|
|
|
_dicActionsPerModule[moduleName].Add(action);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
|
|
|
|
// 创建InterlockLimit到被使用的InterlockAction映射字典
|
2023-08-09 17:57:50 +08:00
|
|
|
|
foreach (var limit in doActionLimits)
|
2023-04-23 16:43:08 +08:00
|
|
|
|
{
|
|
|
|
|
// 检查InterlockLimit是否已经存在于字典中
|
2023-08-09 17:57:50 +08:00
|
|
|
|
var exists = _dicLimitToActionMap.ContainsKey(limit);
|
|
|
|
|
Debug.Assert(exists, "The limit object is not in the mapping dictionary");
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
// 当前Limit是否反向绑定了其所影响的所有DoAction
|
|
|
|
|
_dicLimitToActionMap[limit].Add(action);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
_dicModulePostInfo = _dicLimitsPerModule.Keys.ToDictionary(x => x, x=>false);
|
|
|
|
|
|
|
|
|
|
Debug.Assert(!_dicActionsPerModule.ContainsKey(ModuleName.UnDefined),
|
|
|
|
|
$"InterlockManager {nameof(_dicActionsPerModule)} contains key {ModuleName.UnDefined}");
|
|
|
|
|
|
|
|
|
|
Debug.Assert(!_dicLimitsPerModule.ContainsKey(ModuleName.UnDefined),
|
|
|
|
|
$"InterlockManager {nameof(_dicLimitsPerModule)} contains key {ModuleName.UnDefined}");
|
2023-04-23 16:43:08 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
sbReason.AppendLine(ex.Message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sbReason.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
reason = sbReason.ToString().TrimEnd('\n', '\r');
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-13 10:13:55 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 背景扫描线程执行的任务。
|
|
|
|
|
/// </summary>
|
2023-04-23 16:43:08 +08:00
|
|
|
|
public void Monitor()
|
2023-08-09 17:57:50 +08:00
|
|
|
|
{
|
|
|
|
|
// 按Module扫描Interlock Limit
|
|
|
|
|
foreach (var moduleName in _dicLimitsPerModule.Keys.ToList())
|
|
|
|
|
{
|
|
|
|
|
Debug.Assert(moduleName != ModuleName.UnDefined,
|
|
|
|
|
$"Interlock Manager CanSetDo() undesired module name {ModuleName.UnDefined}");
|
|
|
|
|
|
|
|
|
|
// 检查当前Module是否旁路Interlock
|
|
|
|
|
var isBypassInterlock = GetScBypassInterlockValue(moduleName);
|
|
|
|
|
if (isBypassInterlock)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
foreach (var limit in _dicLimitsPerModule[moduleName].ToList())
|
|
|
|
|
{
|
2023-05-13 10:13:55 +08:00
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
// 如果互锁没被触发
|
|
|
|
|
if (!limit.IsTriggered())
|
|
|
|
|
continue;
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
var reverseInfo = new StringBuilder();
|
|
|
|
|
var module = "System";
|
2023-05-13 10:13:55 +08:00
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
var actions = _dicLimitToActionMap[limit].ToList();
|
|
|
|
|
foreach (var action in actions)
|
|
|
|
|
{
|
|
|
|
|
// 尝试根据Action定义复位该互锁限制条件对应的所有DO的电平
|
|
|
|
|
if (action.TryReverse(out var reason))
|
|
|
|
|
{
|
|
|
|
|
var ss = action.ActionName.Split('.');
|
|
|
|
|
if (ss.Length > 1 && ModuleHelper.IsPm(ss[0]))
|
|
|
|
|
module = ss[0];
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
reverseInfo.AppendLine(reason);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
2023-08-09 17:57:50 +08:00
|
|
|
|
// 如果PM腔有被恢复的DO,则打印信息并报警。
|
|
|
|
|
if (reverseInfo.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
reverseInfo.Insert(0,
|
2023-08-10 16:44:46 +08:00
|
|
|
|
$"Due to the {limit.Tip}, {limit.Name} is not [{limit.GetLimitValue()}]\r\n");
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
// Post事件的类型, Info还是Warning
|
|
|
|
|
_dicModulePostInfo.TryGetValue(moduleName, out var isPostInfo);
|
|
|
|
|
|
|
|
|
|
if (isPostInfo)
|
|
|
|
|
EV.PostInfoLog(module, reverseInfo.ToString().TrimEnd('\r', '\n'));
|
|
|
|
|
else
|
|
|
|
|
EV.PostWarningLog(module, reverseInfo.ToString().TrimEnd('\r', '\n'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设置指定Module的Interlock打印信息等级。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="module">模组名称</param>
|
|
|
|
|
/// <param name="isInfo">是否以Info等级打印信息</param>
|
|
|
|
|
public void SetEventLevel(string module, bool isInfo)
|
|
|
|
|
{
|
|
|
|
|
var moduleName = ModuleHelper.Converter(module);
|
|
|
|
|
if (_dicModulePostInfo.ContainsKey(moduleName))
|
|
|
|
|
_dicModulePostInfo[moduleName] = isInfo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2023-05-13 10:13:55 +08:00
|
|
|
|
/// 对指定的DO的操作是否满足互锁条件。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="doName">待操作的DO名称。</param>
|
|
|
|
|
/// <param name="onOff">
|
|
|
|
|
/// 指定的输出。
|
|
|
|
|
/// <value>True:输出有效电平</value>
|
|
|
|
|
/// <br/>
|
|
|
|
|
/// <value>False:清除有效电平输出</value>
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <param name="reason">
|
|
|
|
|
/// 如果触发互锁限制,输出互锁限制的原因。
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns></returns>
|
2023-04-23 16:43:08 +08:00
|
|
|
|
public bool CanSetDo(string doName, bool onOff, out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = string.Empty;
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
var moduleName = GetModuleFromIo(doName);
|
|
|
|
|
|
|
|
|
|
Debug.Assert(moduleName != ModuleName.UnDefined,
|
|
|
|
|
$"Interlock Manager CanSetDo() undesired module name {ModuleName.UnDefined}");
|
|
|
|
|
|
|
|
|
|
var isBypassInterlock = GetScBypassInterlockValue(moduleName);
|
|
|
|
|
if (isBypassInterlock)
|
2023-04-23 16:43:08 +08:00
|
|
|
|
return true;
|
2023-05-13 10:13:55 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var action in _lstActions.Where(action => action.IsSame(doName, onOff)))
|
|
|
|
|
{
|
|
|
|
|
return action.CanDo(out reason);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-23 16:43:08 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取系统配置中指定Module的ByPassInterlock参数设置值。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="module">
|
|
|
|
|
/// 模组名称, <see cref="ModuleName"/>
|
|
|
|
|
/// </param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool GetScBypassInterlockValue(ModuleName module)
|
|
|
|
|
{
|
|
|
|
|
return GetScBypassInterlockValue(module.ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取系统配置中指定Module的ByPassInterlock参数设置值。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="module">模组名称</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool GetScBypassInterlockValue(string module)
|
|
|
|
|
{
|
|
|
|
|
if (ModuleHelper.IsPm(module))
|
|
|
|
|
return SC.SafeGetValue($"PM.{module}.BypassInterlock", false);
|
|
|
|
|
else
|
|
|
|
|
return SC.SafeGetValue($"{module}.BypassInterlock", false);
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
2023-05-13 10:13:55 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建互锁显示条件对象。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 注意:对于同一个IO、相同状态的互锁限制条件,该函数确保仅生成一个实例,不会针对同一条件创建不同实例。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <param name="ioType">IO类型,请参考<see cref="IOType"/>。</param>
|
|
|
|
|
/// <param name="ioName">IO名称。</param>
|
|
|
|
|
/// <param name="limitValue">当前限制条件中的IO状态。</param>
|
|
|
|
|
/// <param name="tip">默认语言提示信息。</param>
|
|
|
|
|
/// <param name="cultureTip">多国语言提示信息。</param>
|
|
|
|
|
/// <param name="reason">创建失败并返回null的原因。</param>
|
|
|
|
|
/// <returns>互锁限制条件对象的实例。</returns>
|
|
|
|
|
private IInterlockLimit CreateInterlockLimit(IOType ioType, string ioName, string limitValue,
|
|
|
|
|
string tip, Dictionary<string, string> cultureTip, out string reason)
|
2023-04-23 16:43:08 +08:00
|
|
|
|
{
|
2023-04-24 11:53:56 +08:00
|
|
|
|
Debug.Assert(Enum.TryParse<IOType>(ioType.ToString(), out _),"Undefined IO Type");
|
|
|
|
|
Debug.Assert(!string.IsNullOrEmpty(ioName),"IO Name can not be empty");
|
|
|
|
|
Debug.Assert(!string.IsNullOrEmpty(limitValue),"LimitValue can not be empty");
|
|
|
|
|
|
2023-04-23 16:43:08 +08:00
|
|
|
|
reason = "";
|
|
|
|
|
|
|
|
|
|
// 创建一个InterlockLimit实例。
|
|
|
|
|
IIOAccessor io;
|
|
|
|
|
IInterlockLimit limit;
|
|
|
|
|
try
|
|
|
|
|
{
|
2023-04-24 11:53:56 +08:00
|
|
|
|
io = GetIoByIoType(ioType, ioName);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
}
|
|
|
|
|
catch (IoNotFoundException)
|
|
|
|
|
{
|
2023-04-24 11:53:56 +08:00
|
|
|
|
reason = $"limit node {ioName} no such {ioType} defined";
|
2023-04-23 16:43:08 +08:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
catch (InvalidIoTypeExeption)
|
|
|
|
|
{
|
|
|
|
|
reason = $"limit node {ioName} no such io type defined";
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-24 11:53:56 +08:00
|
|
|
|
switch (ioType)
|
2023-04-23 16:43:08 +08:00
|
|
|
|
{
|
|
|
|
|
case IOType.DI:
|
2023-08-10 16:44:46 +08:00
|
|
|
|
limit = new DiLimit(new DiValueProvider((DIAccessor)io), limitValue, tip, cultureTip);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOType.DO:
|
2023-08-10 16:44:46 +08:00
|
|
|
|
limit = new DoLimit(new DoValueProvider((DOAccessor)io), limitValue, tip, cultureTip);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOType.AI:
|
2023-08-10 16:44:46 +08:00
|
|
|
|
limit = new AiLimit(new AiValueProvider((AIAccessor)io), limitValue, tip, cultureTip);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case IOType.AO:
|
2023-08-10 16:44:46 +08:00
|
|
|
|
limit = new AoLimit(new AoValueProvider((AOAccessor)io), limitValue, tip, cultureTip);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new InvalidIoTypeExeption();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-13 10:13:55 +08:00
|
|
|
|
|
|
|
|
|
// 检查limit是否已经存在。
|
|
|
|
|
var limitExist = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
|
|
|
|
// 如果不存在,则返回新创建的对象。
|
|
|
|
|
if (limitExist == null)
|
2023-04-24 11:53:56 +08:00
|
|
|
|
{
|
2023-05-13 10:13:55 +08:00
|
|
|
|
_dicLimitToActionMap.Add(limit, new List<InterlockAction>());
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
var moduleName = GetModuleFromIo(ioName);
|
|
|
|
|
if (!_dicLimitsPerModule.ContainsKey(moduleName))
|
|
|
|
|
_dicLimitsPerModule[moduleName] = new List<IInterlockLimit>();
|
|
|
|
|
_dicLimitsPerModule[moduleName].Add(limit);
|
|
|
|
|
|
2023-04-23 16:43:08 +08:00
|
|
|
|
return limit;
|
2023-04-24 11:53:56 +08:00
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
|
|
|
|
// 返回已存在的对象。
|
|
|
|
|
return limitExist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 通过XML配置创建互锁限制条件对象。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="xmlNodeLimit">包含Limit定义的Xml节点。</param>
|
|
|
|
|
/// <param name="reason">创建失败并返回null的原因。</param>
|
|
|
|
|
/// <returns></returns>
|
2023-05-13 10:13:55 +08:00
|
|
|
|
private IInterlockLimit CreateInterlockLimit(XmlElement xmlNodeLimit, out string reason)
|
2023-04-23 16:43:08 +08:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-24 11:53:56 +08:00
|
|
|
|
// 节点不包含‘di’、‘do’、‘ai’、‘ao’,或者不包含‘value’属性
|
2023-04-23 16:43:08 +08:00
|
|
|
|
string limitIoName;
|
2023-04-24 11:53:56 +08:00
|
|
|
|
IOType limitIoType;
|
2023-04-23 16:43:08 +08:00
|
|
|
|
if (xmlNodeLimit.HasAttribute("di"))
|
2023-04-24 11:53:56 +08:00
|
|
|
|
{
|
2023-04-23 16:43:08 +08:00
|
|
|
|
limitIoName = xmlNodeLimit.GetAttribute("di");
|
2023-04-24 11:53:56 +08:00
|
|
|
|
limitIoType = IOType.DI;
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
else if (xmlNodeLimit.HasAttribute("do"))
|
2023-04-24 11:53:56 +08:00
|
|
|
|
{
|
2023-04-23 16:43:08 +08:00
|
|
|
|
limitIoName = xmlNodeLimit.GetAttribute("do");
|
2023-04-24 11:53:56 +08:00
|
|
|
|
limitIoType = IOType.DO;
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
else if (xmlNodeLimit.HasAttribute("ai"))
|
2023-04-24 11:53:56 +08:00
|
|
|
|
{
|
2023-04-23 16:43:08 +08:00
|
|
|
|
limitIoName = xmlNodeLimit.GetAttribute("ai");
|
2023-04-24 11:53:56 +08:00
|
|
|
|
limitIoType = IOType.AI;
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
else if (xmlNodeLimit.HasAttribute("ao"))
|
2023-04-24 11:53:56 +08:00
|
|
|
|
{
|
2023-04-23 16:43:08 +08:00
|
|
|
|
limitIoName = xmlNodeLimit.GetAttribute("ao");
|
2023-04-24 11:53:56 +08:00
|
|
|
|
limitIoType = IOType.AO;
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
reason = "limit node lack of di/do/ai/ao/io attribute";
|
|
|
|
|
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<string, string>();
|
|
|
|
|
if (xmlNodeLimit.HasAttribute("tip"))
|
|
|
|
|
tip = xmlNodeLimit.GetAttribute("tip");
|
|
|
|
|
|
|
|
|
|
if (xmlNodeLimit.HasAttribute("tip.zh-CN"))
|
2023-05-13 10:13:55 +08:00
|
|
|
|
{
|
|
|
|
|
dicLimitTips["zh-CN"] = xmlNodeLimit.GetAttribute("tip.zh-CN");
|
|
|
|
|
if (string.IsNullOrEmpty(tip))
|
|
|
|
|
tip = dicLimitTips["zh-CN"];
|
|
|
|
|
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
|
|
|
|
if (xmlNodeLimit.HasAttribute("tip.en-US"))
|
|
|
|
|
{
|
|
|
|
|
dicLimitTips["en-US"] = xmlNodeLimit.GetAttribute("tip.en-US");
|
2023-05-13 10:13:55 +08:00
|
|
|
|
if (string.IsNullOrEmpty(tip))
|
|
|
|
|
tip = dicLimitTips["en-US"];
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
2023-05-13 10:13:55 +08:00
|
|
|
|
var limit = CreateInterlockLimit(limitIoType, limitIoName, limitValue, tip, dicLimitTips, out reason);
|
2023-04-23 16:43:08 +08:00
|
|
|
|
return limit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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);
|
|
|
|
|
}
|
2023-08-09 17:57:50 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 从指定的IO中解析该IO所属的模组。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="ioName"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
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);
|
|
|
|
|
}
|
2023-04-23 16:43:08 +08:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|