2023-04-17 17:05:49 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Xml;
|
|
|
|
|
using Aitex.Core.RT.Device;
|
|
|
|
|
using Aitex.Core.RT.Event;
|
|
|
|
|
using Aitex.Core.RT.IOCore;
|
2023-08-25 16:05:19 +08:00
|
|
|
|
using Aitex.Core.RT.Log;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
using Aitex.Core.RT.SCCore;
|
|
|
|
|
using Aitex.Core.RT.Tolerance;
|
|
|
|
|
using Aitex.Core.Util;
|
|
|
|
|
using MECF.Framework.Common.Event;
|
|
|
|
|
|
|
|
|
|
namespace MECF.Framework.Common.Aitex.Core.RT.Device
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 具有SetPoint、FeedBack误差实时检测功能的设备基类。
|
|
|
|
|
/// <para>注意:在子类中注册FeedbackGetter、SetPointGetter、RampTimerGetter</para>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class ErrorDetectableBaseDevice : BaseDevice, IDevice
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
#region Variable
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
private readonly R_TRIG _alarmTrigger;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 是否启用误差检测
|
|
|
|
|
/// </summary>
|
2023-08-08 11:24:51 +08:00
|
|
|
|
private bool _isEnableErrDetection;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 误差报警超限阈值
|
|
|
|
|
/// </summary>
|
2023-08-08 11:24:51 +08:00
|
|
|
|
private double _alarmRange;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 误差报警超限持续时间
|
|
|
|
|
/// </summary>
|
2023-08-08 11:24:51 +08:00
|
|
|
|
private double _alarmTime;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 误差警告超限阈值
|
|
|
|
|
/// </summary>
|
2023-08-08 11:24:51 +08:00
|
|
|
|
private double _warningRange;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 误差警告超限持续时间
|
|
|
|
|
/// </summary>
|
2023-08-08 11:24:51 +08:00
|
|
|
|
private double _warningTime;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
|
|
|
|
|
protected readonly DIAccessor DiAlarm;
|
|
|
|
|
protected readonly string AlarmText;
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 误差超限警告检查器
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly ToleranceChecker ErrWarningChecker;
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 误差超限报警检查器
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly ToleranceChecker ErrAlarmChecker;
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
private readonly AlarmEventItem _alarmEvent;
|
|
|
|
|
private readonly AlarmEventItem _warningEvent;
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
2023-04-19 15:38:04 +08:00
|
|
|
|
public ErrorDetectableBaseDevice(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
|
2023-04-17 17:05:49 +08:00
|
|
|
|
{
|
|
|
|
|
// 获取diAlarm
|
|
|
|
|
DiAlarm = ParseDiNode("diAlarm", node, ioModule);
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
// 获取Alarm Text
|
|
|
|
|
AlarmText = node.GetAttribute("AlarmText");
|
|
|
|
|
|
|
|
|
|
_isEnableErrDetection =
|
2023-04-19 15:38:04 +08:00
|
|
|
|
ParseScNode("scEnableAlarm", node, ioModule, $"{ScBasePath}.{Display}.EnableAlarm")?.BoolValue ?? false;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
_alarmRange =
|
2023-04-19 15:38:04 +08:00
|
|
|
|
ParseScNode("scAlarmRange", node, ioModule, $"{ScBasePath}.AlarmRange")?.DoubleValue ??
|
2023-04-17 17:05:49 +08:00
|
|
|
|
double.NaN;
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_alarmTime = ParseScNode("scAlarmTime", node, ioModule, $"{ScBasePath}.AlarmTime")?.IntValue ??
|
2023-04-17 17:05:49 +08:00
|
|
|
|
int.MinValue;
|
|
|
|
|
|
2023-04-19 15:38:04 +08:00
|
|
|
|
_warningRange = SC.GetConfigItem($"{ScBasePath}.WarningRange")?.DoubleValue ?? double.NaN;
|
|
|
|
|
_warningTime = SC.GetConfigItem($"{ScBasePath}.WarningTime")?.IntValue ?? int.MinValue;
|
2023-04-17 17:05:49 +08:00
|
|
|
|
|
|
|
|
|
_alarmTrigger = new R_TRIG();
|
|
|
|
|
ErrWarningChecker = new ToleranceChecker();
|
|
|
|
|
ErrAlarmChecker = new ToleranceChecker();
|
|
|
|
|
|
|
|
|
|
_warningEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceWarning", "", ResetWarningChecker,
|
|
|
|
|
EventLevel.Warning);
|
|
|
|
|
_alarmEvent = SubscribeAlarm($"{Module}.{Display}.ToleranceAlarm", "", ResetAlarmChecker);
|
|
|
|
|
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-08-25 16:05:19 +08:00
|
|
|
|
SC.RegisterValueChangedCallback($"{ScBasePath}.{Display}.EnableAlarm", (newValue) =>
|
|
|
|
|
{
|
|
|
|
|
if (!bool.TryParse(newValue.ToString(), out _isEnableErrDetection))
|
|
|
|
|
{
|
|
|
|
|
_isEnableErrDetection = false;
|
|
|
|
|
LOG.Error($"Unable to convert {ScBasePath}.{Display}.EnableAlarm value {newValue} to bool");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
SC.RegisterValueChangedCallback($"{ScBasePath}.AlarmRange", (newValue) =>
|
|
|
|
|
{
|
|
|
|
|
if (!double.TryParse(newValue.ToString(), out _alarmRange))
|
|
|
|
|
{
|
|
|
|
|
_alarmRange = double.NaN;
|
|
|
|
|
LOG.Error($"Unable to convert {ScBasePath}.{Display}.AlarmRange value {newValue} to double");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
SC.RegisterValueChangedCallback($"{ScBasePath}.AlarmTime", (newValue) =>
|
|
|
|
|
{
|
|
|
|
|
if (!double.TryParse(newValue.ToString(), out _alarmTime))
|
|
|
|
|
{
|
|
|
|
|
_alarmTime = int.MinValue;
|
|
|
|
|
LOG.Error($"Unable to convert {ScBasePath}.{Display}.AlarmRange value {newValue} to double");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
SC.RegisterValueChangedCallback($"{ScBasePath}.WarningRange", (newValue) =>
|
|
|
|
|
{
|
|
|
|
|
if (!double.TryParse(newValue.ToString(), out _warningRange))
|
|
|
|
|
{
|
|
|
|
|
_warningRange = double.NaN;
|
|
|
|
|
LOG.Error($"Unable to convert {ScBasePath}.{Display}.WarningRange value {newValue} to double");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
SC.RegisterValueChangedCallback($"{ScBasePath}.WarningTime", (newValue) =>
|
|
|
|
|
{
|
|
|
|
|
if (!double.TryParse(newValue.ToString(), out _warningTime))
|
|
|
|
|
{
|
|
|
|
|
_warningTime = int.MinValue;
|
|
|
|
|
LOG.Error($"Unable to convert {ScBasePath}.{Display}.WarningTime value {newValue} to double");
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-04-17 17:05:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
|
|
|
|
private bool ResetWarningChecker()
|
|
|
|
|
{
|
|
|
|
|
ErrWarningChecker.Reset(_warningTime);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool ResetAlarmChecker()
|
|
|
|
|
{
|
|
|
|
|
ErrAlarmChecker.Reset(_alarmTime);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 监测设置值和反馈值误差
|
|
|
|
|
/// </summary>
|
|
|
|
|
public virtual void MonitorSpFbError(bool allowErrorCheck, double setPoint, double feedback)
|
|
|
|
|
{
|
|
|
|
|
_alarmTrigger.CLK = DiAlarm != null && DiAlarm.Value;
|
|
|
|
|
if (_alarmTrigger.Q)
|
|
|
|
|
{
|
|
|
|
|
EV.PostAlarmLog(Module, AlarmText);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_isEnableErrDetection || !allowErrorCheck)
|
|
|
|
|
{
|
|
|
|
|
ErrWarningChecker.RST = true;
|
|
|
|
|
ErrAlarmChecker.RST = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Out-of-rang Alarm detection
|
|
|
|
|
var alarmLow = (setPoint * Math.Abs(100 - _alarmRange) / 100);
|
|
|
|
|
var alarmHigh = (setPoint * Math.Abs(100 + _alarmRange) / 100);
|
|
|
|
|
ErrAlarmChecker.Monitor(feedback, alarmLow, alarmHigh, _alarmTime);
|
|
|
|
|
if (ErrAlarmChecker.Trig)
|
|
|
|
|
{
|
|
|
|
|
_alarmEvent.Description = $"{Display} feedback ({feedback:F1}{Unit}) out of range {alarmLow} - {alarmHigh} {Unit} in {_alarmTime:F0} seconds";
|
|
|
|
|
_alarmEvent.Set();
|
|
|
|
|
}
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
// 如果报Alarm,不再检查Warning。
|
|
|
|
|
if (_alarmEvent.IsTriggered)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Out-of-rang warning detection
|
|
|
|
|
var warnLow = (setPoint * Math.Abs(100 - _warningRange) / 100);
|
|
|
|
|
var warnHigh = (setPoint * Math.Abs(100 + _warningRange) / 100);
|
|
|
|
|
|
|
|
|
|
ErrWarningChecker.Monitor(feedback, warnLow, warnHigh, _warningTime);
|
|
|
|
|
if (ErrWarningChecker.Trig)
|
|
|
|
|
{
|
|
|
|
|
_warningEvent.Description = $"{Display} feedback ({feedback:F1}{Unit}) out of range {warnLow} - {warnHigh} {Unit} in {_warningTime:F0} seconds";
|
|
|
|
|
_warningEvent.Set();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2023-08-25 16:05:19 +08:00
|
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
2023-04-17 17:05:49 +08:00
|
|
|
|
public virtual bool Initialize()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 16:05:19 +08:00
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
public new void Monitor()
|
2023-04-17 17:05:49 +08:00
|
|
|
|
{
|
2023-08-25 16:05:19 +08:00
|
|
|
|
base.Monitor();
|
2023-04-17 17:05:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 16:05:19 +08:00
|
|
|
|
/// <inheritdoc/>
|
2023-04-17 17:05:49 +08:00
|
|
|
|
public virtual void Terminate()
|
|
|
|
|
{
|
2023-08-08 11:24:51 +08:00
|
|
|
|
|
2023-04-17 17:05:49 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 16:05:19 +08:00
|
|
|
|
/// <inheritdoc />
|
2023-04-17 17:05:49 +08:00
|
|
|
|
public virtual void Reset()
|
|
|
|
|
{
|
|
|
|
|
_alarmTrigger.RST = true;
|
|
|
|
|
ErrAlarmChecker.Reset(_alarmTime);
|
|
|
|
|
ErrWarningChecker.Reset(_warningTime);
|
|
|
|
|
_warningEvent.Reset();
|
|
|
|
|
_alarmEvent.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|