优化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,
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.Text;
namespace Aitex.Core.RT.IOCore
{
@ -48,32 +49,38 @@ namespace Aitex.Core.RT.IOCore
// 如果DO已经输出Action定义的电平则反向
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;
}
public bool CanDo(out string reason)
{
reason = string.Empty;
bool result = true;
var sbReason = new StringBuilder();
var result = true;
foreach (var limit in _limits)
{
if (!limit.CanDo(out var reason2))
if (!limit.CanDo(out var limitReason))
{
if (reason.Length > 0)
{
reason += "\n";
}
else
{
reason =
$"Interlock triggered, DO-{_do.IoTableIndex}({_do.Name}) can not be [{(_actionValue ? "ON" : "OFF")}], {_tip} \n";
}
reason += reason2;
sbReason.AppendLine(limitReason);
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;
}
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;
Max = max;
}
throw new InvalidCastException($"unable to convert {value} to double range.");
else
throw new InvalidCastException($"unable to convert {value} to double range.");
}
public InterlockLimitRangeDouble(double min, double max)

View File

@ -1,9 +1,25 @@
using System;
namespace Aitex.Core.RT.IOCore;
public class InterlockLimitRangeInt: IAnalogInterlockLimitRange<int>
{
#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)
{
Min = min;

View File

@ -1,8 +1,24 @@
using System;
namespace Aitex.Core.RT.IOCore;
public class InterlockLimitRangeShort : IAnalogInterlockLimitRange<short>
{
#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)
{

View File

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

View File

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