283 lines
11 KiB
C#
283 lines
11 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.SCCore;
|
||
using Aitex.Core.RT.Tolerance;
|
||
using MECF.Framework.Common.Event;
|
||
using MECF.Framework.Common.SicMath;
|
||
|
||
namespace Aitex.Core.RT.Device.Devices
|
||
{
|
||
public abstract 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 bool _doActiveLevel;
|
||
|
||
private readonly TimeDomainArithmeticMeanFilter _tdamFilter;
|
||
|
||
private int _warningDelaySec;
|
||
private int _alarmDelaySec;
|
||
|
||
protected readonly string _scNameWarnLow;
|
||
protected readonly string _scNameWarnHigh;
|
||
protected readonly string _scNameAlarmLow;
|
||
protected readonly string _scNameAlarmHigh;
|
||
protected readonly string _scNameWarnDelay;
|
||
protected readonly string _scNameAlarmDelay;
|
||
protected readonly string _scNameFilterDuration;
|
||
|
||
protected double _warningLow;
|
||
protected double _warningHigh;
|
||
protected double _alarmLow;
|
||
protected double _alarmHigh;
|
||
protected int _filterDurationS;
|
||
|
||
/// <summary>
|
||
/// 误差超限警告检查器
|
||
/// </summary>
|
||
protected readonly ToleranceChecker _warningChecker;
|
||
|
||
/// <summary>
|
||
/// 误差超限报警检查器
|
||
/// </summary>
|
||
protected readonly ToleranceChecker _alarmChecker;
|
||
|
||
protected readonly AlarmEventItem _alarmEvent;
|
||
protected readonly AlarmEventItem _warningEvent;
|
||
|
||
private readonly AITSensorAnalogData _devData;
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 获取Sensor实时反馈值。
|
||
/// <para>注意:如果绑定的AI或者AO为空,返回<see cref="Double.NaN"/></para>
|
||
/// </summary>
|
||
public double Value
|
||
{
|
||
get
|
||
{
|
||
if (_ai != null)
|
||
return _ai.FloatValue;
|
||
|
||
if (_ao != null)
|
||
return _ao.FloatValue;
|
||
|
||
return double.NaN;
|
||
}
|
||
}
|
||
|
||
protected AITSensorAnalogData DeviceData
|
||
{
|
||
get
|
||
{
|
||
_devData.DeviceName = Name;
|
||
_devData.DeviceSchematicId = DeviceID;
|
||
_devData.DisplayName = Display;
|
||
_devData.WarningLimitLow = _warningLow;
|
||
_devData.WarningLimitHigh = _warningHigh;
|
||
_devData.AlarmLimitLow = _alarmLow;
|
||
_devData.AlarmLimitHigh = _alarmHigh;
|
||
_devData.Value = Value;
|
||
_devData.IsWarning = _warningEvent.IsTriggered;
|
||
_devData.IsAlarm = _alarmEvent.IsTriggered;
|
||
return _devData;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
public IoSensorAnalog(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
|
||
{
|
||
_devData = new AITSensorAnalogData();
|
||
|
||
var scWarningLow = node.GetAttribute("scWarningLow");
|
||
var scWarningHigh = node.GetAttribute("scWarningHigh");
|
||
var scAlarmLow = node.GetAttribute("scAlarmLow");
|
||
var scAlarmHigh = node.GetAttribute("scAlarmHigh");
|
||
var scFilterDuration = node.GetAttribute("scFilterDuration");
|
||
|
||
_scNameWarnLow = $"{ScBasePath}.{scWarningLow}";
|
||
_scNameWarnHigh = $"{ScBasePath}.{scWarningHigh}";
|
||
_scNameAlarmLow = $"{ScBasePath}.{scAlarmLow}";
|
||
_scNameAlarmHigh = $"{ScBasePath}.{scAlarmHigh}";
|
||
_scNameWarnDelay = $"{ScBasePath}.WarningDelay";
|
||
_scNameAlarmDelay = $"{ScBasePath}.AlarmDelay";
|
||
_scNameFilterDuration = $"{ScBasePath}.{scFilterDuration}";
|
||
|
||
_ai = ParseAiNode("ai", node, ioModule);
|
||
_ao = ParseAoNode("ao", node, ioModule);
|
||
|
||
// DO输出的有效电平,当Warn或Alarm触发时,相应的DO输出此电平
|
||
var activeLevel = node.GetAttribute("doActiveLevel");
|
||
if (bool.TryParse(activeLevel, out var bLevel))
|
||
_doActiveLevel = bLevel;
|
||
else
|
||
_doActiveLevel = true;
|
||
|
||
_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), $"{Name},{Display} Incorrect configuration.");
|
||
|
||
LoadConfig();
|
||
|
||
_warningChecker = new ToleranceChecker();
|
||
_alarmChecker = new ToleranceChecker();
|
||
_warningEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceWarning", "", ResetWarningChecker,
|
||
EventLevel.Warning);
|
||
_alarmEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceAlarm", "", ResetAlarmChecker);
|
||
|
||
_tdamFilter = new TimeDomainArithmeticMeanFilter(_filterDurationS);
|
||
}
|
||
|
||
public virtual bool Initialize()
|
||
{
|
||
// DATA.Subscribe($"{Module}.{Name}.Value", () => Value);
|
||
return true;
|
||
}
|
||
|
||
public virtual void Terminate()
|
||
{
|
||
}
|
||
|
||
protected override void HandleMonitor()
|
||
{
|
||
try
|
||
{
|
||
// 对输入量滤波
|
||
_tdamFilter.Feed(Value);
|
||
var filtered = _tdamFilter.Filter();
|
||
|
||
_alarmChecker.Monitor(filtered, _alarmLow, _alarmHigh, _alarmDelaySec);
|
||
if (_alarmChecker.Trig)
|
||
{
|
||
// 根据条件设定指定的DO
|
||
if (filtered < _alarmLow)
|
||
{
|
||
_alarmEvent.Description =
|
||
$"{Display} feedback ({filtered:F1}{Unit}) is below {_alarmLow:F1} in {_alarmDelaySec:F0} seconds";
|
||
_doAlarmLow?.SetValue(_doActiveLevel, out _);
|
||
_doAlarmHigh?.SetValue(!_doActiveLevel, out _);
|
||
}
|
||
else if (filtered > _alarmHigh)
|
||
{
|
||
_alarmEvent.Description =
|
||
$"{Display} feedback ({filtered:F1}{Unit}) is above {_alarmHigh:F1} in {_alarmDelaySec:F0} seconds";
|
||
_doAlarmLow?.SetValue(!_doActiveLevel, out _);
|
||
_doAlarmHigh?.SetValue(_doActiveLevel, out _);
|
||
}
|
||
|
||
_alarmEvent.Set();
|
||
}
|
||
|
||
// 如果报Alarm,不再检查Warning。
|
||
if (_alarmEvent.IsTriggered)
|
||
return;
|
||
|
||
// Out-of-rang warning detection
|
||
_warningChecker.Monitor(filtered, _warningLow, _warningHigh, _warningDelaySec);
|
||
if (_warningChecker.Trig)
|
||
{
|
||
// 根据条件设定指定的DO
|
||
if (filtered < _warningLow)
|
||
{
|
||
_warningEvent.Description =
|
||
$"{Display} feedback ({filtered:F1}{Unit}) is below {_warningLow:F1} in {_warningDelaySec:F0} seconds";
|
||
_doWarningLow?.SetValue(_doActiveLevel, out _);
|
||
_doWarningHigh?.SetValue(!_doActiveLevel, out _);
|
||
}
|
||
else if (filtered > _warningHigh)
|
||
{
|
||
_warningEvent.Description =
|
||
$"{Display} feedback ({filtered:F1}{Unit}) is above {_warningHigh:F1} in {_warningDelaySec:F0} seconds";
|
||
_doWarningLow?.SetValue(!_doActiveLevel, out _);
|
||
_doWarningHigh?.SetValue(_doActiveLevel, out _);
|
||
}
|
||
|
||
_warningEvent.Set();
|
||
}
|
||
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LOG.Write(ex);
|
||
|
||
}
|
||
}
|
||
|
||
public virtual void Reset()
|
||
{
|
||
_warningChecker.Reset(_warningDelaySec);
|
||
_alarmChecker.Reset(_alarmDelaySec);
|
||
_warningEvent.Reset();
|
||
_alarmEvent.Reset();
|
||
|
||
_doAlarmLow?.SetValue(!_doActiveLevel, out _);
|
||
_doAlarmHigh?.SetValue(!_doActiveLevel, out _);
|
||
|
||
|
||
}
|
||
|
||
#region Private Methods
|
||
|
||
|
||
private bool ResetWarningChecker()
|
||
{
|
||
_warningChecker.Reset(_warningDelaySec);
|
||
_doWarningLow?.SetValue(!_doActiveLevel, out _);
|
||
_doWarningHigh?.SetValue(!_doActiveLevel, out _);
|
||
return true;
|
||
}
|
||
|
||
private bool ResetAlarmChecker()
|
||
{
|
||
_alarmChecker.Reset(_alarmDelaySec);
|
||
_doAlarmLow?.SetValue(!_doActiveLevel, out _);
|
||
_doAlarmHigh?.SetValue(!_doActiveLevel, out _);
|
||
return true;
|
||
}
|
||
|
||
protected virtual void LoadConfig()
|
||
{
|
||
_warningLow = SC.SafeGetValue(_scNameWarnLow, double.NegativeInfinity);
|
||
_warningHigh = SC.SafeGetValue(_scNameWarnHigh, double.PositiveInfinity);
|
||
_alarmLow = SC.SafeGetValue(_scNameAlarmLow, double.NegativeInfinity);
|
||
_alarmHigh = SC.SafeGetValue(_scNameAlarmHigh, double.PositiveInfinity);
|
||
_warningDelaySec = SC.SafeGetValue(_scNameWarnDelay, 0);
|
||
_alarmDelaySec = SC.SafeGetValue(_scNameAlarmDelay, 0);
|
||
_filterDurationS = SC.SafeGetValue(_scNameFilterDuration, 0);
|
||
|
||
// 当设置值变化时自动重载
|
||
SC.RegisterValueChangedCallback(_scNameWarnLow, v => _warningLow = (double)v);
|
||
SC.RegisterValueChangedCallback(_scNameWarnHigh, v => _warningHigh = (double)v);
|
||
SC.RegisterValueChangedCallback(_scNameAlarmLow, v => _alarmLow = (double)v);
|
||
SC.RegisterValueChangedCallback(_scNameAlarmHigh, v => _alarmHigh = (double)v);
|
||
SC.RegisterValueChangedCallback(_scNameWarnDelay, v => _warningDelaySec = (int)v);
|
||
SC.RegisterValueChangedCallback(_scNameAlarmDelay, v => _alarmDelaySec = (int)v);
|
||
SC.RegisterValueChangedCallback(_scNameFilterDuration, v =>
|
||
{
|
||
_filterDurationS = (int)v;
|
||
_tdamFilter.FilterDurationInSec = _filterDurationS;
|
||
});
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |