修正Interlock可能过早触发的问题。
优化IIoBuffer接口和IoManager对象 - 增加DOMap、DIMap等属性,使私有的_doMap等字段可以被外部对象访问,准备好分离IoManager和InterlockManager。 - 移除初始化InterlockManager的代码。 - 移除OnTimer方法,以及调用OnTimer的后台县城。 优化IIoProvider接口、IoProvider对象 - 新增IsSynced属性,用于检查IoProvider对象是否已经和PLC进行了数据同步。 - 优化IoProvider对象的OnTimer方法中的代码,整理结构并删除多余的代码。 优化IoProviderManager对象 - 新增WaitFirstSync方法,用于检测是否所有的IoProvider均同步了PLC数据。 优化InterlockManagerBase和InterlockManager对象 - Initialize方法返回值由bool变更为void。 - Initialize方法参数表中的doMap、diMap等参数变更为IIoBuffer对象。 - 新增OnTimer方法以及PeriodicJob变量,用于背景线程中执行互锁检查。 优化代码格式 - IoDataCache对象中增加一些注释。
This commit is contained in:
parent
18a96c27ae
commit
2dd2854ecd
|
@ -6,6 +6,7 @@ using Aitex.Core.RT.Event;
|
|||
using Aitex.Core.RT.IOCore.Interlock.Actions;
|
||||
using Aitex.Core.RT.SCCore;
|
||||
using MECF.Framework.Common.Equipment;
|
||||
using MECF.Framework.RT.Core.IoProviders;
|
||||
|
||||
namespace Aitex.Core.RT.IOCore.Interlock
|
||||
{
|
||||
|
@ -41,33 +42,20 @@ namespace Aitex.Core.RT.IOCore.Interlock
|
|||
}
|
||||
|
||||
#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>
|
||||
/// <param name="ioManager">点表。</param>ioManager
|
||||
/// <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)
|
||||
public override void Initialize(string configFie, IIoBuffer ioManager)
|
||||
{
|
||||
reason = "";
|
||||
var succeeded = base.Initialize(configFie, doMap, diMap, aiMap, aoMap, out reason);
|
||||
if (!succeeded)
|
||||
return false;
|
||||
|
||||
_dicModulePostInfo = _dictINTLKActionsPerModule.Keys.ToDictionary(x => x, x => false);
|
||||
return true;
|
||||
base.Initialize(configFie, ioManager);
|
||||
|
||||
_dicModulePostInfo = _dictINTLKActionsPerModule?.Keys.ToDictionary(x => x, x => false) ?? new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -9,7 +9,9 @@ using System.Xml;
|
|||
using Aitex.Core.RT.IOCore.Interlock.DataProvider;
|
||||
using Aitex.Core.RT.IOCore.Interlock.Limits;
|
||||
using Aitex.Core.RT.Log;
|
||||
using Aitex.Core.Util;
|
||||
using MECF.Framework.Common.Equipment;
|
||||
using MECF.Framework.RT.Core.IoProviders;
|
||||
|
||||
namespace Aitex.Core.RT.IOCore.Interlock;
|
||||
|
||||
|
@ -18,10 +20,10 @@ public abstract class InterlockManagerBase<TAction>
|
|||
{
|
||||
#region Variables
|
||||
|
||||
private static List<IIOAccessor> _diMap;
|
||||
private static List<IIOAccessor> _doMap;
|
||||
private static List<IIOAccessor> _aiMap;
|
||||
private static List<IIOAccessor> _aoMap;
|
||||
private List<IIOAccessor> _diMap;
|
||||
private List<IIOAccessor> _doMap;
|
||||
private List<IIOAccessor> _aiMap;
|
||||
private List<IIOAccessor> _aoMap;
|
||||
|
||||
protected string RootNodeName;
|
||||
|
||||
|
@ -42,6 +44,9 @@ public abstract class InterlockManagerBase<TAction>
|
|||
/// </summary>
|
||||
protected readonly Dictionary<ModuleName, List<IInterlockLimit>> _dictLIMTPerModule;
|
||||
|
||||
private PeriodicJob _monitorThread;
|
||||
private R_TRIG _rTrigMonitorError;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -65,37 +70,23 @@ public abstract class InterlockManagerBase<TAction>
|
|||
/// 初始化互锁管理器。
|
||||
/// </summary>
|
||||
/// <param name="configFile">互锁配置文件。</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>
|
||||
/// <param name="ioManager">点表。</param>
|
||||
/// <returns></returns>
|
||||
public virtual bool Initialize(string configFile,
|
||||
Dictionary<string, DOAccessor> doMap,
|
||||
Dictionary<string, DIAccessor> diMap,
|
||||
Dictionary<string, AIAccessor> aiMap,
|
||||
Dictionary<string, AOAccessor> aoMap,
|
||||
out string reason)
|
||||
public virtual void Initialize(string configFile, IIoBuffer ioManager)
|
||||
{
|
||||
reason = "";
|
||||
Debug.Assert(!string.IsNullOrEmpty(configFile));
|
||||
Debug.Assert(ioManager != null);
|
||||
|
||||
if (string.IsNullOrEmpty(configFile))
|
||||
{
|
||||
reason = "interlock daemon config file does not specified";
|
||||
return false;
|
||||
LOG.Error("Interlock config file does not specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(configFile))
|
||||
{
|
||||
reason = $"interlock daemon config file {configFile} does not exist";
|
||||
return false;
|
||||
}
|
||||
|
||||
_doMap = doMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_diMap = diMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_aiMap = aiMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_aoMap = aoMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_doMap = ioManager.DOMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_diMap = ioManager.DIMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_aiMap = ioManager.AIMap.Values.Cast<IIOAccessor>().ToList();
|
||||
_aoMap = ioManager.AOMap.Values.Cast<IIOAccessor>().ToList();
|
||||
|
||||
var sbReason = new StringBuilder();
|
||||
try
|
||||
|
@ -107,10 +98,10 @@ public abstract class InterlockManagerBase<TAction>
|
|||
if (xmlNode == null) // 如果Interlock节点不存在
|
||||
{
|
||||
var err =
|
||||
$"Failed to load interlock daemon file, the node 'Daemon' is not found in file {configFile}.";
|
||||
$"Failed to load interlock config file, the node '{RootNodeName}' is not found in {configFile}.";
|
||||
sbReason.AppendLine(err);
|
||||
LOG.Error(err);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (XmlNode childNode in xmlNode.ChildNodes)
|
||||
|
@ -139,14 +130,14 @@ public abstract class InterlockManagerBase<TAction>
|
|||
var doValue = Convert.ToBoolean(actionNode.GetAttribute("value"));
|
||||
var tip = string.Empty;
|
||||
var dicTips = new Dictionary<string, string>();
|
||||
if (!doMap.ContainsKey(doName))
|
||||
if (!ioManager.DOMap.ContainsKey(doName))
|
||||
{
|
||||
sbReason.AppendLine("action node " + doName + " no such DO defined");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取DO实例
|
||||
var targetDo = doMap[doName];
|
||||
var targetDo = ioManager.DOMap[doName];
|
||||
if (targetDo == null)
|
||||
{
|
||||
// 如果DO不存在,则读取下一个Action节点
|
||||
|
@ -163,11 +154,11 @@ public abstract class InterlockManagerBase<TAction>
|
|||
if (actionNode.HasAttribute("tip.en-US"))
|
||||
dicTips["en-US"] = actionNode.GetAttribute("tip.en-US");
|
||||
|
||||
var ignoreReverse_All = false;//单个Action的全局ignore,为True时所有Limit条件不反向控制
|
||||
var ignoreReverseAll = false; //单个Action的全局ignore,为True时所有Limit条件不反向控制
|
||||
if (actionNode.HasAttribute("ignoreReverse"))
|
||||
{
|
||||
var strIgnoreReverse = actionNode.GetAttribute("ignoreReverse");
|
||||
if (!bool.TryParse(strIgnoreReverse, out ignoreReverse_All))
|
||||
if (!bool.TryParse(strIgnoreReverse, out ignoreReverseAll))
|
||||
LOG.Error($"Unable to convert attribute 'ignoreReverse' of action '{doName}' to bool.");
|
||||
}
|
||||
|
||||
|
@ -217,22 +208,22 @@ public abstract class InterlockManagerBase<TAction>
|
|||
continue;
|
||||
}
|
||||
|
||||
if(!_dictINTLKActionsFileLoader.TryAdd((action.ActionName, doValue), action))
|
||||
if (!_dictINTLKActionsFileLoader.TryAdd((action.ActionName, doValue), action))
|
||||
LOG.Error($"Unable to add {action} to the ConcurrentDictionary");
|
||||
|
||||
// 如果当前Action设置了忽略翻转,则不要注册Limit到当前Action的映射,避免Limit触发导致Action翻转。
|
||||
if (!ignoreReverse_All)
|
||||
if (!ignoreReverseAll)
|
||||
{
|
||||
foreach (var limit in action.Limits)
|
||||
{
|
||||
if (!limit.IgnoreReverse)//如果单个limit也忽略反转,不要注册Limit到当前Action的映射,避免Limit触发导致Action翻转。
|
||||
if (!limit.IgnoreReverse) //如果单个limit也忽略反转,不要注册Limit到当前Action的映射,避免Limit触发导致Action翻转。
|
||||
{
|
||||
// 创建以Limit分组的Action字典
|
||||
if (!_dictLIMT2INTLKActionMap.ContainsKey(limit))
|
||||
_dictLIMT2INTLKActionMap[limit] = new List<IInterlockAction>();
|
||||
|
||||
_dictLIMT2INTLKActionMap[limit].Add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +231,7 @@ public abstract class InterlockManagerBase<TAction>
|
|||
|
||||
Debug.Assert(!_dictINTLKActionsPerModule.ContainsKey(ModuleName.UnDefined),
|
||||
$"InterlockManager {nameof(_dictINTLKActionsPerModule)} contains key {ModuleName.UnDefined}");
|
||||
|
||||
|
||||
Debug.Assert(!_dictLIMTPerModule.ContainsKey(ModuleName.UnDefined),
|
||||
$"InterlockManager {nameof(_dictLIMTPerModule)} contains key {ModuleName.UnDefined}");
|
||||
}
|
||||
|
@ -250,11 +241,25 @@ public abstract class InterlockManagerBase<TAction>
|
|||
}
|
||||
finally
|
||||
{
|
||||
if (sbReason.Length > 0)
|
||||
reason = sbReason.ToString().TrimEnd('\n', '\r');
|
||||
_monitorThread ??= new PeriodicJob(200, OnTimer, $"{GetType().Name} Monitor Thread", isStartNow: true);
|
||||
}
|
||||
}
|
||||
|
||||
private bool OnTimer()
|
||||
{
|
||||
try
|
||||
{
|
||||
Monitor();
|
||||
_rTrigMonitorError.CLK = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_rTrigMonitorError.CLK = true;
|
||||
if (_rTrigMonitorError.Q)
|
||||
LOG.Write(ex);
|
||||
}
|
||||
|
||||
return sbReason.Length == 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -424,7 +429,7 @@ public abstract class InterlockManagerBase<TAction>
|
|||
/// <returns></returns>
|
||||
/// <exception cref="InvalidIoTypeExeption">无效的IO类型。</exception>
|
||||
/// <exception cref="IoNotFoundException">未找到IO。</exception>
|
||||
private static IIOAccessor GetIoByIoType(IOType type, string ioName)
|
||||
private IIOAccessor GetIoByIoType(IOType type, string ioName)
|
||||
{
|
||||
List<IIOAccessor> dictMap;
|
||||
switch (type)
|
||||
|
|
|
@ -2,6 +2,11 @@ using System.Collections.Generic;
|
|||
|
||||
namespace MECF.Framework.Common.IOCore;
|
||||
|
||||
/// <summary>
|
||||
/// Key: Offset
|
||||
/// Value: Buffer
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class IoDataCachePerOffset<T> : Dictionary<int, T>, IIoDataCache
|
||||
{
|
||||
public IoDataCachePerOffset(string providerName)
|
||||
|
|
|
@ -7,7 +7,6 @@ using Aitex.Core.RT.DataCenter;
|
|||
using Aitex.Core.RT.Event;
|
||||
using Aitex.Core.RT.IOCore;
|
||||
using Aitex.Core.RT.IOCore.Interlock;
|
||||
using Aitex.Core.RT.Log;
|
||||
using Aitex.Core.RT.OperationCenter;
|
||||
using Aitex.Core.RT.SCCore;
|
||||
using Aitex.Core.Util;
|
||||
|
@ -17,48 +16,42 @@ namespace MECF.Framework.Common.IOCore
|
|||
{
|
||||
public class IoManager : Singleton<IoManager>, IIoBuffer
|
||||
{
|
||||
private Dictionary<string, DIAccessor> _diMap = new();
|
||||
|
||||
private Dictionary<string, DOAccessor> _doMap = new();
|
||||
|
||||
private Dictionary<string, AIAccessor> _aiMap = new();
|
||||
|
||||
private Dictionary<string, AOAccessor> _aoMap = new();
|
||||
|
||||
private Dictionary<string, IoDataCachePerOffset<bool[]>> _diBuffer = new();
|
||||
|
||||
private Dictionary<string, IoDataCachePerOffset<bool[]>> _doBuffer = new();
|
||||
|
||||
private Dictionary<string, IoDataCachePerOffset<float[]>> _aiBuffer = new();
|
||||
|
||||
private Dictionary<string, IoDataCachePerOffset<float[]>> _aoBuffer = new();
|
||||
|
||||
private Dictionary<string, IoDataCachePerOffset<Type>> _aiBufferType = new();
|
||||
|
||||
private Dictionary<string, IoDataCachePerOffset<Type>> _aoBufferType = new();
|
||||
|
||||
private Dictionary<string, List<DIAccessor>> _diListPerPlc = new();
|
||||
|
||||
private Dictionary<string, List<DOAccessor>> _doListPerPlc = new();
|
||||
|
||||
private Dictionary<string, List<AIAccessor>> _aiListPerPlc = new();
|
||||
|
||||
private Dictionary<string, List<AOAccessor>> _aoListPerPlc = new();
|
||||
|
||||
private Dictionary<string, List<NotifiableIoItem>> _ioItemList = new();
|
||||
|
||||
private PeriodicJob _monitorThread;
|
||||
private readonly Dictionary<string, DIAccessor> _diMap = new();
|
||||
private readonly Dictionary<string, DOAccessor> _doMap = new();
|
||||
private readonly Dictionary<string, AIAccessor> _aiMap = new();
|
||||
private readonly Dictionary<string, AOAccessor> _aoMap = new();
|
||||
private readonly Dictionary<string, IoDataCachePerOffset<bool[]>> _diBuffer = new();
|
||||
private readonly Dictionary<string, IoDataCachePerOffset<bool[]>> _doBuffer = new();
|
||||
private readonly Dictionary<string, IoDataCachePerOffset<float[]>> _aiBuffer = new();
|
||||
private readonly Dictionary<string, IoDataCachePerOffset<float[]>> _aoBuffer = new();
|
||||
private readonly Dictionary<string, IoDataCachePerOffset<Type>> _aiBufferType = new();
|
||||
private readonly Dictionary<string, IoDataCachePerOffset<Type>> _aoBufferType = new();
|
||||
private readonly Dictionary<string, List<DIAccessor>> _diListPerPlc = new();
|
||||
private readonly Dictionary<string, List<DOAccessor>> _doListPerPlc = new();
|
||||
private readonly Dictionary<string, List<AIAccessor>> _aiListPerPlc = new();
|
||||
private readonly Dictionary<string, List<AOAccessor>> _aoListPerPlc = new();
|
||||
private readonly Dictionary<string, List<NotifiableIoItem>> _ioItemList = new();
|
||||
//private PeriodicJob _monitorThread;
|
||||
|
||||
/// <summary>
|
||||
/// 是否使用在模拟器中。
|
||||
/// </summary>
|
||||
protected bool IsSimulator;
|
||||
|
||||
public void Initialize(string interlockConfigFile, string daemonConfigFile = "")
|
||||
public IReadOnlyDictionary<string, DIAccessor> DIMap => _diMap;
|
||||
|
||||
public IReadOnlyDictionary<string, DOAccessor> DOMap => _doMap;
|
||||
|
||||
public IReadOnlyDictionary<string, AIAccessor> AIMap => _aiMap;
|
||||
|
||||
public IReadOnlyDictionary<string, AOAccessor> AOMap => _aoMap;
|
||||
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (!Singleton<InterlockManager>.Instance.Initialize(interlockConfigFile, _doMap, _diMap, _aiMap, _aoMap,
|
||||
out var reason1))
|
||||
throw new Exception($"init {nameof(InterlockManager)} error: \r\n{reason1}");
|
||||
//if (!Singleton<InterlockManager>.Instance.Initialize(interlockConfigFile, _doMap, _diMap, _aiMap, _aoMap,
|
||||
// out var reason1))
|
||||
// throw new Exception($"init {nameof(InterlockManager)} error: \r\n{reason1}");
|
||||
|
||||
/*if (!Singleton<InterlockDaemonManager>.Instance.Initialize(daemonConfigFile, _doMap, _diMap, _aiMap, _aoMap,
|
||||
out var reason2))
|
||||
|
@ -69,23 +62,23 @@ namespace MECF.Framework.Common.IOCore
|
|||
LOG.Error($"init {nameof(InterlockDaemonManager)} error: \r\n{reason2}");
|
||||
}*/
|
||||
|
||||
if(_monitorThread == null)
|
||||
_monitorThread = new PeriodicJob(200, OnTimer, "IOManager Monitor Thread", isStartNow: true);
|
||||
//if(_monitorThread == null)
|
||||
// _monitorThread = new PeriodicJob(200, OnTimer, "IOManager Monitor Thread", isStartNow: true);
|
||||
}
|
||||
|
||||
private bool OnTimer()
|
||||
{
|
||||
try
|
||||
{
|
||||
Singleton<InterlockManager>.Instance.Monitor();
|
||||
// Singleton<InterlockDaemonManager>.Instance.Monitor();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LOG.Write(ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//private bool OnTimer()
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// Singleton<InterlockManager>.Instance.Monitor();
|
||||
// // Singleton<InterlockDaemonManager>.Instance.Monitor();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// LOG.Write(ex);
|
||||
// }
|
||||
// return true;
|
||||
//}
|
||||
|
||||
internal List<Tuple<int, int, string>> GetIONameList(string group, IOType ioType)
|
||||
{
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
using Aitex.Core.RT.IOCore;
|
||||
using MECF.Framework.Common.IOCore;
|
||||
|
||||
namespace MECF.Framework.RT.Core.IoProviders
|
||||
{
|
||||
public interface IIoBuffer
|
||||
{
|
||||
void SetBufferBlock(string provider, List<IoBlockItem> lstBlocks);
|
||||
IReadOnlyDictionary<string, DIAccessor> DIMap { get; }
|
||||
|
||||
IReadOnlyDictionary<string, DOAccessor> DOMap { get; }
|
||||
|
||||
IReadOnlyDictionary<string, AIAccessor> AIMap { get; }
|
||||
|
||||
IReadOnlyDictionary<string, AOAccessor> AOMap { get; }
|
||||
|
||||
void SetBufferBlock(string provider, List<IoBlockItem> lstBlocks);
|
||||
|
||||
void SetIoMap(string provider, int blockOffset, List<DIAccessor> ioList);
|
||||
|
||||
|
@ -26,8 +35,8 @@ namespace MECF.Framework.RT.Core.IoProviders
|
|||
Dictionary<int, float[]> GetAoBuffer(string source);
|
||||
|
||||
Dictionary<int, float[]> GetAiBuffer(string source);
|
||||
|
||||
void SetDiBuffer(string source, int offset, bool[] buffer);
|
||||
|
||||
void SetDiBuffer(string source, int offset, bool[] buffer);
|
||||
|
||||
void SetDoBuffer(string provider, int offset, bool[] buffer);
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ namespace MECF.Framework.RT.Core.IoProviders
|
|||
string Module { get; set; }
|
||||
|
||||
bool IsOpened { get; }
|
||||
|
||||
bool IsSynced { get; }
|
||||
|
||||
void Initialize(string module, string name, List<IoBlockItem> lstBuffers, IIoBuffer buffer, XmlElement nodeParameter, Dictionary<int, string> ioMappingPathFile);
|
||||
|
||||
|
|
|
@ -33,6 +33,40 @@ namespace MECF.Framework.RT.Core.IoProviders
|
|||
|
||||
public bool IsOpened => State == IoProviderStateEnum.Opened;
|
||||
|
||||
/// <summary>
|
||||
/// 根据DI的值判断是否已经和PLC同步数据。
|
||||
/// </summary>
|
||||
public bool IsSynced
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
var dict = _buffer.GetDiBuffer(_source);
|
||||
if (dict == null)
|
||||
return false;
|
||||
|
||||
if (dict.TryGetValue(0, out var diBuff) == false)
|
||||
return false;
|
||||
|
||||
if(diBuff == null)
|
||||
return false;
|
||||
|
||||
if (Array.TrueForAll(diBuff, x => x == true) ||
|
||||
Array.TrueForAll(diBuff, x => x == false))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IoProviderStateEnum State { get; set; }
|
||||
|
||||
protected abstract void SetParameter(XmlElement nodeParameter);
|
||||
|
@ -118,49 +152,38 @@ namespace MECF.Framework.RT.Core.IoProviders
|
|||
{
|
||||
try
|
||||
{
|
||||
bool flag = false;
|
||||
foreach (IoBlockItem blockSection in _blockSections)
|
||||
{
|
||||
if (blockSection.Type == IoType.DI)
|
||||
{
|
||||
bool[] array = ReadDi(blockSection.Offset, blockSection.Size);
|
||||
if (array != null)
|
||||
{
|
||||
_buffer.SetDiBuffer(_source, blockSection.Offset, array);
|
||||
}
|
||||
var diBuffer = ReadDi(blockSection.Offset, blockSection.Size);
|
||||
if (diBuffer != null)
|
||||
_buffer.SetDiBuffer(_source, blockSection.Offset, diBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (blockSection.Type != IoType.AI)
|
||||
if (blockSection.Type == IoType.AI)
|
||||
{
|
||||
continue;
|
||||
var aiBuffer = ReadAi(blockSection.Offset, blockSection.Size);
|
||||
if (aiBuffer != null)
|
||||
_buffer.SetAiBuffer(_source, blockSection.Offset,
|
||||
aiBuffer.Select(s => (float)s).ToArray());
|
||||
}
|
||||
var array2 = ReadAi(blockSection.Offset, blockSection.Size);
|
||||
if (array2 == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_buffer.SetAiBuffer(_source, blockSection.Offset, array2.Select(s => (float)s).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
var aoBuffer = _buffer.GetAoBuffer(_source);
|
||||
if (aoBuffer != null)
|
||||
{
|
||||
foreach (var item in aoBuffer)
|
||||
{
|
||||
WriteAo(item.Key, item.Value.Select(x=>(short)x).ToArray());
|
||||
if (flag)
|
||||
WriteAoFloat(item.Key, item.Value);
|
||||
}
|
||||
WriteAo(item.Key, item.Value.Select(x => (short)x).ToArray());
|
||||
}
|
||||
Dictionary<int, bool[]> doBuffer = _buffer.GetDoBuffer(_source);
|
||||
|
||||
var doBuffer = _buffer.GetDoBuffer(_source);
|
||||
if (doBuffer != null)
|
||||
{
|
||||
foreach (KeyValuePair<int, bool[]> item2 in doBuffer)
|
||||
{
|
||||
WriteDo(item2.Key, item2.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
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;
|
||||
using MECF.Framework.Common.IOCore;
|
||||
|
||||
namespace MECF.Framework.RT.Core.IoProviders
|
||||
{
|
||||
public class IoProviderManager : Singleton<IoProviderManager>
|
||||
{
|
||||
private List<IIoProvider> _providers = new List<IIoProvider>();
|
||||
private readonly List<IIoProvider> _providers = new ();
|
||||
|
||||
private Dictionary<string, IIoProvider> _dicProviders = new Dictionary<string, IIoProvider>();
|
||||
private readonly Dictionary<string, IIoProvider> _dicProviders = new ();
|
||||
|
||||
public List<IIoProvider> Providers => _providers;
|
||||
|
||||
|
@ -257,5 +262,35 @@ namespace MECF.Framework.RT.Core.IoProviders
|
|||
LOG.Write(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 等待PLC第一次同步数据,以免缓存数据默认值导致误报警。
|
||||
/// </summary>
|
||||
/// <param name="timeout"></param>
|
||||
public void WaitFirstSync(int timeout = 3000)
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
LOG.Info($"{nameof(IoProviderManager)} Waiting for the first data synchronization from the PLC.");
|
||||
while (true)
|
||||
{
|
||||
var notSyncedPlc = _providers.Where(x => x.IsSynced == false).ToArray();
|
||||
if (notSyncedPlc.Length == 0)
|
||||
-break;
|
||||
|
||||
if (sw.ElapsedMilliseconds > timeout)
|
||||
{
|
||||
var plcNames = string.Join(", ", notSyncedPlc.Select(x => $"{x.Module}.{x.Name}").ToArray());
|
||||
EV.PostWarningLog(ModuleName.System.ToString(),
|
||||
$"The PLC(s) [{plcNames}] has not synchronized data in {timeout}ms, " +
|
||||
$"which may lead to erroneous warnings or alarms in the system.");
|
||||
break;
|
||||
}
|
||||
|
||||
// keep waiting
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
using Xunit;
|
||||
using Aitex.Core.RT.IOCore.Interlock;
|
||||
using Xunit;
|
||||
using Aitex.Core.RT.SCCore;
|
||||
using Aitex.Core.Util;
|
||||
using MECF.Framework.Common.IOCore;
|
||||
using MECF.Framework.Common.SCCore;
|
||||
using MECF.Framework.RT.Core.IoProviders;
|
||||
|
@ -23,7 +25,8 @@ namespace Aitex.Core.RT.IOCore.Tests
|
|||
SC.Manager = mock.Object;
|
||||
|
||||
IoProviderManager.Instance.Initialize(FN_IO_PROVIDER);
|
||||
IoManager.Instance.Initialize(FN_INTERLOCK);
|
||||
IoManager.Instance.Initialize();
|
||||
Singleton<InterlockManager>.Instance.Initialize(FN_INTERLOCK, IoManager.Instance, out var reason);
|
||||
Assert.True(false, "This test needs an implementation");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue