358 lines
13 KiB
C#
358 lines
13 KiB
C#
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.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<STEventAction>> _signalTowerEvent;
|
||
|
||
/// <summary>
|
||
/// 信号塔中组件字典。
|
||
/// </summary>
|
||
private readonly Dictionary<LightType, SignalTowerPartBase> _dicStParts;
|
||
|
||
/// <summary>
|
||
/// 信号塔组件动作字典。
|
||
/// </summary>
|
||
private Dictionary<LightType, STEventAction> _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)
|
||
{
|
||
_dicStParts = 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("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)
|
||
_dicStParts.Add(light, new SignalTowerPartBase(light, doSw, aoBlinkFreq));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从指定的配置文件中解析信号塔事件。
|
||
/// </summary>
|
||
/// <param name="fileName"></param>
|
||
/// <returns></returns>
|
||
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));
|
||
stEvents.ParseEvents(out var events);
|
||
if (events == null)
|
||
LOG.Error("Unable to parse the signal tower events from config file.");
|
||
|
||
_signalTowerEvent = events;
|
||
return _signalTowerEvent != null;
|
||
}
|
||
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 _dicStParts.Values)
|
||
light.Reset();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 终止信号塔对象实例。
|
||
/// </summary>
|
||
public void Terminate()
|
||
{
|
||
foreach (var light in _dicStParts.Values)
|
||
light.Terminate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 周期性扫描信号塔以执行后台任务。
|
||
/// </summary>
|
||
public void Monitor()
|
||
{
|
||
_dicMergedActions = _dicStParts.ToDictionary(
|
||
x => x.Key,
|
||
v => v.Value.GetAction());
|
||
|
||
// 遍历所有注册的事件,决定信号塔各元件的输出状态。
|
||
foreach (var signalEvent in _signalTowerEvent)
|
||
{
|
||
var obj = DATA.Poll(signalEvent.Key);
|
||
if(obj is not true)
|
||
continue;
|
||
|
||
foreach (var newAction in signalEvent.Value)
|
||
{
|
||
if (!_dicStParts.TryGetValue(newAction.Light, out var stPart))
|
||
continue;
|
||
|
||
// 根据当前设备状态,判断下一步信号组件的输出动作是什么
|
||
var mergedAction = MergeAction(_dicMergedActions[stPart.Light], newAction);
|
||
|
||
_dicMergedActions[stPart.Light] = mergedAction;
|
||
}
|
||
}
|
||
|
||
// 设置信号塔每个组件的输出。
|
||
foreach (var action in _dicMergedActions)
|
||
{
|
||
// 创建一个动作副本,解决蜂鸣器被手动关闭后无法启用的问题
|
||
var clonedAction = (STEventAction)action.Value?.Clone();
|
||
if(clonedAction == null)
|
||
continue;
|
||
|
||
// 如果蜂鸣器被强制关闭
|
||
if (clonedAction.Light == LightType.Buzzer && _switchBuzzerOff)
|
||
clonedAction.Status = TowerLightStatus.Off;
|
||
|
||
|
||
_dicStParts[action.Key].SetAction(clonedAction);
|
||
}
|
||
|
||
// 扫描信号塔组件状态
|
||
foreach (var light in _dicStParts.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 (_dicStParts.TryGetValue(light, out var lightInstance))
|
||
{
|
||
// 强制使能蜂鸣器
|
||
_switchBuzzerOff = false;
|
||
lightInstance.SetAction(new STEventAction(lightInstance.Light, TowerLightStatus.Customized,
|
||
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="STEventAction.Status"/>的优先级决定执行的动作。
|
||
/// <br/>
|
||
/// 此属性的数值越小,表示优先级越高。
|
||
/// </remarks>
|
||
/// </summary>
|
||
/// <param name="currentAction">当前输出状态。</param>
|
||
/// <param name="nextAction">期望的输出状态。</param>
|
||
/// <returns>合并后的信号灯输出状态。</returns>
|
||
private STEventAction MergeAction(STEventAction currentAction, STEventAction nextAction)
|
||
{
|
||
if (currentAction == null)
|
||
return nextAction;
|
||
if (nextAction == null)
|
||
return currentAction;
|
||
|
||
// 获取当前动作的优先级
|
||
var curPrior = currentAction.Status == TowerLightStatus.Customized
|
||
? currentAction.BlinkPattern.Priority
|
||
: (int)currentAction.Status;
|
||
|
||
// 获取下一个动作的优先级
|
||
var nextPrior = nextAction.Status == TowerLightStatus.Customized
|
||
? nextAction.BlinkPattern.Priority
|
||
: (int)nextAction.Status;
|
||
|
||
|
||
return curPrior < nextPrior ? currentAction : nextAction;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指定信号塔元件的输出状态。
|
||
/// </summary>
|
||
/// <param name="light"></param>
|
||
/// <returns></returns>
|
||
private bool GetSignalTowerPartValue(LightType light)
|
||
{
|
||
if (_dicStParts.TryGetValue(light, out var part))
|
||
return part.GetValue();
|
||
|
||
return false;
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
|
||
|
||
|
||
}
|