|
@@ -0,0 +1,465 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Security;
|
|
|
+using System.ServiceModel;
|
|
|
+using System.Text;
|
|
|
+using Aitex.Core.RT.DataCenter;
|
|
|
+using Aitex.Core.RT.Device;
|
|
|
+using Aitex.Core.RT.Event;
|
|
|
+using Aitex.Core.RT.Log;
|
|
|
+using Aitex.Core.RT.OperationCenter;
|
|
|
+using Aitex.Core.RT.SCCore;
|
|
|
+using Aitex.Core.Util;
|
|
|
+using Aitex.Core.WCF;
|
|
|
+using EPInterface;
|
|
|
+using EPInterface.Data;
|
|
|
+using EPInterface.Datas;
|
|
|
+
|
|
|
+namespace Aitex.RT.Device.Custom
|
|
|
+{
|
|
|
+ public class EPDDevice : BaseDevice, IDevice
|
|
|
+ {
|
|
|
+ private PeriodicJob _monitorThead;
|
|
|
+
|
|
|
+ private R_TRIG _triggerConnected = new R_TRIG();
|
|
|
+ private R_TRIG _triggerNotConnected = new R_TRIG();
|
|
|
+
|
|
|
+ private int _channel;
|
|
|
+
|
|
|
+ private string _channelStatus;
|
|
|
+
|
|
|
+
|
|
|
+ //private bool _enableRetry = true;
|
|
|
+
|
|
|
+ private object _lockerTrigger = new object();
|
|
|
+
|
|
|
+ private bool _isEnd = false;
|
|
|
+
|
|
|
+ public bool IsEnd
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return _isEnd;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool IsConnected
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ return _triggerConnected.M;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //private bool _isRecipeStarted;
|
|
|
+ //private bool _isStepStarted;
|
|
|
+
|
|
|
+ private int _connectionCounter = 0;
|
|
|
+
|
|
|
+ public EPDDevice(string module, string name) :
|
|
|
+ base(module, name, name, name)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool Initialize()
|
|
|
+ {
|
|
|
+ _channel = SC.GetValue<int>($"{Module}.{Name}.ChannelNumber");
|
|
|
+
|
|
|
+ DATA.Subscribe($"{Module}.{Name}.IsConnected", ()=> IsConnected);
|
|
|
+ DATA.Subscribe($"{Module}.{Name}.CurrentChannel", () => _channel);
|
|
|
+ DATA.Subscribe($"{Module}.{Name}.ChannelStatus", () => _channelStatus);
|
|
|
+
|
|
|
+ OP.Subscribe($"{Module}.{Name}.SetConfig", (out string reason, int time, object[] args) => {
|
|
|
+
|
|
|
+ if (!IsConnected)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.WARN_ENDPOINT, Module, $"{Module} {Name} not connected, can not set config");
|
|
|
+ reason = $"{Module} {Name} not connected, can not set config";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ _isEnd = false;
|
|
|
+
|
|
|
+ StepStart(args[1].ToString(), Convert.ToInt32(args[0]));
|
|
|
+
|
|
|
+ reason = "";
|
|
|
+ return true;
|
|
|
+ });
|
|
|
+
|
|
|
+ _monitorThead = new PeriodicJob(100, OnTimer, "EPDMonitor", true);
|
|
|
+
|
|
|
+ EPDCallbackClient.Instance.Notify += Instance_Notify;
|
|
|
+ EPDCallbackClient.Instance.Trigger += Instance_Trigger;
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void RecipeStart(string recipeName)
|
|
|
+ {
|
|
|
+ if (!IsConnected)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.ERR_ENDPOINT, Module, "EPD not connected, call recipe start ignored");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, Notify EPD recipe {recipeName} start");
|
|
|
+
|
|
|
+ EPDClient.Instance.Service.RecipeStart(_channel, recipeName);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void RecipeStop()
|
|
|
+ {
|
|
|
+ if (!IsConnected)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.ERR_ENDPOINT, Module, "EPD not connected, call recipe start ignored");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, Notify EPD recipe stopped");
|
|
|
+
|
|
|
+ EPDClient.Instance.Service.RecipeStop(_channel);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * ExposureTime=222;WaveLengthA=2;BinningA=3;WaveLengthB=4;BinningB=6;WaveLengthC=5;BinningC=8;
|
|
|
+ * WaveLengthD=7;BinningD=9;Fd=1;PrefilterTime=2;PostfilterTime=3;AlgorithmType=Valley;
|
|
|
+ * Criteria=4;DelayTime=5;ValidationTime=6;ValidationValue=7;
|
|
|
+ * TimeWindow=8;MinimalTime=9;PostponeTime=10;Control=11;Normalization=12;
|
|
|
+ * EnablePostponePercent=True;EnableCriterialPercent=True;
|
|
|
+ * TriggerMode=System.Windows.Controls.ComboBoxItem: Event;IsFaultIfNoTrigger=True;
|
|
|
+ */
|
|
|
+ public void StepStart(string config, int index)
|
|
|
+ {
|
|
|
+ if (!IsConnected)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.ERR_ENDPOINT, Module, "EPD not connected, call step start ignored");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, Notify EPD recipe step {index+1} start");
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, EPD config {config}");
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ EPDConfig epd = new EPDConfig();
|
|
|
+
|
|
|
+ epd.nParameterCount = 1;
|
|
|
+
|
|
|
+ string[] items = config.Split(';');
|
|
|
+ foreach (var item in items)
|
|
|
+ {
|
|
|
+ if (string.IsNullOrEmpty(item))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ string[] pairs = item.Split('=');
|
|
|
+ if (pairs.Length != 2)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ switch (pairs[0])
|
|
|
+ {
|
|
|
+ case "ExposureTime":
|
|
|
+ epd.Columns[0].nCCDExposureTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "WaveLengthA":
|
|
|
+ epd.Columns[0].nWaveLength[0] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "BinningA":
|
|
|
+ epd.Columns[0].nBinning[0] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "WaveLengthB":
|
|
|
+ epd.Columns[0].nWaveLength[1] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "BinningB":
|
|
|
+ epd.Columns[0].nBinning[1] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "WaveLengthC":
|
|
|
+ epd.Columns[0].nWaveLength[2] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "BinningC":
|
|
|
+ epd.Columns[0].nBinning[2] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "WaveLengthD":
|
|
|
+ epd.Columns[0].nWaveLength[3] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "BinningD":
|
|
|
+ epd.Columns[0].nBinning[3] = ushort.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "Fd":
|
|
|
+ epd.Columns[0].cFunc = pairs[1];
|
|
|
+ break;
|
|
|
+ case "PrefilterTime":
|
|
|
+ epd.Columns[0].nPreFilterTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "PostfilterTime":
|
|
|
+ epd.Columns[0].nPostFilterTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "AlgorithmType":
|
|
|
+ epd.Columns[0].algorithmType = MapType(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "Criteria":
|
|
|
+ epd.Columns[0].nCriteria = float.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "DelayTime":
|
|
|
+ epd.Columns[0].nDelayTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "ValidationTime":
|
|
|
+ epd.Columns[0].nValidationTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "ValidationValue":
|
|
|
+ epd.Columns[0].nValidationValue = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "TimeWindow":
|
|
|
+ epd.Columns[0].nTimeWindow = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "MinimalTime":
|
|
|
+ epd.Columns[0].nMinimalTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "PostponeTime":
|
|
|
+ epd.Columns[0].nPostponeTime = int.Parse(pairs[1]);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case "Control":
|
|
|
+ epd.Columns[0].bControl = Convert.ToBoolean(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "Normalization":
|
|
|
+ epd.Columns[0].bNormalization = Convert.ToBoolean(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "EnablePostponePercent":
|
|
|
+ epd.Columns[0].bPostponePercent = Convert.ToBoolean(pairs[1]);
|
|
|
+ break;
|
|
|
+ case "EnableCriterialPercent":
|
|
|
+ epd.Columns[0].bCriteriaPercent = Convert.ToBoolean(pairs[1]);
|
|
|
+ break;
|
|
|
+
|
|
|
+
|
|
|
+ case "EnableEventTrigger":
|
|
|
+ epd.Columns[0].bEvtTrigger = Convert.ToBoolean(pairs[1]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ EPDClient.Instance.Service.StartByConfig(_channel, index, $"step{index}", epd);
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.ERR_ENDPOINT, Module, ex.ToString());
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, Step {index + 1} config values not valid, {ex.Message}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private AlgorithmType MapType(string type)
|
|
|
+ {
|
|
|
+ switch (type)
|
|
|
+ {
|
|
|
+ case "Unknown": return AlgorithmType.ALG_NONE;
|
|
|
+ case "Above_ABS_Value": return AlgorithmType.ALG_RISE_VALUE;
|
|
|
+ case "Below_ABS_Value": return AlgorithmType.ALG_FALL_VALUE;
|
|
|
+ case "Drop_Percent": return AlgorithmType.ALG_FALL_PERCENT;
|
|
|
+ case "Up_Percent": return AlgorithmType.ALG_RISE_PERCENT;
|
|
|
+ case "Range_In": return AlgorithmType.ALG_RANGE_IN;
|
|
|
+ case "Gradient": return AlgorithmType.ALG_GRADIENT;
|
|
|
+ case "Peek": return AlgorithmType.ALG_PEAK;
|
|
|
+ case "Valley": return AlgorithmType.ALG_VALLEY;
|
|
|
+ case "Min_Drop_Percent": return AlgorithmType.ALG_MIN_FALL_PERCENT;
|
|
|
+ case "Min_Up_Percent": return AlgorithmType.ALG_MIN_RISE_PERCENT;
|
|
|
+ case "Max_Drop_Percent": return AlgorithmType.ALG_MAX_FALL_PERCENT;
|
|
|
+ case "Max_Up_Percent": return AlgorithmType.ALG_MAX_RISE_PERCENT;
|
|
|
+ case "Rise_Fall": return AlgorithmType.ALG_RISE_FALL;
|
|
|
+ case "Fall_Rise": return AlgorithmType.ALG_FALL_RISE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return AlgorithmType.ALG_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void StepStop()
|
|
|
+ {
|
|
|
+ if (!IsConnected)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.ERR_ENDPOINT, Module, "EPD not connected, call step stop ignored");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, Notify EPD recipe step stopped");
|
|
|
+
|
|
|
+ EPDClient.Instance.Service.Stop(_channel);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private bool OnTimer()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ bool retryConnect = false;
|
|
|
+ lock (_lockerTrigger)
|
|
|
+ {
|
|
|
+ retryConnect = _triggerConnected.M;
|
|
|
+ if (!_triggerConnected.M)
|
|
|
+ {
|
|
|
+ //if (_enableRetry)
|
|
|
+ {
|
|
|
+ retryConnect = true;
|
|
|
+ //_enableRetry = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (retryConnect)
|
|
|
+ {
|
|
|
+ _connectionCounter++;
|
|
|
+ if (_connectionCounter > 10000)
|
|
|
+ _connectionCounter = 1;
|
|
|
+
|
|
|
+ _triggerConnected.CLK = _connectionCounter== EPDClient.Instance.Service.Heartbeat(_connectionCounter);
|
|
|
+
|
|
|
+ _triggerNotConnected.CLK = !_triggerConnected.M;
|
|
|
+
|
|
|
+ if (_triggerConnected.Q)
|
|
|
+ {
|
|
|
+ EPDCallbackClient.Instance.Init();
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, EPD Connected");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_triggerConnected.M)
|
|
|
+ {
|
|
|
+ _channelStatus = EPDClient.Instance.Service.QueryState(_channel).ToString();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (_triggerNotConnected.Q)
|
|
|
+ {
|
|
|
+ EPDCallbackClient.Instance.Stop();
|
|
|
+ LOG.Write(eEvent.WARN_ENDPOINT, Module, $"{Module} {Name}, EPD disconnected");
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ LOG.Write(eEvent.ERR_ENDPOINT, Module, ex.ToString());
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Instance_Notify(int channel, string e)
|
|
|
+ {
|
|
|
+ if (_channel != channel)
|
|
|
+ return;
|
|
|
+
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, EPD Feedback:{e}");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Instance_Trigger(int channel, TriggerEventArgs e)
|
|
|
+ {
|
|
|
+ if (_channel != channel)
|
|
|
+ return;
|
|
|
+
|
|
|
+ _isEnd = true;
|
|
|
+
|
|
|
+ LOG.Write(eEvent.INFO_ENDPOINT, Module, $"{Module} {Name}, EPD: {e.Channel}.{e.Name} Triggered");
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Monitor()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Terminate()
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Reset()
|
|
|
+ {
|
|
|
+ lock (_lockerTrigger)
|
|
|
+ {
|
|
|
+ if (!_triggerConnected.M)
|
|
|
+ {
|
|
|
+ //_enableRetry = true;
|
|
|
+
|
|
|
+ _triggerConnected.RST = true;
|
|
|
+ _triggerNotConnected.RST = true;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class EPDCallbackClient : Singleton<EPDCallbackClient>, IEPDCallback
|
|
|
+ {
|
|
|
+ public event Action<int, string> Notify;
|
|
|
+
|
|
|
+ public event Action<int, TriggerEventArgs> Trigger;
|
|
|
+
|
|
|
+ private EPDCallbackServiceClient _service;
|
|
|
+
|
|
|
+ public EPDCallbackClient()
|
|
|
+ {
|
|
|
+ _service = new EPDCallbackServiceClient(this);
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Init()
|
|
|
+ {
|
|
|
+ _service.Register();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Stop()
|
|
|
+ {
|
|
|
+ _service.UnRegister();
|
|
|
+ }
|
|
|
+
|
|
|
+ //public void OnNotify(string channel, string message)
|
|
|
+ //{
|
|
|
+
|
|
|
+ //}
|
|
|
+
|
|
|
+ //public void OnTrigger(string channel, string name, long ticket)
|
|
|
+ //{
|
|
|
+
|
|
|
+ //}
|
|
|
+
|
|
|
+ public void OnNotify(int channel, EPDEventType EventType, string message)
|
|
|
+ {
|
|
|
+ Notify?.Invoke(channel, $"{EventType}:{message}");
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public void OnTrigger(int channel, string name, long ticket)
|
|
|
+ {
|
|
|
+ Trigger?.Invoke(channel, new TriggerEventArgs() { Channel = channel, Name = name, Ticket = ticket });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class TriggerEventArgs
|
|
|
+ {
|
|
|
+ public int Channel { get; set; }
|
|
|
+ public string Name { get; set; }
|
|
|
+ public long Ticket { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ public class EPDCallbackServiceClient : DuplexChannelServiceClientWrapper<IEPDCallbackService>
|
|
|
+ {
|
|
|
+ private Guid _clientId = Guid.Empty;
|
|
|
+
|
|
|
+ public EPDCallbackServiceClient(IEPDCallback callback) : base(new InstanceContext(callback), "Client_IEPDCallbackService", "IEPDCallbackService")
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Register()
|
|
|
+ {
|
|
|
+ if (_clientId != Guid.Empty)
|
|
|
+ UnRegister();
|
|
|
+
|
|
|
+ _clientId = Guid.NewGuid();
|
|
|
+ Invoke(x => x.Register(_clientId));
|
|
|
+ }
|
|
|
+
|
|
|
+ public void UnRegister()
|
|
|
+ {
|
|
|
+ Invoke(x => x.UnRegister(_clientId));
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|