255 lines
8.3 KiB
C#
255 lines
8.3 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using Aitex.Core.RT.Event;
|
||
using Aitex.Core.RT.IOCore.Interlock.Base;
|
||
using Aitex.Core.RT.SCCore;
|
||
using MECF.Framework.Common.Equipment;
|
||
|
||
namespace Aitex.Core.RT.IOCore
|
||
{
|
||
/// <summary>
|
||
/// 互锁管理器。
|
||
/// <remarks>
|
||
/// 互锁管理器作为独立工作的设备,被系统的后台循环调度。
|
||
/// <br/>
|
||
/// 当监测到某个InterlockLimit被触发时,和该Limit相关的所有Action中定义的DO均被
|
||
/// 置为Action节点Value属性中定义电平的反向电平。
|
||
/// </remarks>
|
||
/// </summary>
|
||
public class InterlockManager : InterlockManagerBase<InterlockAction>
|
||
{
|
||
#region Variables
|
||
|
||
/// <summary>
|
||
/// 以Module为单位,当设置该Module所属的DO并触发互锁时,是否输出Info而不是Warning。
|
||
/// </summary>
|
||
private Dictionary<ModuleName, bool> _dicModulePostInfo;
|
||
|
||
/// <summary>
|
||
/// 每个Module对应的Limit集合。
|
||
/// </summary>
|
||
private readonly Dictionary<ModuleName, List<IInterlockLimit>> _dicLimitsPerModule;
|
||
|
||
/// <summary>
|
||
/// 每个Limit对应的DoAction集合。
|
||
/// </summary>
|
||
private readonly Dictionary<IInterlockLimit, List<InterlockAction>> _dicLimitToActionMap;
|
||
|
||
#endregion
|
||
|
||
#region Constructors
|
||
|
||
/// <summary>
|
||
/// 互锁管理器的构造函数。
|
||
/// </summary>
|
||
public InterlockManager()
|
||
{
|
||
RootNodeName = "Interlock";
|
||
|
||
_dicLimitToActionMap = new ();
|
||
_dicLimitsPerModule = new ();
|
||
_dicModulePostInfo = new();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Methods
|
||
|
||
/// <summary>
|
||
/// 初始化互锁管理器。
|
||
/// </summary>
|
||
/// <param name="configFie">互锁配置文件。</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 override bool Initialize(string configFie,
|
||
Dictionary<string, DOAccessor> doMap,
|
||
Dictionary<string, DIAccessor> diMap,
|
||
Dictionary<string, AIAccessor> aiMap,
|
||
Dictionary<string, AOAccessor> aoMap,
|
||
out string reason)
|
||
{
|
||
reason = "";
|
||
var succ = base.Initialize(configFie, doMap, diMap, aiMap, aoMap, out reason);
|
||
if (!succ)
|
||
return false;
|
||
|
||
try
|
||
{
|
||
foreach (var action in _lstActions)
|
||
{
|
||
// 创建InterlockLimit到被使用的InterlockAction映射字典
|
||
foreach (var limit in action.Limits)
|
||
{
|
||
// 检查InterlockLimit是否已经存在于字典中
|
||
var exists = _dicLimitToActionMap.ContainsKey(limit);
|
||
Debug.Assert(exists, "The limit object is not in the mapping dictionary");
|
||
|
||
// 当前Limit是否反向绑定了其所影响的所有DoAction
|
||
_dicLimitToActionMap[limit].Add(action);
|
||
}
|
||
}
|
||
|
||
_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}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
reason = ex.Message;
|
||
}
|
||
|
||
if (reason.Length <= 0)
|
||
return true;
|
||
|
||
reason = reason.TrimEnd('\n', '\r');
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 背景扫描线程执行的任务。
|
||
/// </summary>
|
||
public override void Monitor()
|
||
{
|
||
// 按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())
|
||
{
|
||
|
||
// 如果互锁没被触发
|
||
if (!limit.IsTriggered())
|
||
continue;
|
||
|
||
var reverseInfo = new StringBuilder();
|
||
var module = "System";
|
||
|
||
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];
|
||
|
||
reverseInfo.AppendLine(reason);
|
||
}
|
||
}
|
||
|
||
// 如果PM腔有被恢复的DO,则打印信息并报警。
|
||
if (reverseInfo.Length > 0)
|
||
{
|
||
reverseInfo.Insert(0,
|
||
$"Due to the {limit.Tip}, {limit.Name} is not [{limit.GetLimitValue()}]\r\n");
|
||
|
||
// 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>
|
||
/// 对指定的DO的操作是否满足互锁条件。
|
||
/// </summary>
|
||
/// <param name="doName">待操作的DO名称。</param>
|
||
/// <param name="onOff">
|
||
/// 指定的输出。
|
||
/// <value>True:输出有效电平</value>
|
||
/// <br/>
|
||
/// <value>False:清除有效电平输出</value>
|
||
/// </param>
|
||
/// <param name="reason">
|
||
/// 如果触发互锁限制,输出互锁限制的原因。
|
||
/// </param>
|
||
/// <returns></returns>
|
||
public bool CanSetDo(string doName, bool onOff, out string reason)
|
||
{
|
||
reason = string.Empty;
|
||
|
||
var moduleName = GetModuleFromIo(doName);
|
||
|
||
Debug.Assert(moduleName != ModuleName.UnDefined,
|
||
$"Interlock Manager CanSetDo() undesired module name {ModuleName.UnDefined}");
|
||
|
||
var isBypassInterlock = GetScBypassInterlockValue(moduleName);
|
||
if (isBypassInterlock)
|
||
return true;
|
||
|
||
|
||
foreach (var action in _lstActions.Where(action => action.IsSame(doName, onOff)))
|
||
{
|
||
return action.CanDo(out reason);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <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);
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|