优化InterlockAction的CanDo函数的互锁信息生成方法。
修正InterlockLimitRangeDouble构造函数错误抛出InvalidCastException异常的问题。
修正InterlockLimitRangeInt和InterlockLimitRangeShort未实现字串解析构造函数的问题。
修正InterlockManager的CreateInterlockLimit函数中未将新生成的InterlockLimit对象添加到_dicLimitToActionMap字典中的问题。
移除IOType对象中的ToIoType()扩展方法,因为IO的前缀为ModuleName,而不是DI_、DO_等。
This commit is contained in:
DESKTOP-1N1NK8A\auvkk 2023-04-24 11:53:56 +08:00
parent f57dbff818
commit be713cd7af
7 changed files with 88 additions and 53 deletions

View File

@ -9,29 +9,4 @@ namespace Aitex.Core.RT.IOCore
AI = 2, AI = 2,
AO = 3 AO = 3
} }
public static class IoCoreExtensions
{
public static IOType ToIoType(this string ioName)
{
IOType ioType;
// 根据IO名称前缀确定IO类型
if (ioName.StartsWith("DI_"))
ioType = IOType.DI;
else if(ioName.StartsWith("DO_"))
ioType = IOType.DO;
else if(ioName.StartsWith("AI_"))
ioType = IOType.AI;
else if(ioName.StartsWith("AO_"))
ioType = IOType.AO;
else
{
var err = $"Undefined prefix of io name '{ioName}' of interlock limit.";
LOG.Error(err);
throw new InvalidIoNameException(err);
}
return ioType;
}
}
} }

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace Aitex.Core.RT.IOCore namespace Aitex.Core.RT.IOCore
{ {
@ -48,32 +49,38 @@ namespace Aitex.Core.RT.IOCore
// 如果DO已经输出Action定义的电平则反向 // 如果DO已经输出Action定义的电平则反向
if (_do.SetValue(!_actionValue, out reason)) if (_do.SetValue(!_actionValue, out reason))
reason = $"Force setting DO-{_do.IoTableIndex}({_do.Name}) = [{((!_actionValue) ? "ON" : "OFF")}]"; reason = $"Interlock Force set DO-{_do.IoTableIndex}({_do.Name}) = [{((!_actionValue) ? "ON" : "OFF")}]";
return true; return true;
} }
public bool CanDo(out string reason) public bool CanDo(out string reason)
{ {
reason = string.Empty; reason = string.Empty;
bool result = true; var sbReason = new StringBuilder();
var result = true;
foreach (var limit in _limits) foreach (var limit in _limits)
{ {
if (!limit.CanDo(out var reason2)) if (!limit.CanDo(out var limitReason))
{ {
if (reason.Length > 0) sbReason.AppendLine(limitReason);
{
reason += "\n";
}
else
{
reason =
$"Interlock triggered, DO-{_do.IoTableIndex}({_do.Name}) can not be [{(_actionValue ? "ON" : "OFF")}], {_tip} \n";
}
reason += reason2;
result = false; result = false;
} }
} }
// 如果有互锁被触发,则打印互锁信息
if (!result)
{
sbReason.Insert(0,
$"Interlock triggered, DO-{_do.IoTableIndex}({_do.Name}) can not be [{(_actionValue ? "ON" : "OFF")}], {_tip} \r\n");
reason = sbReason.ToString();
}
return result; return result;
} }
public override string ToString()
{
return $"{ActionName}, Action={_actionValue}, Limit Count={_limits.Count}";
}
} }
} }

View File

@ -16,8 +16,8 @@ public class InterlockLimitRangeDouble : IAnalogInterlockLimitRange<double>
Min = min; Min = min;
Max = max; Max = max;
} }
else
throw new InvalidCastException($"unable to convert {value} to double range."); throw new InvalidCastException($"unable to convert {value} to double range.");
} }
public InterlockLimitRangeDouble(double min, double max) public InterlockLimitRangeDouble(double min, double max)

View File

