Sic.Framework-Nanjing-Baishi/MECF.Framework.Common/Aitex/Core/RT/IOCore/Interlock/InterlockDaemonManager.cs

381 lines
13 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
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<InterlockDaemonManager>
{
#region Variables
private readonly List<InterlockDaemonAction> _lstActions;
private static List<IIOAccessor> _diMap;
private static List<IIOAccessor> _doMap;
private static List<IIOAccessor> _aiMap;
private static List<IIOAccessor> _aoMap;
/// <summary>
/// 每个Module对应的DoAction集合。
/// </summary>
private readonly Dictionary<ModuleName, List<InterlockDaemonAction>> _dicActionsPerModule;
#endregion
#region Constructors
/// <summary>
/// 互锁管理器的构造函数。
/// </summary>
public InterlockDaemonManager()
{
_lstActions = new();
_dicActionsPerModule = new();
}
#endregion
#region Methods
/// <summary>
/// 初始化互锁管理器。
/// </summary>
/// <param name="interlockDaemonFile">互锁配置文件。</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 interlockDaemonFile,
Dictionary<string, DOAccessor> doMap,
Dictionary<string, DIAccessor> diMap,
Dictionary<string, AIAccessor> aiMap,
Dictionary<string, AOAccessor> 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<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();
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<string, string>();
var lstLimits = new List<IInterlockLimit>();
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");
// 遍历Action下的Limit节点并创建InterlockLimit对象。
foreach (XmlElement nodeLimit in xmlElement.ChildNodes)
{
// 获取InterlockLimit对象。
var limit = CreateInterlockLimit(nodeLimit, out var err);
if (limit != null)
{
lstLimits.Add(limit);
}
else
sbReason.AppendLine(err);
}
// 创建InterlockAction对象
var action = new InterlockDaemonAction(targetDo, doValue, tip, dicTips, lstLimits);
_lstActions.Add(action);
var moduleName = GetModuleFromIo(targetDo.Name);
if (!_dicActionsPerModule.ContainsKey(moduleName))
_dicActionsPerModule[moduleName] = new List<InterlockDaemonAction>();
_dicActionsPerModule[moduleName].Add(action);
}
Debug.Assert(!_dicActionsPerModule.ContainsKey(ModuleName.UnDefined),
$"{nameof(InterlockDaemonManager)} {nameof(_dicActionsPerModule)} contains key {ModuleName.UnDefined}");
}
catch (Exception ex)
{
sbReason.AppendLine(ex.Message);
}
if (sbReason.Length > 0)
{
reason = sbReason.ToString().TrimEnd('\n', '\r');
return false;
}
return true;
}
/// <summary>
/// 背景扫描线程执行的任务。
/// </summary>
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())
{
if (action.DaemonMonitor(out var reason))
EV.PostWarningLog(moduleName.ToString(), reason.ToString().TrimEnd('\r', '\n'));
}
}
}
/// <summary>
/// 通过XML配置创建互锁限制条件对象。
/// </summary>
/// <param name="xmlNodeLimit">包含Limit定义的Xml节点。</param>
/// <param name="reason">创建失败并返回null的原因。</param>
/// <returns></returns>
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<string, string>();
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"];
}
// 节点不包含didoaiao或者不包含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;
}
/// <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>
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
}