269 lines
9.4 KiB
C#
269 lines
9.4 KiB
C#
|
using System;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.Xml;
|
|||
|
using Aitex.Core.Common.DeviceData;
|
|||
|
using Aitex.Core.RT.DataCenter;
|
|||
|
using Aitex.Core.RT.Event;
|
|||
|
using Aitex.Core.RT.IOCore;
|
|||
|
using Aitex.Core.RT.Log;
|
|||
|
using Aitex.Core.RT.Tolerance;
|
|||
|
using Aitex.Core.Util;
|
|||
|
using MECF.Framework.Common.Event;
|
|||
|
using MECF.Framework.Common.SicMath;
|
|||
|
|
|||
|
namespace Aitex.Core.RT.Device.Devices
|
|||
|
{
|
|||
|
public class IoSensorAnalog : BaseDevice, IDevice
|
|||
|
{
|
|||
|
#region Variables
|
|||
|
|
|||
|
private readonly AIAccessor _ai;
|
|||
|
private readonly AOAccessor _ao;
|
|||
|
private readonly DOAccessor _doWarningLow;
|
|||
|
private readonly DOAccessor _doWarningHigh;
|
|||
|
private readonly DOAccessor _doAlarmLow;
|
|||
|
private readonly DOAccessor _doAlarmHigh;
|
|||
|
|
|||
|
private readonly TimeDomainArithmeticMeanFilter _tdamFilter;
|
|||
|
private readonly R_TRIG _trigTextOut = new R_TRIG();
|
|||
|
|
|||
|
private readonly string _scWarningLow;
|
|||
|
private readonly string _scWarningHigh;
|
|||
|
private readonly string _scAlarmLow;
|
|||
|
private readonly string _scAlarmHigh;
|
|||
|
|
|||
|
private readonly int _warningDelaySec;
|
|||
|
private readonly int _alarmDelaySec;
|
|||
|
|
|||
|
protected double _filterDurationSec;
|
|||
|
protected double _warningLow;
|
|||
|
protected double _warningHigh;
|
|||
|
protected double _alarmLow;
|
|||
|
protected double _alarmHigh;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 误差超限警告检查器
|
|||
|
/// </summary>
|
|||
|
protected readonly ToleranceChecker _warningChecker;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 误差超限报警检查器
|
|||
|
/// </summary>
|
|||
|
protected readonly ToleranceChecker _alarmChecker;
|
|||
|
|
|||
|
private readonly AlarmEventItem _alarmEvent;
|
|||
|
private readonly AlarmEventItem _warningEvent;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
protected string Unit
|
|||
|
{
|
|||
|
get; set;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取Sensor实时反馈值。
|
|||
|
/// <para>注意:如果绑定的AI或者AO为空,返回<see cref="Double.NaN"/></para>
|
|||
|
/// </summary>
|
|||
|
public double Value
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (_ai != null)
|
|||
|
return _ai.Value;
|
|||
|
|
|||
|
if (_ao != null)
|
|||
|
return _ao.Value;
|
|||
|
|
|||
|
return double.NaN;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private AITSensorAnalogData DeviceData
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
AITSensorAnalogData data = new AITSensorAnalogData()
|
|||
|
{
|
|||
|
DeviceName = Name,
|
|||
|
DeviceSchematicId = DeviceID,
|
|||
|
DisplayName = Display,
|
|||
|
WarningLimitLow = _warningLow,
|
|||
|
WarningLimitHigh = _warningHigh,
|
|||
|
AlarmLimitLow = _alarmLow,
|
|||
|
AlarmLimitHigh = _alarmHigh,
|
|||
|
Value = Value,
|
|||
|
};
|
|||
|
|
|||
|
return data;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
public IoSensorAnalog(string module, XmlElement node, string ioModule = "")
|
|||
|
{
|
|||
|
var attrModule = node.GetAttribute("module");
|
|||
|
Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;
|
|||
|
Name = node.GetAttribute("id");
|
|||
|
Unit = node.GetAttribute("unit");
|
|||
|
Display = node.GetAttribute("display");
|
|||
|
DeviceID = node.GetAttribute("schematicId");
|
|||
|
|
|||
|
// 映射到系统配置中的节点的名称
|
|||
|
ScBasePath = node.GetAttribute("scBase");
|
|||
|
_scWarningLow = node.GetAttribute("scWarningLow");
|
|||
|
_scWarningHigh = node.GetAttribute("scWarningHigh");
|
|||
|
_scAlarmLow = node.GetAttribute("scAlarmLow");
|
|||
|
_scAlarmHigh = node.GetAttribute("scAlarmHigh");
|
|||
|
|
|||
|
// 读取滤波时长设定
|
|||
|
if (double.TryParse(node.GetAttribute("filterTime"), out var dblFilter))
|
|||
|
_filterDurationSec = dblFilter;
|
|||
|
_filterDurationSec = 0;
|
|||
|
|
|||
|
_ai = ParseAiNode("ai", node, ioModule);
|
|||
|
_ao = ParseAoNode("ao", node, ioModule);
|
|||
|
_doWarningLow = ParseDoNode("doWarningLow", node, ioModule);
|
|||
|
_doWarningHigh = ParseDoNode("doWarningHigh", node, ioModule);
|
|||
|
_doAlarmLow = ParseDoNode("doAlarmLow", node, ioModule);
|
|||
|
_doAlarmHigh = ParseDoNode("doAlarmHigh", node, ioModule);
|
|||
|
|
|||
|
// AI和AO不能同时配置
|
|||
|
Debug.Assert((_ai != null && _ao == null) || (_ai == null && _ao != null), "Incorrect configuration.");
|
|||
|
|
|||
|
_warningLow = ParseScNode("", node, ioModule, $"{ScBasePath}.{_scWarningLow}")?.DoubleValue ??
|
|||
|
double.MinValue;
|
|||
|
_warningHigh = ParseScNode("", node, ioModule, $"{ScBasePath}.{_scWarningHigh}")?.DoubleValue ??
|
|||
|
double.MaxValue;
|
|||
|
|
|||
|
_alarmLow = ParseScNode("", node, ioModule, $"{ScBasePath}.{_scAlarmLow}")?.DoubleValue ??
|
|||
|
double.MinValue;
|
|||
|
_alarmHigh = ParseScNode("", node, ioModule, $"{ScBasePath}.{_scAlarmHigh}")?.DoubleValue ??
|
|||
|
double.MaxValue;
|
|||
|
|
|||
|
_warningDelaySec = ParseScNode("", node, ioModule, $"{ScBasePath}.WarningDelay")?.IntValue ??
|
|||
|
0;
|
|||
|
|
|||
|
_alarmDelaySec = ParseScNode("", node, ioModule, $"{ScBasePath}.AlarmDelay")?.IntValue ??
|
|||
|
0;
|
|||
|
|
|||
|
_warningChecker = new ToleranceChecker();
|
|||
|
_alarmChecker = new ToleranceChecker();
|
|||
|
_warningEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceWarning", "", ResetWarningChecker,
|
|||
|
EventLevel.Warning);
|
|||
|
_alarmEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceAlarm", "", ResetAlarmChecker);
|
|||
|
|
|||
|
// 初始化滤波器
|
|||
|
_tdamFilter = new TimeDomainArithmeticMeanFilter(_filterDurationSec);
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool Initialize()
|
|||
|
{
|
|||
|
DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData);
|
|||
|
DATA.Subscribe($"{Module}.{Name}.Value", () => Value);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
public virtual void Terminate()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
public virtual void Monitor()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
// 对输入量滤波
|
|||
|
_tdamFilter.Feed(Value);
|
|||
|
var filtered = _tdamFilter.Filter();
|
|||
|
|
|||
|
_alarmChecker.Monitor(filtered, _alarmLow, _alarmHigh, _alarmDelaySec);
|
|||
|
if (_alarmChecker.Trig)
|
|||
|
{
|
|||
|
_alarmEvent.Description =
|
|||
|
$"{Display} feedback ({filtered:F1}{Unit}) out of range {_alarmLow:F1} - {_alarmHigh:F1} {Unit} in {_alarmDelaySec:F0} seconds";
|
|||
|
_alarmEvent.Set();
|
|||
|
|
|||
|
// 根据条件设定指定的DO
|
|||
|
if (filtered < _alarmLow)
|
|||
|
{
|
|||
|
_doAlarmLow?.SetValue(true, out _);
|
|||
|
_doAlarmHigh?.SetValue(false, out _);
|
|||
|
}
|
|||
|
else if (filtered > _alarmHigh)
|
|||
|
{
|
|||
|
_doAlarmLow?.SetValue(false, out _);
|
|||
|
_doAlarmHigh?.SetValue(true, out _);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 如果报Alarm,不再检查Warning。
|
|||
|
if (_alarmEvent.IsTriggered)
|
|||
|
return;
|
|||
|
|
|||
|
// Out-of-rang warning detection
|
|||
|
_warningChecker.Monitor(filtered, _warningLow, _warningHigh, _warningDelaySec);
|
|||
|
if (_warningChecker.Trig)
|
|||
|
{
|
|||
|
_warningEvent.Description =
|
|||
|
$"{Display} feedback ({filtered:F1}{Unit}) out of range {_warningLow:F1} - {_warningHigh:F1} {Unit} in {_warningDelaySec:F0} seconds";
|
|||
|
_warningEvent.Set();
|
|||
|
|
|||
|
// 根据条件设定指定的DO
|
|||
|
if (filtered < _warningLow)
|
|||
|
{
|
|||
|
_doWarningLow?.SetValue(true, out _);
|
|||
|
_doWarningHigh?.SetValue(false, out _);
|
|||
|
}
|
|||
|
else if (filtered > _warningHigh)
|
|||
|
{
|
|||
|
_doWarningLow?.SetValue(false, out _);
|
|||
|
_doWarningHigh?.SetValue(true, out _);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LOG.Write(ex);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual void Reset()
|
|||
|
{
|
|||
|
_warningChecker.Reset(_warningDelaySec);
|
|||
|
_alarmChecker.Reset(_alarmDelaySec);
|
|||
|
_warningEvent.Reset();
|
|||
|
_alarmEvent.Reset();
|
|||
|
|
|||
|
_doAlarmLow?.SetValue(false, out _);
|
|||
|
_doAlarmHigh?.SetValue(false, out _);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#region Private Methods
|
|||
|
|
|||
|
|
|||
|
private bool ResetWarningChecker()
|
|||
|
{
|
|||
|
_warningChecker.Reset(_warningDelaySec);
|
|||
|
_doWarningLow?.SetValue(false, out _);
|
|||
|
_doWarningHigh?.SetValue(false, out _);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
private bool ResetAlarmChecker()
|
|||
|
{
|
|||
|
_alarmChecker.Reset(_alarmDelaySec);
|
|||
|
_doAlarmLow?.SetValue(false, out _);
|
|||
|
_doAlarmHigh?.SetValue(false, out _);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|