修正InterlockManager的Monitor()方法中遍历互锁条件字典时可能引发“集合已修改”异常的问题。fix #1
This commit is contained in:
DESKTOP-1N1NK8A\auvkk 2023-05-13 10:13:55 +08:00
parent 613652d5cf
commit e31e0426cc
1 changed files with 70 additions and 42 deletions

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@ -9,6 +10,8 @@ using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Equipment;
using DictLimitToActionMap =
System.Collections.Generic.Dictionary<Aitex.Core.RT.IOCore.IInterlockLimit, System.Collections.Generic.List<Aitex.Core.RT.IOCore.InterlockAction>>;
namespace Aitex.Core.RT.IOCore
{
@ -24,9 +27,9 @@ namespace Aitex.Core.RT.IOCore
public class InterlockManager : Singleton<InterlockManager>
{
#region Variables
private static readonly List<InterlockAction> _lstActions;
private static readonly Dictionary<IInterlockLimit, List<InterlockAction>> _dicLimitToActionMap;
private readonly List<InterlockAction> _lstActions;
private readonly DictLimitToActionMap _dicLimitToActionMap;
private static List<IIOAccessor> _diMap;
private static List<IIOAccessor> _doMap;
private static List<IIOAccessor> _aiMap;
@ -39,10 +42,10 @@ namespace Aitex.Core.RT.IOCore
/// <summary>
/// 互锁管理器的构造函数。
/// </summary>
static InterlockManager()
public InterlockManager()
{
_lstActions = new List<InterlockAction>();
_dicLimitToActionMap = new Dictionary<IInterlockLimit, List<InterlockAction>>();
_dicLimitToActionMap = new DictLimitToActionMap();
}
#endregion
@ -185,6 +188,9 @@ namespace Aitex.Core.RT.IOCore
return true;
}
/// <summary>
/// 背景扫描线程执行的任务。
/// </summary>
public void Monitor()
{
// 如果系统设置中旁路了互锁,则不监测互锁条件
@ -192,15 +198,19 @@ namespace Aitex.Core.RT.IOCore
return;
// 遍历每一个InterlockLimit对象。
foreach (var limit in _dicLimitToActionMap)
var limits = _dicLimitToActionMap.Keys.ToList();
foreach (var limit in limits)
{
// 如果互锁没被触发
if (!limit.Key.IsTriggered())
if (!limit.IsTriggered())
continue;
var reverseInfo = new StringBuilder();
var module = "System";
foreach (var action in limit.Value)
var actions = _dicLimitToActionMap[limit].ToList();
foreach (var action in actions)
{
// 尝试根据Action定义复位该互锁限制条件对应的所有DO的电平
if (action.TryReverse(out var reason))
@ -217,13 +227,27 @@ namespace Aitex.Core.RT.IOCore
if (reverseInfo.Length > 0)
{
reverseInfo.Insert(0,
$"Due to the {limit.Key.Tip}, {limit.Key.Name} is not [{limit.Key.GetLimitValue()}]\r\n");
$"Due to the {limit.Tip}, {limit.Name} is not [{limit.GetLimitValue()}]\r\n");
EV.PostWarningLog(module, reverseInfo.ToString().TrimEnd('\r', '\n'));
}
}
}
/// <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;
@ -231,30 +255,31 @@ namespace Aitex.Core.RT.IOCore
{
return true;
}
foreach (var action in _lstActions)
{
if (action.IsSame(doName, onOff))
{
return action.CanDo(out reason);
}
}
foreach (var action in _lstActions.Where(action => action.IsSame(doName, onOff)))
{
return action.CanDo(out reason);
}
return true;
}
/// <summary>
/// 创建互锁显示条件对象。
/// </summary>
/// <remarks>
/// 注意对于同一个IO、相同状态的互锁限制条件该函数确保仅生成一个实例不会针对同一条件创建不同实例。
/// </remarks>
/// <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 static IInterlockLimit CreateInterlockLimit(IOType ioType, string ioName, string limitValue, string tip,
Dictionary<string, string> cultureTip , out string reason)
/// <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)
{
Debug.Assert(Enum.TryParse<IOType>(ioType.ToString(), out _),"Undefined IO Type");
Debug.Assert(!string.IsNullOrEmpty(ioName),"IO Name can not be empty");
@ -301,15 +326,15 @@ namespace Aitex.Core.RT.IOCore
default:
throw new InvalidIoTypeExeption();
}
// 检查limit是否已经存在。
var limitExist = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
// 检查limit是否已经存在。
var limitExist = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
// 如果不存在,则返回新创建的对象。
if (limitExist == null)
{
_dicLimitToActionMap.Add(limit, new List<InterlockAction>());
_dicLimitToActionMap.Add(limit, new List<InterlockAction>());
return limit;
}
@ -323,7 +348,7 @@ namespace Aitex.Core.RT.IOCore
/// <param name="xmlNodeLimit">包含Limit定义的Xml节点。</param>
/// <param name="reason">创建失败并返回null的原因。</param>
/// <returns></returns>
private static IInterlockLimit CreateInterlockLimit(XmlElement xmlNodeLimit, out string reason)
private IInterlockLimit CreateInterlockLimit(XmlElement xmlNodeLimit, out string reason)
{
reason = "";
@ -381,21 +406,24 @@ namespace Aitex.Core.RT.IOCore
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");
}
{
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"];
}
var limit = CreateInterlockLimit(limitIoType, limitIoName, limitValue, tip, dicLimitTips, out reason);
var limit = CreateInterlockLimit(limitIoType, limitIoName, limitValue, tip, dicLimitTips, out reason);
return limit;
}