Sic.Framework-Nanjing-Baishi/MECF.Framework.RT.Equipment.../Devices/IoSensorAnalog.cs

283 lines
11 KiB
C#
Raw Normal View History

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
}
}