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

283 lines
11 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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