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

375 lines
15 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.Linq;
using System.Text.RegularExpressions;
using System.Xml;
namespace MECF.Framework.Common.Device.Bases
{
/// <summary>
/// 信号塔对象。
/// </summary>
public class SignalTowerBase : BaseDevice, IDevice
{
#region Variables
private bool _switchBuzzerOff;
private Dictionary<string, List<SignalTowerPartAction>> _signalTowerEvent;
private readonly Dictionary<LightType, SignalTowerPartBase> _dicSignalParts;
#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>();
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("BuzzerBlinkFreq", node, ioModule);
// 红、黄、绿、蜂鸣器是必须定义的元件。
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();
}
protected SignalTowerBase()
{
throw new NotImplementedException();
}
#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()
{
try
{
var fileName = PathManager.GetCfgDir() + $"_{Module}_SignalTower.xml";
if (!File.Exists(fileName))
{
fileName = PathManager.GetCfgDir() + $"{Module}_SignalTower.xml";
if (!File.Exists(fileName))
{
LOG.Error("Unable to find signal tower events definition file.");
return false;
}
}
var stEvents = CustomXmlSerializer.Deserialize<STEvents>(new FileInfo(fileName));
_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 _event in stEvents.Events)
{
if (!_signalTowerEvent.ContainsKey(_event.Name))
{
_signalTowerEvent[_event.Name] = new List<SignalTowerPartAction>();
foreach (LightType type in Enum.GetValues(typeof(LightType)))
{
if (!_dicSignalParts.ContainsKey(type))
continue;
var obj = _event.GetType().GetProperty(type.ToString())?.GetValue(_event);
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[_event.Name].Add(new SignalTowerPartAction(type, status, blinkPattern));
}
}
}
}
return true;
}
catch (Exception ex)
{
LOG.Error(ex.ToString());
return false;
}
}
public bool Initialize()
{
OP.Subscribe($"{Module}.{Name}.{AITSignalTowerOperation.SwitchOffBuzzer}", SwitchOffBuzzer);
DATA.Subscribe(Module + "." + Name + ".DeviceData", () => DeviceData);
return true;
}
public void Reset()
{
_switchBuzzerOff = false;
foreach (var light in _dicSignalParts.Values)
light.Reset();
}
public void Terminate()
{
foreach (var light in _dicSignalParts.Values)
light.Terminate();
}
public void Monitor()
{
var tempStatus = _dicSignalParts.Keys.ToDictionary(x => x, type => TowerLightStatus.Off);
// 遍历所有注册的事件,决定信号塔各元件的输出状态。
foreach (var signalEvent in _signalTowerEvent)
{
var obj = DATA.Poll(signalEvent.Key);
if (obj == null || !(obj is bool flag) || !flag)
continue;
foreach (var action in signalEvent.Value)
{
var mergedAction = MergeAction(tempStatus[action.Light], action.Status);
if (_switchBuzzerOff && action.Light == LightType.Buzzer)
{
mergedAction = TowerLightStatus.Off;
}
tempStatus[action.Light] = mergedAction;
}
}
foreach (var status in tempStatus)
{
_dicSignalParts[status.Key].SetValue(status.Value);
}
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>
/// 如果信号灯期望的输出状态和当前输出状态中任一状态为On则输出On
/// <br/>
/// 如果信号灯期望的输出状态和当前输出状态中任一状态为Blink则输出Blink
/// <br/>
/// 如果信号灯期望的输出状态和当前输出状态*均为*为Off则输出Off
/// </remarks>
/// </summary>
/// <param name="currentStatus">当前输出状态。</param>
/// <param name="desiredStatus">期望的输出状态。</param>
/// <returns>合并后的信号灯输出状态。</returns>
private TowerLightStatus MergeAction(TowerLightStatus currentStatus, TowerLightStatus desiredStatus)
{
if (currentStatus == TowerLightStatus.On || desiredStatus == TowerLightStatus.On)
{
return TowerLightStatus.On;
}
if (currentStatus == TowerLightStatus.Blinking || desiredStatus == TowerLightStatus.Blinking)
{
return TowerLightStatus.Blinking;
}
return TowerLightStatus.Off;
}
/// <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
}
}