using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Equipment;
namespace Aitex.Core.RT.IOCore
{
///
/// 互锁管理器。
///
/// 互锁管理器作为独立工作的设备,被系统的后台循环调度。
///
/// 当监测到某个InterlockLimit被触发时,和该Limit相关的所有Action中定义的DO均被
/// 置为Action节点Value属性中定义电平的反向电平。
///
///
public class InterlockManager : Singleton
{
#region Variables
private static readonly List _lstActions;
private static readonly Dictionary> _dicLimitToActionMap;
private static List _diMap;
private static List _doMap;
private static List _aiMap;
private static List _aoMap;
#endregion
#region Constructors
///
/// 互锁管理器的构造函数。
///
static InterlockManager()
{
_lstActions = new List();
_dicLimitToActionMap = new Dictionary>();
}
#endregion
#region Methods
///
/// 初始化互锁管理器。
///
/// 互锁配置文件。
/// DO点表。
/// DI点表。
/// AO点表。
/// AI点表。
/// 初始化失败原因。
///
public bool Initialize(string interlockFile,
Dictionary doMap,
Dictionary diMap,
Dictionary aiMap,
Dictionary aoMap,
out string reason)
{
reason = "";
_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(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();
var lstLimit = new List();
if (!doMap.ContainsKey(doName))
{
sbReason.AppendLine("action node " + doName + " no such DO defined");
continue;
}
// 获取DO实例
var doAction = doMap[doName];
if (doAction == 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节点
foreach (XmlElement nodeLimit in xmlElement.ChildNodes)
{
// 获取InterlockLimit对象。
var limit = CreateInterlockLimit(nodeLimit, out var err);
if(limit != null)
lstLimit.Add(limit);
else
sbReason.AppendLine(err);
}
// 创建InterlockAction对象
var newAction = new InterlockAction(doAction, doValue, tip, dicTips, lstLimit);
_lstActions.Add(newAction);
// 创建InterlockLimit到被使用的InterlockAction映射字典
foreach (var limit in lstLimit)
{
// 检查InterlockLimit是否已经存在于字典中
var limitInDic = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
// 存在则插入InterlockAction;不存在则新建
if (limitInDic != null)
_dicLimitToActionMap[limitInDic].Add(newAction);
else
_dicLimitToActionMap[limit] = new List { newAction };
}
}
}
catch (Exception ex)
{
sbReason.AppendLine(ex.Message);
}
if (sbReason.Length > 0)
{
reason = sbReason.ToString().TrimEnd('\n', '\r');
return false;
}
return true;
}
public void Monitor()
{
// 如果系统设置中旁路了互锁,则不监测互锁条件
if (SC.ContainsItem("System.BypassInterlock") && SC.GetValue("System.BypassInterlock"))
return;
// 遍历每一个InterlockLimit对象。
foreach (var limit in _dicLimitToActionMap)
{
// 如果互锁没被触发
if (!limit.Key.IsTriggered())
continue;
var reverseInfo = new StringBuilder();
var module = "System";
foreach (var action in limit.Value)
{
// 尝试根据Action定义复位该互锁限制条件对应的所有DO的电平
if (action.TryReverse(out var reason))
{
var ss = action.ActionName.Split('.');
if (ss.Length > 1 && ModuleHelper.IsPm(ss[0]))
module = ss[0];
reverseInfo.AppendLine(reason);
}
}
// 如果PM腔有被恢复的DO,则打印信息并报警。
if (reverseInfo.Length > 0)
{
reverseInfo.Insert(0,
$"Due to the {limit.Key.Tip}, {limit.Key.Name} is not [{limit.Key.GetLimitValue()}]\r\n");
EV.PostWarningLog(module, reverseInfo.ToString().TrimEnd('\r', '\n'));
}
}
}
public bool CanSetDo(string doName, bool onOff, out string reason)
{
reason = string.Empty;
if (SC.ContainsItem("System.BypassInterlock") && SC.GetValue("System.BypassInterlock"))
{
return true;
}
foreach (var action in _lstActions)
{
if (action.IsSame(doName, onOff))
{
return action.CanDo(out reason);
}
}
return true;
}
///
/// 创建互锁显示条件对象。
///
///
/// 注意:对于同一个IO、相同状态的互锁限制条件,该函数确保仅生成一个实例,不会针对同一条件创建不同实例。
///
/// IO名称。
/// 当前限制条件中的IO状态。
/// 默认语言提示信息。
/// 多国语言提示信息。
/// 创建失败并返回null的原因。
/// 互锁限制条件对象的实例。
private static IInterlockLimit CreateInterlockLimit(string ioName, string limitValue, string tip,
Dictionary cultureTip , out string reason)
{
reason = "";
var type = ioName.ToIoType();
// 创建一个InterlockLimit实例。
IIOAccessor io;
IInterlockLimit limit;
try
{
io = GetIoByIoType(ioName.ToIoType(), ioName);
}
catch (IoNotFoundException)
{
reason = $"limit node {ioName} no such {ioName.ToIoType()} defined";
return null;
}
catch (InvalidIoTypeExeption)
{
reason = $"limit node {ioName} no such io type defined";
return null;
}
switch (type)
{
case IOType.DI:
limit = new DiLimit((DIAccessor)io, limitValue, tip, cultureTip);
break;
case IOType.DO:
limit = new DoLimit((DOAccessor)io, limitValue, tip, cultureTip);
break;
case IOType.AI:
limit = new AiLimit((AIAccessor)io, limitValue, tip, cultureTip);
break;
case IOType.AO:
limit = new AoLimit((AOAccessor)io, limitValue, tip, cultureTip);
break;
default:
throw new InvalidIoTypeExeption();
}
// 检查limit是否已经存在。
var limitExist = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
// 如果不存在,则返回新创建的对象。
if (limitExist == null)
return limit;
// 返回已存在的对象。
return limitExist;
}
///
/// 通过XML配置创建互锁限制条件对象。
///
/// 包含Limit定义的Xml节点。
/// 创建失败并返回null的原因。
///
private static 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;
}
// 节点不包含‘di’、‘do’、‘ai’、‘ao’、‘io’,或者不包含‘value’属性
string limitIoName;
if (xmlNodeLimit.HasAttribute("di"))
limitIoName = xmlNodeLimit.GetAttribute("di");
else if (xmlNodeLimit.HasAttribute("do"))
limitIoName = xmlNodeLimit.GetAttribute("do");
else if (xmlNodeLimit.HasAttribute("ai"))
limitIoName = xmlNodeLimit.GetAttribute("ai");
else if (xmlNodeLimit.HasAttribute("ao"))
limitIoName = xmlNodeLimit.GetAttribute("ao");
else if (xmlNodeLimit.HasAttribute("io"))
limitIoName = xmlNodeLimit.GetAttribute("io");
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();
if (xmlNodeLimit.HasAttribute("tip"))
{
tip = xmlNodeLimit.GetAttribute("tip");
}
if (xmlNodeLimit.HasAttribute("tip.zh-CN"))
{
dicLimitTips["zh-CN"] = xmlNodeLimit.GetAttribute("tip.zh-CN");
}
if (xmlNodeLimit.HasAttribute("tip.en-US"))
{
dicLimitTips["en-US"] = xmlNodeLimit.GetAttribute("tip.en-US");
}
var limit = CreateInterlockLimit(limitIoName, limitValue, tip, dicLimitTips, out reason);
return limit;
}
///
/// 根据给定的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);
}
#endregion
}
}