2023-04-28 10:15:33 +08:00
|
|
|
|
using Aitex.Common.Util;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
using Aitex.Core.Common.DeviceData;
|
|
|
|
|
using Aitex.Core.RT.DataCenter;
|
|
|
|
|
using Aitex.Core.RT.Device;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
using Aitex.Core.RT.IOCore;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
using Aitex.Core.RT.Log;
|
|
|
|
|
using Aitex.Core.RT.OperationCenter;
|
|
|
|
|
using Aitex.Core.Util;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.IO;
|
2023-05-05 15:46:00 +08:00
|
|
|
|
using System.Linq;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
using System.Xml;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
using DictEvent = System.Collections.Generic.Dictionary<string, System.Collections.Generic.List<MECF.Framework.Common.Device.Bases.STAction>>;
|
|
|
|
|
using DictActionsPerLight = System.Collections.Generic.Dictionary<MECF.Framework.Common.Device.Bases.STLightTypes, MECF.Framework.Common.Device.Bases.STAction>;
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
namespace MECF.Framework.Common.Device.Bases
|
|
|
|
|
{
|
2023-04-28 10:15:33 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 信号塔对象。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class SignalTowerBase : BaseDevice, IDevice
|
|
|
|
|
{
|
|
|
|
|
#region Variables
|
|
|
|
|
|
2023-05-08 11:19:59 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 系统保留的工作模式配置项,用于工艺结束后蜂鸣器发出提示音。
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected const string KEY_PATTERN_JOB_DONE = "JobDone";
|
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 信号灯字典。
|
|
|
|
|
/// </summary>
|
|
|
|
|
private readonly Dictionary<STLightTypes, SignalTowerLightBase> _dictStLights;
|
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 是否关闭蜂鸣器输出。
|
|
|
|
|
/// <value>True:蜂鸣器被手动关闭,满足事件条件也不要打开蜂鸣器。</value>
|
|
|
|
|
/// <value>False:根据当前系统状态和配置事件自动输出蜂鸣器状态。</value>
|
|
|
|
|
/// </summary>
|
2023-04-28 10:15:33 +08:00
|
|
|
|
private bool _switchBuzzerOff;
|
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 信号塔事件字典,从配置文件中读取。
|
2023-05-08 11:19:59 +08:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 字典的Key表示事件名称,对应STEvent配置文件中的name属性。
|
|
|
|
|
/// </remarks>
|
2023-05-09 11:04:45 +08:00
|
|
|
|
/// <br/>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 注意:操作该字典时需要加锁,因为当前对象支持实时监测配置文件并重载配置文件的改动;配置文件
|
|
|
|
|
/// 内容改动后,会重新创建该字典,此时有可能Monitor()方法正在使用此字典。
|
|
|
|
|
/// </remarks>
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private DictEvent _dictPreDefinedEvents;
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 信号塔扩展事件字典。
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 除STEvents配置中预设的事件外,还有一类事件通常由RT触发,这类事件也需要参与三色灯输出状态判断。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private readonly DictEvent _dictDynamicSTLightActions;
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 信号塔组件动作字典。
|
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private DictActionsPerLight _dictSTLightActions;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-05-08 13:26:30 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 上一次Monitor周期触发的事件列表。
|
|
|
|
|
/// 用于下一次Monitor周期判断是否有新的事件产生,已解决蜂鸣器被手动关闭后,如果未按下Reset,则下次新事件产生时,无法响蜂鸣器的问题。
|
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private readonly List<string> _lastEventsWithBuzzer = [];
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-05-08 09:39:26 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// STEvents配置文件解析对象。
|
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private STEvents _eventsFileLoader;
|
2023-05-08 09:39:26 +08:00
|
|
|
|
|
2023-04-28 10:15:33 +08:00
|
|
|
|
#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)
|
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
_dictSTLightActions = new();
|
|
|
|
|
_dictStLights = new Dictionary<STLightTypes, SignalTowerLightBase>();
|
|
|
|
|
_dictDynamicSTLightActions = new ();
|
2023-05-05 15:46:00 +08:00
|
|
|
|
|
2023-04-28 10:15:33 +08:00
|
|
|
|
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);
|
2023-05-05 11:38:25 +08:00
|
|
|
|
var aoBuzzerBlinkFreq = ParseAoNode("aoBuzzerBlinkFreq", node, ioModule);
|
|
|
|
|
var eventFile = node.GetAttribute("eventFile");
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
|
|
|
|
// 红、黄、绿、蜂鸣器是必须定义的元件。
|
2023-12-27 15:12:26 +08:00
|
|
|
|
Debug.Assert(doRedLight != null, "DO of RedLight not defined");
|
|
|
|
|
Debug.Assert(doYellowLight != null, "DO of YellowLight not defined");
|
|
|
|
|
Debug.Assert(doGreenLight != null, "DO of GreenLight not defined");
|
|
|
|
|
Debug.Assert(doBuzzer != null, "DO of Buzzer not defined");
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
|
|
|
|
// 创建三色灯控制元件。
|
2023-12-27 10:42:19 +08:00
|
|
|
|
CreateSTLight(STLightTypes.Red, doRedLight);
|
|
|
|
|
CreateSTLight(STLightTypes.Yellow, doYellowLight);
|
|
|
|
|
CreateSTLight(STLightTypes.Green, doGreenLight);
|
|
|
|
|
CreateSTLight(STLightTypes.Blue, doBlueLight);
|
|
|
|
|
CreateSTLight(STLightTypes.White, doWhiteLight);
|
|
|
|
|
CreateSTLight(STLightTypes.Buzzer, doBuzzer, aoBuzzerBlinkFreq);
|
|
|
|
|
CreateSTLight(STLightTypes.Buzzer1, doBuzzer1);
|
|
|
|
|
CreateSTLight(STLightTypes.Buzzer2, doBuzzer2);
|
|
|
|
|
CreateSTLight(STLightTypes.Buzzer3, doBuzzer3);
|
|
|
|
|
CreateSTLight(STLightTypes.Buzzer4, doBuzzer4);
|
|
|
|
|
CreateSTLight(STLightTypes.Buzzer5, doBuzzer5);
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-05-08 14:59:31 +08:00
|
|
|
|
// 添加文件到监视器,当文件内容发生变化时重新加载配置。
|
|
|
|
|
var fullFn = PathManager.GetCfgDir() + eventFile;
|
|
|
|
|
FileSystemWatcherManager.Instance.Register(fullFn, fn =>
|
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
ParseSTEvent(fn);
|
2023-05-08 14:59:31 +08:00
|
|
|
|
});
|
|
|
|
|
|
2023-04-28 10:15:33 +08:00
|
|
|
|
//解析三色灯Event
|
2023-12-27 10:42:19 +08:00
|
|
|
|
ParseSTEvent(fullFn);
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 返回信号塔当前状态数据集。
|
|
|
|
|
/// <remarks>该属性用于RT到UI间的数据传递。</remarks>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public AITSignalTowerData DeviceData => new AITSignalTowerData
|
|
|
|
|
{
|
|
|
|
|
DeviceName = Name,
|
|
|
|
|
DeviceSchematicId = DeviceID,
|
|
|
|
|
DisplayName = Display,
|
2023-12-27 10:42:19 +08:00
|
|
|
|
IsGreenLightOn = GetSignalTowerPartValue(STLightTypes.Green),
|
|
|
|
|
IsRedLightOn = GetSignalTowerPartValue(STLightTypes.Red),
|
|
|
|
|
IsYellowLightOn = GetSignalTowerPartValue(STLightTypes.Yellow),
|
|
|
|
|
IsBlueLightOn = GetSignalTowerPartValue(STLightTypes.Blue),
|
|
|
|
|
IsWhiteLightOn = GetSignalTowerPartValue(STLightTypes.White),
|
|
|
|
|
IsBuzzerOn = GetSignalTowerPartValue(STLightTypes.Buzzer),
|
|
|
|
|
IsBuzzer1On = GetSignalTowerPartValue(STLightTypes.Buzzer1),
|
|
|
|
|
IsBuzzer2On = GetSignalTowerPartValue(STLightTypes.Buzzer2),
|
|
|
|
|
IsBuzzer3On = GetSignalTowerPartValue(STLightTypes.Buzzer3),
|
|
|
|
|
IsBuzzer4On = GetSignalTowerPartValue(STLightTypes.Buzzer4),
|
|
|
|
|
IsBuzzer5On = GetSignalTowerPartValue(STLightTypes.Buzzer5),
|
2023-12-27 15:12:26 +08:00
|
|
|
|
GreenLightEvent = _dictStLights[STLightTypes.Green]?.Action?.EventName ?? "",
|
|
|
|
|
YellowLightEvent = _dictStLights[STLightTypes.Yellow]?.Action?.EventName ?? "",
|
|
|
|
|
RedLightEvent = _dictStLights[STLightTypes.Red]?.Action?.EventName ?? "",
|
|
|
|
|
BuzzerEvent = _dictStLights[STLightTypes.Buzzer]?.Action?.EventName ?? ""
|
2023-04-28 10:15:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建一个信号塔元件,并添加到字典中。
|
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// <param name="light">信号塔元件类型,请参考<see cref="STLightTypes"/>。</param>
|
2023-05-01 18:58:42 +08:00
|
|
|
|
/// <param name="doSw">控制元件开关的DO。</param>
|
|
|
|
|
/// <param name="aoBlinkFreq">控制Blink频率的AO。</param>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private void CreateSTLight(STLightTypes light, DOAccessor doSw, AOAccessor aoBlinkFreq = null)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
2023-05-01 18:58:42 +08:00
|
|
|
|
if (doSw != null)
|
2023-12-27 15:12:26 +08:00
|
|
|
|
_dictStLights.Add(light, new SignalTowerLightBase(Module, light, doSw, aoBlinkFreq));
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-06 16:57:09 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 从指定的配置文件中解析信号塔事件。
|
|
|
|
|
/// </summary>
|
2023-05-08 14:59:31 +08:00
|
|
|
|
/// <param name="fileName">包含完整路径的配置文件文件名。</param>
|
2023-05-06 16:57:09 +08:00
|
|
|
|
/// <returns></returns>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private bool ParseSTEvent(string fileName)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
2023-05-08 14:59:31 +08:00
|
|
|
|
if (!File.Exists(fileName))
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
2023-05-08 14:59:31 +08:00
|
|
|
|
LOG.Error($"Unable to find signal tower events config file {fileName}.");
|
2023-05-05 11:38:25 +08:00
|
|
|
|
return false;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
_eventsFileLoader = CustomXmlSerializer.Deserialize<STEvents>(new FileInfo(fileName));
|
|
|
|
|
_eventsFileLoader.ParseEvents(_dictStLights, out var events);
|
2023-05-06 16:57:09 +08:00
|
|
|
|
if (events == null)
|
|
|
|
|
LOG.Error("Unable to parse the signal tower events from config file.");
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-05-08 14:59:31 +08:00
|
|
|
|
lock (SyncRoot)
|
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
_dictPreDefinedEvents = events;
|
|
|
|
|
return _dictPreDefinedEvents != null;
|
2023-05-08 14:59:31 +08:00
|
|
|
|
}
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
LOG.Error(ex.ToString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-06 16:57:09 +08:00
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 初始化信号塔对象实例。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 注册SwitchOffBuzzer操作和DeviceData数据。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// <returns></returns>
|
2023-04-28 10:15:33 +08:00
|
|
|
|
public bool Initialize()
|
|
|
|
|
{
|
|
|
|
|
OP.Subscribe($"{Module}.{Name}.{AITSignalTowerOperation.SwitchOffBuzzer}", SwitchOffBuzzer);
|
|
|
|
|
DATA.Subscribe(Module + "." + Name + ".DeviceData", () => DeviceData);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 复位信号塔状态及其各个组件。
|
|
|
|
|
/// </summary>
|
2023-04-28 10:15:33 +08:00
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
_switchBuzzerOff = false;
|
2023-12-27 10:42:19 +08:00
|
|
|
|
_dictDynamicSTLightActions?.Clear();
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
foreach (var light in _dictStLights.Values)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
light.Reset();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 终止信号塔对象实例。
|
|
|
|
|
/// </summary>
|
2023-04-28 10:15:33 +08:00
|
|
|
|
public void Terminate()
|
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
foreach (var light in _dictStLights.Values)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
light.Terminate();
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 周期性扫描信号塔以执行后台任务。
|
|
|
|
|
/// </summary>
|
2023-05-11 14:34:05 +08:00
|
|
|
|
protected override void HandleMonitor()
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// 遍历所有预设的事件,决定信号塔各元件的输出状态。
|
|
|
|
|
MonitorEvents(out var buzzerOnEvents);
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// 判断是否有新的Buzzer相关事件产生,如果有,需要复位_switchBuzzerOff状态,以重新使能蜂鸣器,解决蜂鸣器被手动关闭后,如果不按Reset,则无法重启启用的问题。
|
|
|
|
|
var buzzerOnEventArray = buzzerOnEvents as string[] ?? buzzerOnEvents.ToArray();
|
|
|
|
|
var newBuzzerOnEvents = _lastEventsWithBuzzer.Except(buzzerOnEventArray);
|
|
|
|
|
if (newBuzzerOnEvents.Any())
|
2023-05-11 14:34:05 +08:00
|
|
|
|
{
|
|
|
|
|
_lastEventsWithBuzzer.Clear();
|
2023-12-27 10:42:19 +08:00
|
|
|
|
_lastEventsWithBuzzer.AddRange(buzzerOnEventArray);
|
2023-05-08 13:26:30 +08:00
|
|
|
|
_switchBuzzerOff = false;
|
2023-05-11 14:34:05 +08:00
|
|
|
|
}
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// 分配每个信号灯的动作。
|
|
|
|
|
foreach (var kvp in _dictSTLightActions)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
2023-05-08 11:19:59 +08:00
|
|
|
|
var action = kvp.Value;
|
2023-12-27 10:42:19 +08:00
|
|
|
|
|
|
|
|
|
if (action?.Light == null)
|
2023-05-08 11:19:59 +08:00
|
|
|
|
continue;
|
2023-05-05 15:46:00 +08:00
|
|
|
|
|
2023-05-08 11:19:59 +08:00
|
|
|
|
// 创建动作副本,因为后续可能根据_switchBuzzerOff调整输出状态,避免覆盖原配置。
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var cloned = (STAction)action.Clone();
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-05-08 11:19:59 +08:00
|
|
|
|
// 如果蜂鸣器被强制关闭,则将待执行动作中的状态修改为Off
|
2023-12-27 10:42:19 +08:00
|
|
|
|
if (action.Light.IsBuzzer && _switchBuzzerOff)
|
|
|
|
|
cloned.Output = SignalTowerActions.Off;
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
action.Light.SetAction(cloned);
|
2023-05-05 15:46:00 +08:00
|
|
|
|
}
|
2023-04-28 10:15:33 +08:00
|
|
|
|
|
2023-05-05 11:38:25 +08:00
|
|
|
|
// 扫描信号塔组件状态
|
2023-12-27 10:42:19 +08:00
|
|
|
|
foreach (var light in _dictStLights.Values)
|
|
|
|
|
{
|
|
|
|
|
if(light == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
light.Monitor();
|
|
|
|
|
|
|
|
|
|
// remove the cycle done action from dynamic STEvent dictionary
|
2023-12-27 15:12:26 +08:00
|
|
|
|
var currAction = light.Action;
|
2023-12-27 10:42:19 +08:00
|
|
|
|
if (currAction is { IsCycleDone: true }
|
|
|
|
|
&& _dictDynamicSTLightActions.TryGetValue(currAction.EventName, out var actions))
|
|
|
|
|
{
|
|
|
|
|
for (var i = actions.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
if (actions[i].LightType == light.Type)
|
|
|
|
|
actions.RemoveAt(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 以指定的模式闪烁指定的信号塔元件。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="light">指定的信号塔元件。</param>
|
|
|
|
|
/// <param name="pattern">闪烁模式。</param>
|
|
|
|
|
/// <returns>True:启动闪烁成功;False:启动闪烁失败。</returns>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
public bool Blink(STLightTypes light, STBlinkPattern pattern)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
|
|
|
|
if (pattern == null)
|
|
|
|
|
{
|
|
|
|
|
LOG.Error($"{light} Blink Pattern can not be null");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var evName = $"Manual_Blink_{light}_{Guid.NewGuid()}";
|
|
|
|
|
var blinkAction = new STAction(evName, _dictStLights[light], SignalTowerActions.Customized,
|
|
|
|
|
pattern);
|
|
|
|
|
|
|
|
|
|
CreateDynamicEvent(evName, [blinkAction]);
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
|
|
|
|
return true;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
2023-05-08 09:39:26 +08:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 当Wafer回到Cassette后,打开蜂鸣器以指示ProcessJob结束。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
public bool AlertJobDone()
|
2023-05-08 09:39:26 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
SignalTowerLightBase targetBuzzer = null;
|
2023-05-08 09:39:26 +08:00
|
|
|
|
STBlinkPattern blinkPattern = null;
|
|
|
|
|
|
|
|
|
|
// 读取STEvents配置文件中的JobDone模式的配置。
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var alertPattern = _eventsFileLoader.PatternsSettings.FirstOrDefault(x => x.Name == KEY_PATTERN_JOB_DONE);
|
2023-05-08 09:39:26 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
if (alertPattern != null)
|
2023-05-08 09:39:26 +08:00
|
|
|
|
{
|
|
|
|
|
// 解析配置中的Buzzer,如果未指定Buzzer,或指定为除Buzzer以外的信号灯,则默认使用Buzzer。
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var targetLight = alertPattern.Part;
|
|
|
|
|
if (Enum.TryParse(targetLight, out STLightTypes lightType)
|
|
|
|
|
&& _dictStLights.TryGetValue(lightType, out var buzzer)
|
2023-05-08 11:19:59 +08:00
|
|
|
|
&& buzzer.IsBuzzer)
|
2023-05-11 14:34:05 +08:00
|
|
|
|
targetBuzzer = buzzer;
|
2023-05-08 09:39:26 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
blinkPattern = new STBlinkPattern(alertPattern.Pattern, priority: alertPattern.Priority, cycle: alertPattern.Cycles);
|
2023-05-08 09:39:26 +08:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// 没找到JobDone的工作模式配置,生成默认配置
|
|
|
|
|
blinkPattern = STBlinkPattern.GetJobDonePattern();
|
2023-05-08 11:19:59 +08:00
|
|
|
|
LOG.Warning($"Unable to find pre-defined pattern for {KEY_PATTERN_JOB_DONE}");
|
2023-05-08 09:39:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var buzzerAction = new STAction(KEY_PATTERN_JOB_DONE, targetBuzzer, SignalTowerActions.Customized,
|
|
|
|
|
blinkPattern);
|
|
|
|
|
CreateDynamicEvent(KEY_PATTERN_JOB_DONE, [buzzerAction]);
|
2023-05-08 11:19:59 +08:00
|
|
|
|
return true;
|
2023-05-08 09:39:26 +08:00
|
|
|
|
}
|
2023-12-27 10:42:19 +08:00
|
|
|
|
|
2023-04-28 10:15:33 +08:00
|
|
|
|
/// <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>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// 获取优先级更高的STAction。
|
2023-04-28 10:15:33 +08:00
|
|
|
|
/// <remarks>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// 根据<see cref="STAction.Output"/>的优先级决定执行的动作。
|
2023-04-28 10:15:33 +08:00
|
|
|
|
/// <br/>
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// 此属性的数值越小,表示优先级越高。
|
2023-04-28 10:15:33 +08:00
|
|
|
|
/// </remarks>
|
|
|
|
|
/// </summary>
|
2023-05-05 11:38:25 +08:00
|
|
|
|
/// <param name="currentAction">当前输出状态。</param>
|
|
|
|
|
/// <param name="nextAction">期望的输出状态。</param>
|
2023-04-28 10:15:33 +08:00
|
|
|
|
/// <returns>合并后的信号灯输出状态。</returns>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private STAction PickPriorAction(STAction currentAction, STAction nextAction)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
2023-05-05 11:38:25 +08:00
|
|
|
|
if (currentAction == null)
|
|
|
|
|
return nextAction;
|
|
|
|
|
if (nextAction == null)
|
|
|
|
|
return currentAction;
|
2023-05-06 16:57:09 +08:00
|
|
|
|
|
|
|
|
|
// 获取当前动作的优先级
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var curPrior = currentAction.Output == SignalTowerActions.Customized
|
2023-05-06 16:57:09 +08:00
|
|
|
|
? currentAction.BlinkPattern.Priority
|
2023-12-27 10:42:19 +08:00
|
|
|
|
: (int)currentAction.Output;
|
2023-05-06 16:57:09 +08:00
|
|
|
|
|
|
|
|
|
// 获取下一个动作的优先级
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var nextPrior = nextAction.Output == SignalTowerActions.Customized
|
2023-05-06 16:57:09 +08:00
|
|
|
|
? nextAction.BlinkPattern.Priority
|
2023-12-27 10:42:19 +08:00
|
|
|
|
: (int)nextAction.Output;
|
2023-05-06 16:57:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return curPrior < nextPrior ? currentAction : nextAction;
|
2023-04-28 10:15:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取指定信号塔元件的输出状态。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="light"></param>
|
|
|
|
|
/// <returns></returns>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private bool GetSignalTowerPartValue(STLightTypes light)
|
2023-04-28 10:15:33 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
if (_dictStLights.TryGetValue(light, out var part))
|
2023-04-28 10:15:33 +08:00
|
|
|
|
return part.GetValue();
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-08 13:26:30 +08:00
|
|
|
|
/// <summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// 从RT拉取已发生的STEvent列表。
|
2023-05-08 13:26:30 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private static DictEvent PollRaisedEvents(DictEvent dicEvents)
|
2023-05-08 13:26:30 +08:00
|
|
|
|
{
|
2023-05-11 12:02:46 +08:00
|
|
|
|
if (dicEvents == null)
|
2023-12-27 10:42:19 +08:00
|
|
|
|
return new();
|
2023-05-11 12:02:46 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
var events = dicEvents.Keys.ToList();
|
|
|
|
|
var data = DATA.PollData(events);
|
|
|
|
|
var raisedEvents = data.Where(x => x.Value is true).Select(x => x.Key).ToList();
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
return dicEvents.Where(x => raisedEvents.Contains(x.Key)).ToDictionary(x => x.Key, v => v.Value);
|
2023-05-08 13:26:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-08 11:19:59 +08:00
|
|
|
|
/// <summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// 监视STEvent.xml中定义的事件。
|
2023-05-08 11:19:59 +08:00
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// <param name="eventsWithBuzzerOn"></param>
|
|
|
|
|
private void MonitorEvents(out IEnumerable<string> eventsWithBuzzerOn)
|
2023-05-08 11:19:59 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
lock (SyncRoot)
|
|
|
|
|
{
|
|
|
|
|
// Clear previous actions
|
|
|
|
|
_dictSTLightActions.Clear();
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// Poll raised events of RT
|
|
|
|
|
var raisedEvents = PollRaisedEvents(_dictPreDefinedEvents);
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// Create dictionary '_dictSTLightActions'
|
|
|
|
|
AssignSTActions(raisedEvents);
|
|
|
|
|
AssignSTActions(_dictDynamicSTLightActions);
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// Get a list of events related to turning on a buzzer
|
|
|
|
|
var buzzerOnEv1 = GetBuzzerOnEvents(raisedEvents).ToArray();
|
|
|
|
|
var buzzerOnEv2 = GetBuzzerOnEvents(_dictDynamicSTLightActions).ToArray();
|
2023-05-11 14:34:05 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
eventsWithBuzzerOn = buzzerOnEv1.Concat(buzzerOnEv2.Except(buzzerOnEv1));
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-08 13:26:30 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据已发生的STEvent分配各个信号灯的动作。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="raisedEvents"></param>
|
|
|
|
|
private void AssignSTActions(DictEvent raisedEvents)
|
|
|
|
|
{
|
|
|
|
|
foreach (var stEvent in raisedEvents)
|
2023-05-08 11:19:59 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
foreach (var action in stEvent.Value)
|
2023-05-08 11:19:59 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// Maybe the target light of the action is not defined
|
|
|
|
|
if (!_dictStLights.ContainsKey(action.LightType))
|
2023-05-08 11:19:59 +08:00
|
|
|
|
continue;
|
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// Make it easier to access dictionaries using the key
|
|
|
|
|
var light = action.LightType;
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
// Determine the action of light according to the event raised
|
|
|
|
|
if (!_dictSTLightActions.TryGetValue(light, out var oldAction))
|
|
|
|
|
_dictSTLightActions.Add(light, action);
|
|
|
|
|
else
|
|
|
|
|
_dictSTLightActions[light] = PickPriorAction(oldAction, action);
|
2023-05-08 11:19:59 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-27 10:42:19 +08:00
|
|
|
|
}
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 获取需要打开Buzzer的事件的名称。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="events"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
private IEnumerable<string> GetBuzzerOnEvents(DictEvent events)
|
|
|
|
|
{
|
|
|
|
|
// create a list for the events operating the buzzer
|
|
|
|
|
var buzzerOnEvents = events
|
|
|
|
|
.Where(x => x.Value
|
|
|
|
|
.Any(v => v.Light.IsBuzzer && v.Output != SignalTowerActions.Off))
|
|
|
|
|
.Select(x => x.Key);
|
2023-05-08 11:19:59 +08:00
|
|
|
|
|
2023-12-27 10:42:19 +08:00
|
|
|
|
return buzzerOnEvents;
|
2023-05-08 11:19:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-08 13:26:30 +08:00
|
|
|
|
/// <summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// 创建一个动态RT事件,以驱动指定的信号塔组件。
|
2023-05-08 13:26:30 +08:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 自动创建Guid作为事件的Key,以和STEvent配置字典的格式保持一致,以为Monitor方法中需要同时对这两个字典处理;格式的统一有助于统一处理任务的逻辑。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// </summary>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
/// <param name="eventName"></param>
|
2023-05-08 13:26:30 +08:00
|
|
|
|
/// <param name="actions">该事件执行的动作列表。
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 列表的每一个元素对应一个信号塔组件需执行的动作。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
/// </param>
|
2023-12-27 10:42:19 +08:00
|
|
|
|
private void CreateDynamicEvent(string eventName, List<STAction> actions)
|
2023-05-08 13:26:30 +08:00
|
|
|
|
{
|
2023-12-27 10:42:19 +08:00
|
|
|
|
_dictDynamicSTLightActions[eventName] = actions;
|
2023-05-08 13:26:30 +08:00
|
|
|
|
}
|
2023-12-27 10:42:19 +08:00
|
|
|
|
|
2023-04-28 10:15:33 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
}
|
2023-05-06 16:57:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|