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; private readonly string _scFilterDuration; private readonly int _warningDelaySec; private readonly int _alarmDelaySec; protected double _warningLow; protected double _warningHigh; protected double _alarmLow; protected double _alarmHigh; /// /// 误差超限警告检查器 /// protected readonly ToleranceChecker _warningChecker; /// /// 误差超限报警检查器 /// protected readonly ToleranceChecker _alarmChecker; private readonly AlarmEventItem _alarmEvent; private readonly AlarmEventItem _warningEvent; #endregion /// /// 获取Sensor实时反馈值。 /// 注意:如果绑定的AI或者AO为空,返回 /// public double Value { get { if (_ai != null) return _ai.FloatValue; if (_ao != null) return _ao.FloatValue; return double.NaN; } } private AITSensorAnalogData DeviceData { get { var data = new AITSensorAnalogData() { DeviceName = Name, DeviceSchematicId = DeviceID, DisplayName = Display, WarningLimitLow = _warningLow, WarningLimitHigh = _warningHigh, AlarmLimitLow = _alarmLow, AlarmLimitHigh = _alarmHigh, Value = Value, IsWarning = _warningEvent.IsTriggered, IsAlarm = _alarmEvent.IsTriggered }; return data; } } public IoSensorAnalog(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule) { _scWarningLow = node.GetAttribute("scWarningLow"); _scWarningHigh = node.GetAttribute("scWarningHigh"); _scAlarmLow = node.GetAttribute("scAlarmLow"); _scAlarmHigh = node.GetAttribute("scAlarmHigh"); _scFilterDuration = node.GetAttribute("scFilterDuration"); _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); // 初始化滤波器 var filterDurationSec = ParseScNode("", node, ioModule, $"{ScBasePath}.{_scFilterDuration}")?.IntValue ?? 0; _tdamFilter = new TimeDomainArithmeticMeanFilter(filterDurationSec); } 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(); _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(true, out _); _doAlarmHigh?.SetValue(false, out _); } else if (filtered > _alarmHigh) { _alarmEvent.Description = $"{Display} feedback ({filtered:F1}{Unit}) is above {_alarmHigh:F1} in {_alarmDelaySec:F0} seconds"; _doAlarmLow?.SetValue(false, out _); _doAlarmHigh?.SetValue(true, 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(true, out _); _doWarningHigh?.SetValue(false, out _); } else if (filtered > _warningHigh) { _warningEvent.Description = $"{Display} feedback ({filtered:F1}{Unit}) is above {_warningHigh:F1} in {_warningDelaySec:F0} seconds"; _doWarningLow?.SetValue(false, out _); _doWarningHigh?.SetValue(true, 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(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 } }