@ -1,9 +1,25 @@
using System;
namespace Aitex.Core.RT.IOCore; namespace Aitex.Core.RT.IOCore;
public class InterlockLimitRangeInt: IAnalogInterlockLimitRange<int> public class InterlockLimitRangeInt: IAnalogInterlockLimitRange<int>
{ {
#region Constructors #region Constructors
public InterlockLimitRangeInt(string value)
{
var strValues = value.Split(':');
if (strValues.Length == 2
&& int.TryParse(strValues[0], out var min)
&& int.TryParse(strValues[1], out var max))
{
Min = min;
Max = max;
}
else
throw new InvalidCastException($"unable to convert {value} to double range.");
}
public InterlockLimitRangeInt(short min, short max) public InterlockLimitRangeInt(short min, short max)
{ {
Min = min; Min = min;

View File

@ -1,8 +1,24 @@
using System;
namespace Aitex.Core.RT.IOCore; namespace Aitex.Core.RT.IOCore;
public class InterlockLimitRangeShort : IAnalogInterlockLimitRange<short> public class InterlockLimitRangeShort : IAnalogInterlockLimitRange<short>
{ {
#region Constructors #region Constructors
public InterlockLimitRangeShort(string value)
{
var strValues = value.Split(':');
if (strValues.Length == 2
&& short.TryParse(strValues[0], out var min)
&& short.TryParse(strValues[1], out var max))
{
Min = min;
Max = max;
}
else
throw new InvalidCastException($"unable to convert {value} to double range.");
}
public InterlockLimitRangeShort(short min, short max) public InterlockLimitRangeShort(short min, short max)
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
@ -158,12 +159,15 @@ namespace Aitex.Core.RT.IOCore
{ {
// 检查InterlockLimit是否已经存在于字典中 // 检查InterlockLimit是否已经存在于字典中
var limitInDic = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId); var limitInDic = _dicLimitToActionMap.Keys.FirstOrDefault(x => x.UniqueId == limit.UniqueId);
Debug.Assert(limitInDic != null, "The condition should never be hit");
// 存在则插入InterlockAction不存在则新建 // 存在则插入InterlockAction不存在则新建
if (limitInDic != null) if (limitInDic != null)
_dicLimitToActionMap[limitInDic].Add(newAction); _dicLimitToActionMap[limitInDic].Add(newAction);
else else
{
_dicLimitToActionMap[limit] = new List<InterlockAction> { newAction }; _dicLimitToActionMap[limit] = new List<InterlockAction> { newAction };
}
} }
} }
} }
@ -249,22 +253,25 @@ namespace Aitex.Core.RT.IOCore
/// <param name="cultureTip">多国语言提示信息。</param> /// <param name="cultureTip">多国语言提示信息。</param>
/// <param name="reason">创建失败并返回null的原因。</param> /// <param name="reason">创建失败并返回null的原因。</param>
/// <returns>互锁限制条件对象的实例。</returns> /// <returns>互锁限制条件对象的实例。</returns>
private static IInterlockLimit CreateInterlockLimit(string ioName, string limitValue, string tip, private static IInterlockLimit CreateInterlockLimit(IOType ioType, string ioName, string limitValue, string tip,
Dictionary<string, string> cultureTip , out string reason) 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");
Debug.Assert(!string.IsNullOrEmpty(limitValue),"LimitValue can not be empty");
reason = ""; reason = "";
var type = ioName.ToIoType();
// 创建一个InterlockLimit实例。 // 创建一个InterlockLimit实例。
IIOAccessor io; IIOAccessor io;
IInterlockLimit limit; IInterlockLimit limit;
try try
{ {
io = GetIoByIoType(ioName.ToIoType(), ioName); io = GetIoByIoType(ioType, ioName);
} }
catch (IoNotFoundException) catch (IoNotFoundException)
{ {
reason = $"limit node {ioName} no such {ioName.ToIoType()} defined"; reason = $"limit node {ioName} no such {ioType} defined";
return null; return null;
} }
catch (InvalidIoTypeExeption) catch (InvalidIoTypeExeption)
@ -273,7 +280,7 @@ namespace Aitex.Core.RT.IOCore
return null; return null;
} }
switch (type) switch (ioType)
{ {
case IOType.DI: case IOType.DI:
limit = new DiLimit((DIAccessor)io, limitValue, tip, cultureTip); limit = new DiLimit((DIAccessor)io, limitValue, tip, cultureTip);
@ -301,7 +308,10 @@ namespace Aitex.Core.RT.IOCore
// 如果不存在,则返回新创建的对象。 // 如果不存在,则返回新创建的对象。
if (limitExist == null) if (limitExist == null)
{
_dicLimitToActionMap.Add(limit, new List<InterlockAction>());
return limit; return limit;
}
// 返回已存在的对象。 // 返回已存在的对象。
return limitExist; return limitExist;
@ -330,18 +340,29 @@ namespace Aitex.Core.RT.IOCore
return null; return null;
} }
// 节点不包含didoaiaoio或者不包含value属性 // 节点不包含didoaiao或者不包含value属性
string limitIoName; string limitIoName;
IOType limitIoType;
if (xmlNodeLimit.HasAttribute("di")) if (xmlNodeLimit.HasAttribute("di"))
{
limitIoName = xmlNodeLimit.GetAttribute("di"); limitIoName = xmlNodeLimit.GetAttribute("di");
limitIoType = IOType.DI;
}
else if (xmlNodeLimit.HasAttribute("do")) else if (xmlNodeLimit.HasAttribute("do"))
{
limitIoName = xmlNodeLimit.GetAttribute("do"); limitIoName = xmlNodeLimit.GetAttribute("do");
limitIoType = IOType.DO;
}
else if (xmlNodeLimit.HasAttribute("ai")) else if (xmlNodeLimit.HasAttribute("ai"))
{
limitIoName = xmlNodeLimit.GetAttribute("ai"); limitIoName = xmlNodeLimit.GetAttribute("ai");
limitIoType = IOType.AI;
}
else if (xmlNodeLimit.HasAttribute("ao")) else if (xmlNodeLimit.HasAttribute("ao"))
{
limitIoName = xmlNodeLimit.GetAttribute("ao"); limitIoName = xmlNodeLimit.GetAttribute("ao");
else if (xmlNodeLimit.HasAttribute("io")) limitIoType = IOType.AO;
limitIoName = xmlNodeLimit.GetAttribute("io"); }
else else
{ {
reason = "limit node lack of di/do/ai/ao/io attribute"; reason = "limit node lack of di/do/ai/ao/io attribute";
@ -374,7 +395,7 @@ namespace Aitex.Core.RT.IOCore
dicLimitTips["en-US"] = xmlNodeLimit.GetAttribute("tip.en-US"); dicLimitTips["en-US"] = xmlNodeLimit.GetAttribute("tip.en-US");
} }
var limit = CreateInterlockLimit(limitIoName, limitValue, tip, dicLimitTips, out reason); var limit = CreateInterlockLimit(limitIoType, limitIoName, limitValue, tip, dicLimitTips, out reason);
return limit; return limit;
} }

View File

@ -34,13 +34,13 @@ namespace MECF.Framework.Common.SCCore
/// <summary> /// <summary>
/// 注册一个回调函数,当指定的配置项发生变化时,调用此函数。 /// 注册一个回调函数,当指定的配置项发生变化时,调用此函数。
/// </summary> /// </summary>
/// <param name="name">系统配置项名称。</param> /// <param name="name">系统配置项名称。</param>
/// <param name="callback"> /// <param name="callback">
/// 当配置项值变化时,调用此委托。 /// 当配置项值变化时,调用此委托。
/// <br/> /// <br/>
/// 此委托的传入参数为系统配置值变更后的新值。 /// 此委托的传入参数为系统配置值变更后的新值。
/// </param> /// </param>
void RegisterValueChangedCallback(string name, Action<object> callback); void RegisterValueChangedCallback(string name, Action<object> callback);
} }