Sic.Framework/MECF.Framework.Common/MECF/Framework/Common/Device/Bases/SignalTowerBase.cs

395 lines
16 KiB
C#
Raw 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 Aitex.Common.Util;
using Aitex.Core.Common.DeviceData;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.Util;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml;
namespace MECF.Framework.Common.Device.Bases
{
/// <summary>
/// 信号塔对象。
/// </summary>
public class SignalTowerBase : BaseDevice, IDevice
{
#region Variables
/// <summary>
/// 是否关闭蜂鸣器输出。
/// <value>True蜂鸣器被手动关闭满足事件条件也不要打开蜂鸣器。</value>
/// <value>False根据当前系统状态和配置事件自动输出蜂鸣器状态。</value>
/// </summary>
private bool _switchBuzzerOff;
/// <summary>
/// 信号塔事件字典,从配置文件中读取。
/// </summary>
private Dictionary<string, List<SignalTowerPartAction>> _signalTowerEvent;
/// <summary>
/// 信号塔中组件字典。
/// </summary>
private readonly Dictionary<LightType, SignalTowerPartBase> _dicSignalParts;
/// <summary>
/// 信号塔组件动作字典。
/// </summary>
private readonly Dictionary<LightType, SignalTowerPartAction> _dicMergedActions;
#endregion
#region Constructors
/// <summary>
/// 构建信号塔对象的实例。
/// </summary>
/// <param name="module">当前模组名称。</param>
/// <param name="node">设备配置文件。</param>
/// <param name="ioModule">所属Module的名称。</param>
public SignalTowerBase(string module, XmlElement node, string ioModule = "")
: base(module, node, ioModule)
{
_dicSignalParts = new Dictionary<LightType, SignalTowerPartBase>();
_dicMergedActions = new Dictionary<LightType, SignalTowerPartAction>();
var doRedLight = ParseDoNode("doRed", node, ioModule);
var doYellowLight = ParseDoNode("doYellow", node, ioModule);
var doGreenLight = ParseDoNode("doGreen", node, ioModule);
var doBlueLight = ParseDoNode("doBlue", node, ioModule);
var doWhiteLight = ParseDoNode("doWhite", node, ioModule);
var doBuzzer = ParseDoNode("doBuzzer", node, ioModule);
var doBuzzer1 = ParseDoNode("doBuzzer1", node, ioModule);
var doBuzzer2 = ParseDoNode("doBuzzer2", node, ioModule);
var doBuzzer3 = ParseDoNode("doBuzzer3", node, ioModule);
var doBuzzer4 = ParseDoNode("doBuzzer4", node, ioModule);
var doBuzzer5 = ParseDoNode("doBuzzer5", node, ioModule);
var aoBuzzerBlinkFreq = ParseAoNode("aoBuzzerBlinkFreq", node, ioModule);
var eventFile = node.GetAttribute("eventFile");
// 红、黄、绿、蜂鸣器是必须定义的元件。
Debug.Assert(doRedLight != null, "DO RedLight is not valid");
Debug.Assert(doYellowLight != null, "DO RedLight is not valid");
Debug.Assert(doGreenLight != null, "DO RedLight is not valid");
Debug.Assert(doBuzzer != null, "DO RedLight is not valid");
// 创建三色灯控制元件。
CreateSignalPart(LightType.Red, doRedLight);
CreateSignalPart(LightType.Yellow, doYellowLight);
CreateSignalPart(LightType.Green, doGreenLight);
CreateSignalPart(LightType.Blue, doBlueLight);
CreateSignalPart(LightType.White, doWhiteLight);
CreateSignalPart(LightType.Buzzer, doBuzzer, aoBuzzerBlinkFreq);
CreateSignalPart(LightType.Buzzer1, doBuzzer1);
CreateSignalPart(LightType.Buzzer2, doBuzzer2);
CreateSignalPart(LightType.Buzzer3, doBuzzer3);
CreateSignalPart(LightType.Buzzer4, doBuzzer4);
CreateSignalPart(LightType.Buzzer5, doBuzzer5);
//解析三色灯Event
ParseSignalTowerEvent(eventFile);
}
#endregion
#region Properties
/// <summary>
/// 返回信号塔当前状态数据集。
/// <remarks>该属性用于RT到UI间的数据传递。</remarks>
/// </summary>
public AITSignalTowerData DeviceData => new AITSignalTowerData
{
DeviceName = Name,
DeviceSchematicId = DeviceID,
DisplayName = Display,
IsGreenLightOn = GetSignalTowerPartValue(LightType.Green),
IsRedLightOn = GetSignalTowerPartValue(LightType.Red),
IsYellowLightOn = GetSignalTowerPartValue(LightType.Yellow),
IsBlueLightOn = GetSignalTowerPartValue(LightType.Blue),
IsWhiteLightOn = GetSignalTowerPartValue(LightType.White),
IsBuzzerOn = GetSignalTowerPartValue(LightType.Buzzer),
IsBuzzer1On = GetSignalTowerPartValue(LightType.Buzzer1),
IsBuzzer2On = GetSignalTowerPartValue(LightType.Buzzer2),
IsBuzzer3On = GetSignalTowerPartValue(LightType.Buzzer3),
IsBuzzer4On = GetSignalTowerPartValue(LightType.Buzzer4),
IsBuzzer5On = GetSignalTowerPartValue(LightType.Buzzer5),
};
#endregion
#region Methods
/// <summary>
/// 创建一个信号塔元件,并添加到字典中。
/// </summary>
/// <param name="light">信号塔元件类型,请参考<see cref="LightType"/>。</param>
/// <param name="doSw">控制元件开关的DO。</param>
/// <param name="aoBlinkFreq">控制Blink频率的AO。</param>
private void CreateSignalPart(LightType light, DOAccessor doSw, AOAccessor aoBlinkFreq = null)
{
if (doSw != null)
_dicSignalParts.Add(light, new SignalTowerPartBase(light, doSw, aoBlinkFreq));
}
private bool ParseSignalTowerEvent(string fileName)
{
try
{
var cFileName = PathManager.GetCfgDir() + fileName;
if (!File.Exists(cFileName))
{
LOG.Error($"Unable to find signal tower events config file {cFileName}.");
return false;
}
var stEvents = CustomXmlSerializer.Deserialize<STEvents>(new FileInfo(cFileName));
_signalTowerEvent = new Dictionary<string, List<SignalTowerPartAction>>();
// Blinking配置字符串规则
// 1、Blinking
// 2、Blinking,---..
// 3、Blinking,0.5
// 在线校验https://regex101.com/r/CescBw/1
var regxBlinkPatternString =
new Regex(
@"^(([bB]linking){1}(,{1})(?(2)([-\.]+)$))|(([bB]linking){1}(,{1})(?(6)(\d+\.?\d*)$))|([bB]linking){1}$");
foreach (var stEvent in stEvents.Events)
{
if (!_signalTowerEvent.ContainsKey(stEvent.Name))
{
_signalTowerEvent[stEvent.Name] = new List<SignalTowerPartAction>();
foreach (LightType type in Enum.GetValues(typeof(LightType)))
{
if (!_dicSignalParts.ContainsKey(type))
continue;
var obj = stEvent.GetType().GetProperty(type.ToString())?.GetValue(stEvent);
if (obj != null)
{
TowerLightStatus status = TowerLightStatus.Unknown;
STBlinkPattern blinkPattern = null;
var str = obj.ToString().ToLower();
if (str.Contains("on"))
{
status = TowerLightStatus.On;
}
else if (str.Contains("off"))
{
status = TowerLightStatus.Off;
}
else if (str.Contains("warning"))
{
status = TowerLightStatus.Warning;
blinkPattern = STBlinkPattern.GetSlowBlinkPattern();
}
else if (str.Contains("alarm"))
{
status = TowerLightStatus.Alarm;
blinkPattern = STBlinkPattern.GetFastBlinkPattern();
}
else if(str.StartsWith(TowerLightStatus.Blinking.ToString().ToLower()))
{
// 如果配置为闪烁方式检查配置中是否包含BlinkPattern字串
var matches = regxBlinkPatternString.Match(str);
if (matches.Success)
{
if (matches.Groups[9].Success) // Blinking
blinkPattern = STBlinkPattern.GetSlowBlinkPattern();
else if (matches.Groups[6].Success) // Blinking,00.0
blinkPattern = STBlinkPattern.ParseSTEvent(matches.Groups[8].Value);
else if (matches.Groups[2].Success) // Blinking,--.-
blinkPattern = STBlinkPattern.ParseSTEvent(matches.Groups[4].Value);
else
blinkPattern = STBlinkPattern.GetFastBlinkPattern();
}
status = TowerLightStatus.Blinking;
}
_signalTowerEvent[stEvent.Name].Add(new SignalTowerPartAction(type, status, blinkPattern));
}
}
}
}
return true;
}
catch (Exception ex)
{
LOG.Error(ex.ToString());
return false;
}
}
/// <summary>
/// 初始化信号塔对象实例。
/// </summary>
/// <remarks>
/// 注册SwitchOffBuzzer操作和DeviceData数据。
/// </remarks>
/// <returns></returns>
public bool Initialize()
{
OP.Subscribe($"{Module}.{Name}.{AITSignalTowerOperation.SwitchOffBuzzer}", SwitchOffBuzzer);
DATA.Subscribe(Module + "." + Name + ".DeviceData", () => DeviceData);
return true;
}
/// <summary>
/// 复位信号塔状态及其各个组件。
/// </summary>
public void Reset()
{
_switchBuzzerOff = false;
foreach (var light in _dicSignalParts.Values)
light.Reset();
}
/// <summary>
/// 终止信号塔对象实例。
/// </summary>
public void Terminate()
{
foreach (var light in _dicSignalParts.Values)
light.Terminate();
}
/// <summary>
/// 周期性扫描信号塔以执行后台任务。
/// </summary>
public void Monitor()
{
_dicMergedActions.Clear();
// 遍历所有注册的事件,决定信号塔各元件的输出状态。
foreach (var signalEvent in _signalTowerEvent)
{
var obj = DATA.Poll(signalEvent.Key);
if (obj is not true)
continue;
foreach (var action in signalEvent.Value)
{
_dicMergedActions.TryGetValue(action.Light, out var oldAction);
var mergeAction = MergeAction(oldAction, action);
// 蜂鸣器被关闭
if (action.Light == LightType.Buzzer && _switchBuzzerOff)
mergeAction.Status = TowerLightStatus.Off;
if (oldAction == null)
_dicMergedActions.Add(action.Light, mergeAction);
else
_dicMergedActions[action.Light] = mergeAction;
}
}
// 设置信号塔每个组件的输出。
foreach (var action in _dicMergedActions)
_dicSignalParts[action.Key].SetValue(action.Value.Status, action.Value.BlinkPattern);
// 扫描信号塔组件状态
foreach (var light in _dicSignalParts.Values)
light?.Monitor();
}
/// <summary>
/// 以指定的模式闪烁指定的信号塔元件。
/// </summary>
/// <param name="light">指定的信号塔元件。</param>
/// <param name="pattern">闪烁模式。</param>
/// <returns>True启动闪烁成功False启动闪烁失败。</returns>
public bool Blink(LightType light, STBlinkPattern pattern)
{
if (pattern == null)
{
LOG.Error($"{light} Blink Pattern can not be null");
return false;
}
if (_dicSignalParts.TryGetValue(light, out var lightInstance))
{
lightInstance.SetValue(TowerLightStatus.Blinking, pattern);
return true;
}
LOG.Error($"{light} Unable to find this part of signal tower, check configuration");
return false;
}
/// <summary>
/// 打开或关闭蜂鸣器。
/// </summary>
/// <param name="isOff"></param>
/// <returns></returns>
public bool SwitchOffBuzzer(bool isOff)
{
_switchBuzzerOff = isOff;
return true;
}
/// <summary>
/// 关闭蜂鸣器。
/// </summary>
/// <param name="arg1"></param>
/// <param name="arg2"></param>
/// <returns></returns>
private bool SwitchOffBuzzer(string arg1, object[] arg2)
{
_switchBuzzerOff = true;
return true;
}
/// <summary>
/// 合并信号事件。
/// <remarks>
/// 根据<see cref="SignalTowerPartAction.Status"/>的优先级决定执行的动作。
/// <br/>
/// 此属性的数值越小,表示优先级越高。
/// </remarks>
/// </summary>
/// <param name="currentAction">当前输出状态。</param>
/// <param name="nextAction">期望的输出状态。</param>
/// <returns>合并后的信号灯输出状态。</returns>
private SignalTowerPartAction MergeAction(SignalTowerPartAction currentAction, SignalTowerPartAction nextAction)
{
if (currentAction == null)
return nextAction;
if (nextAction == null)
return currentAction;
return (int)currentAction.Status < (int)nextAction.Status ? currentAction : nextAction;
}
/// <summary>
/// 获取指定信号塔元件的输出状态。
/// </summary>
/// <param name="light"></param>
/// <returns></returns>
private bool GetSignalTowerPartValue(LightType light)
{
if (_dicSignalParts.TryGetValue(light, out var part))
return part.GetValue();
return false;
}
#endregion
}
}