Sic.Framework-Nanjing-Baishi/MECF.Framework.Common/MECF/Framework/Common/Device/Bases/SignalTowerBase.cs

414 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.Windows.Media.Media3D;
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;
/// <summary>
/// STEvents配置文件解析对象。
/// </summary>
private STEvents _originStEvents;
#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;
}
_originStEvents = CustomXmlSerializer.Deserialize<STEvents>(new FileInfo(cFileName));
_originStEvents.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 => new STEventAction(v.Key, TowerLightStatus.Off));
// 默认设置为Off
_dicMergedActions.Values.ToList().ForEach(x => x.Status = TowerLightStatus.Off);
// 遍历所有注册的事件,决定信号塔各元件的输出状态。
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>
/// 当Wafer回到Cassette后打开蜂鸣器以指示ProcessJob结束。
/// </summary>
/// <param name="reason">如果打开蜂鸣器失败,提示失败原因。</param>
/// <returns></returns>
public bool SwitchOnBuzzerForJobDone(out string reason)
{
reason = "";
var targetBuzzer = LightType.Buzzer;
STBlinkPattern blinkPattern = null;
// 读取STEvents配置文件中的JobDone模式的配置。
var settings = _originStEvents.PatternsSettings.FirstOrDefault(x=>x.Name == "JobDone");
if (settings != null)
{
// 解析配置中的Buzzer如果未指定Buzzer或指定为除Buzzer以外的信号灯则默认使用Buzzer。
var targetPart = settings.Part;
if (string.IsNullOrEmpty(targetPart)
|| !targetPart.StartsWith("Buz")
|| !Enum.TryParse(targetPart, out targetBuzzer))
targetBuzzer = LightType.Buzzer;
blinkPattern = new STBlinkPattern(settings.Pattern, priority: settings.Priority, cycle:settings.Cycles);
}
else
{
// 没找到JobDone的工作模式配置生成默认配置
blinkPattern = STBlinkPattern.GetJobDonePattern();
LOG.Warning("Unable to find pre-defined pattern for JobDone");
}
// 如果在字典中找到Buzzer组件则设置动作
if (_dicStParts.TryGetValue(targetBuzzer, out var objBuzzer))
{
// 强制使能蜂鸣器
_switchBuzzerOff = false;
objBuzzer.SetAction(new STEventAction(targetBuzzer, TowerLightStatus.Customized, blinkPattern));
return true;
}
else
{
reason = $"Unalbe to find \"{targetBuzzer}\"";
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
}
}