2023-04-18 15:49:08 +08:00
|
|
|
|
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 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 string _scWarningLow;
|
|
|
|
|
private readonly string _scWarningHigh;
|
|
|
|
|
private readonly string _scAlarmLow;
|
|
|
|
|
private readonly string _scAlarmHigh;
|
2023-04-19 15:38:04 +08:00
|
|
|
|
private readonly string _scFilterDuration;
|
2023-04-18 15:49:08 +08:00
|
|
|
|
|
|
|
|
|
private readonly int _warningDelaySec;
|
|
|
|
|
private readonly int _alarmDelaySec;
|
2023-04-19 15:38:04 +08:00
|
|
|
|
|
2023-04-18 15:49:08 +08:00
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取Sensor实时反馈值。
|
|
|
|
|
/// <para>注意:如果绑定的AI或者AO为空,返回<see cref="Double.NaN"/></para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public double Value
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_ai != null)
|
2023-04-19 15:38:04 +08:00
|
|
|
|
return _ai.FloatValue;
|
2023-04-18 15:49:08 +08:00
|
|
|
|
|
|
|
|
|
if (_ao != null)
|
2023-04-19 15:38:04 +08:00
|
|
|
|
return _ao.FloatValue;
|
2023-04-18 15:49:08 +08:00
|
|
|
|
|
|
|
|
|
return double.NaN;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private AITSensorAnalogData DeviceData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2023-04-19 15:38:04 +08:00
|
|
|
|
var data = new AITSensorAnalogData()
|
2023-04-18 15:49:08 +08:00
|
|
|
|
{
|
|
|
|
|
DeviceName = Name,
|
|
|
|
|
DeviceSchematicId = DeviceID,
|
|
|
|
|
DisplayName = Display,
|
|
|
|
|
WarningLimitLow = _warningLow,
|
|
|
|
|
WarningLimitHigh = _warningHigh,
|
|
|
|
|
AlarmLimitLow = _alarmLow,
|
|
|
|
|
AlarmLimitHigh = _alarmHigh,
|
|
|
|
|
Value = Value,
|
2023-04-19 15:38:04 +08:00
|
|
|
|
IsWarning = _warningEvent.IsTriggered,
|
|
|
|
|
IsAlarm = _alarmEvent.IsTriggered
|
2023-04-18 15:49:08 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-04-19 15:38:04 +08:00
|
|
|
|
public IoSensorAnalog(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
|
2023-04-18 15:49:08 +08:00
|
|
|
|
{
|
|
|
|
|
_scWarningLow = node.GetAttribute("scWarningLow");
|
|
|
|
|
_scWarningHigh = node.GetAttribute("scWarningHigh");
|
|
|
|
|
_scAlarmLow = node.GetAttribute("scAlarmLow");
|
|
|
|
|
_scAlarmHigh = node.GetAttribute("scAlarmHigh");
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_scFilterDuration = node.GetAttribute("scFilterDuration");
|
2023-04-18 15:49:08 +08:00
|
|
|
|
|
2023-04-19 15:38:04 +08:00
|
|
|
|
|
2023-04-18 15:49:08 +08:00
|
|
|
|
|
|
|
|
|
_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);
|
|
|
|
|
|
|
|
|
|
// 初始化滤波器
|
2023-04-19 15:38:04 +08:00
|
|
|
|
var filterDurationSec = ParseScNode("", node, ioModule, $"{ScBasePath}.{_scFilterDuration}")?.IntValue ??
|
|
|
|
|
0;
|
|
|
|
|
_tdamFilter = new TimeDomainArithmeticMeanFilter(filterDurationSec);
|
2023-04-18 15:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2023-04-19 15:38:04 +08:00
|
|
|
|
|
2023-04-18 15:49:08 +08:00
|
|
|
|
_alarmChecker.Monitor(filtered, _alarmLow, _alarmHigh, _alarmDelaySec);
|
|
|
|
|
if (_alarmChecker.Trig)
|
|
|
|
|
{
|
|
|
|
|
// 根据条件设定指定的DO
|
|
|
|
|
if (filtered < _alarmLow)
|
|
|
|
|
{
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_alarmEvent.Description =
|
|
|
|
|
$"{Display} feedback ({filtered:F1}{Unit}) is below {_alarmLow:F1} in {_alarmDelaySec:F0} seconds";
|
2023-04-18 15:49:08 +08:00
|
|
|
|
_doAlarmLow?.SetValue(true, out _);
|
|
|
|
|
_doAlarmHigh?.SetValue(false, out _);
|
|
|
|
|
}
|
|
|
|
|
else if (filtered > _alarmHigh)
|
|
|
|
|
{
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_alarmEvent.Description =
|
|
|
|
|
$"{Display} feedback ({filtered:F1}{Unit}) is above {_alarmHigh:F1} in {_alarmDelaySec:F0} seconds";
|
2023-04-18 15:49:08 +08:00
|
|
|
|
_doAlarmLow?.SetValue(false, out _);
|
|
|
|
|
_doAlarmHigh?.SetValue(true, out _);
|
|
|
|
|
}
|
2023-04-19 15:38:04 +08:00
|
|
|
|
|
|
|
|
|
_alarmEvent.Set();
|
2023-04-18 15:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果报Alarm,不再检查Warning。
|
|
|
|
|
if (_alarmEvent.IsTriggered)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Out-of-rang warning detection
|
|
|
|
|
_warningChecker.Monitor(filtered, _warningLow, _warningHigh, _warningDelaySec);
|
|
|
|
|
if (_warningChecker.Trig)
|
|
|
|
|
{
|
|
|
|
|
// 根据条件设定指定的DO
|
|
|
|
|
if (filtered < _warningLow)
|
|
|
|
|
{
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_warningEvent.Description =
|
|
|
|
|
$"{Display} feedback ({filtered:F1}{Unit}) is below {_warningLow:F1} in {_warningDelaySec:F0} seconds";
|
2023-04-18 15:49:08 +08:00
|
|
|
|
_doWarningLow?.SetValue(true, out _);
|
|
|
|
|
_doWarningHigh?.SetValue(false, out _);
|
|
|
|
|
}
|
|
|
|
|
else if (filtered > _warningHigh)
|
|
|
|
|
{
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_warningEvent.Description =
|
|
|
|
|
$"{Display} feedback ({filtered:F1}{Unit}) is above {_warningHigh:F1} in {_warningDelaySec:F0} seconds";
|
2023-04-18 15:49:08 +08:00
|
|
|
|
_doWarningLow?.SetValue(false, out _);
|
|
|
|
|
_doWarningHigh?.SetValue(true, out _);
|
|
|
|
|
}
|
2023-04-19 15:38:04 +08:00
|
|
|
|
|
|
|
|
|
_warningEvent.Set();
|
2023-04-18 15:49:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
}
|