Browse Source

epd files missing.

sangwq 2 years ago
parent
commit
f8cec9a940

+ 4 - 2
Venus/Venus_RT/App.config

@@ -62,9 +62,11 @@
 		</services>
 
 		<client>
-			<endpoint address="net.tcp://localhost:9002/PMService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="ClusterInterface.IPmService" name="Client_IPECVDService" />
+			<!--<endpoint address="net.tcp://localhost:9002/PMService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="ClusterInterface.IPmService" name="Client_IPECVDService" />
 			<endpoint address="net.tcp://localhost:9004/PMService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="ClusterInterface.IPmService" name="Client_IIBEService" />
-			<endpoint address="net.tcp://localhost:9006/PMService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="ClusterInterface.IPmService" name="Client_IICPService" />
+			<endpoint address="net.tcp://localhost:9006/PMService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="ClusterInterface.IPmService" name="Client_IICPService" />-->
+			<endpoint address="net.tcp://192.168.10.20:5771/EPDService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="EPInterface.IEPDService" name="Client_IEPDService"/>
+			<endpoint address="net.tcp://192.168.10.20:5773/EPDCallbackService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="EPInterface.IEPDCallbackService" name="Client_IEPDCallbackService" />
 		</client>
 		<behaviors>
 			<serviceBehaviors>

+ 165 - 0
Venus/Venus_RT/Devices/EPD/Datas/DataItemDefine.cs

@@ -0,0 +1,165 @@
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace EPInterface.Data
+{
+    [DataContract]
+    public enum EPDState
+    {
+        [EnumMember]
+        UnSampling,    //硬件未进入采样
+
+        [EnumMember]
+        Running,       //计算中
+
+        [EnumMember]
+        Pause,         //暂停
+
+        [EnumMember]
+        Idle,
+    };
+
+    [DataContract]
+    public enum EPDEventType
+    {
+        [EnumMember]
+        Default = 0,    //基础信息
+
+        [EnumMember]
+        Info = 1,    //基础信息
+
+
+        [EnumMember]
+        Error,    //基础信息
+
+        [EnumMember]
+        Start,       //步骤 开始 
+
+        [EnumMember]
+        Delay,       //步骤 延迟 
+
+        [EnumMember]
+        Normalize,   //步骤 
+
+        
+        [EnumMember]
+        Check,   //步骤 检查条件
+
+        [EnumMember]
+        EndPoint,   //步骤 触发
+
+        [EnumMember]
+        Stop,   //步骤 停止
+    };
+
+    [DataContract]
+    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
+    public struct HardwareInfo
+    {
+        [MarshalAs(UnmanagedType.U8)]
+        public ulong Ver;
+        [MarshalAs(UnmanagedType.U2)]
+        public ushort Pixes;
+        [MarshalAs(UnmanagedType.R8)]
+        public double FirstCoefficient;
+        [MarshalAs(UnmanagedType.R8)]
+        public double SecondCoefficient;
+        [MarshalAs(UnmanagedType.R8)]
+        public double ThirdCoefficient;
+        [MarshalAs(UnmanagedType.R8)]
+        public double Intercept;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
+        public char[] Serial;
+
+        [MarshalAs(UnmanagedType.U8)]
+        public ulong IntegrationTime;
+
+        [MarshalAs(UnmanagedType.U8)]
+        public ulong SamplingTime;
+
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
+        public double[] Wave;
+    }
+
+    [DataContract]
+    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
+    public struct RawDataItem
+    {
+        public static readonly int Size = Marshal.SizeOf(typeof(RawDataItem));
+        [MarshalAs(UnmanagedType.I8)]
+        public long Ticket;
+
+        [MarshalAs(UnmanagedType.I4)]    //累积次数
+        public int Counter;
+
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]  //全光谱数据
+        public ushort[] RawData;
+
+        [MarshalAs(UnmanagedType.I4)]    //波段数据
+        public int RegionNum;
+
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+        public double[] RegionData;
+
+        [MarshalAs(UnmanagedType.I4)]    //趋势数据, 表达式
+        public int TrendNum;
+
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
+        public double[] TrendData;
+    }
+
+
+    [DataContract]
+
+    public class CRawDataItem
+    {
+        [DataMember]
+        public long Ticket
+        {
+            get;set;
+        }
+        [DataMember]
+        public ushort[] RawData
+        { get;
+          set;
+        }
+
+        [DataMember]
+        public int RegionNum
+        {
+            get; set;
+        }
+
+        [DataMember]
+        public double[] RegionData
+        {
+            get;set;
+        }
+
+        [DataMember]
+        public int TrendNum
+        {
+            get;set;
+        }
+        [DataMember]
+        public double[] TrendData
+        {
+            get;set;
+        }
+
+        public CRawDataItem()
+        {
+            Ticket = 0;
+            RawData = new ushort[1024];
+
+            RegionNum = 32;
+            RegionData = new double[32];
+
+            TrendNum = 32;
+            TrendData = new double[32];
+        }
+    }
+
+
+
+}

+ 128 - 0
Venus/Venus_RT/Devices/EPD/Datas/EPDConfig.cs

@@ -0,0 +1,128 @@
+using System.Runtime.Serialization;
+
+namespace EPInterface.Datas
+{
+    [DataContract]
+    public enum AlgorithmType
+    {
+        [EnumMember]
+        ALG_NONE,            //非endpoint
+        //level trigger, invaild time
+        [EnumMember]
+        ALG_FALL_VALUE,              //"FallValue"    ValidationTime,Threshold       < 
+        [EnumMember]
+        ALG_RISE_VALUE,              //"RiseValue"    ValidationTime,Threshold       >
+        [EnumMember]
+        ALG_FALL_PERCENT,            //"FallPecent"    ValidationTime,Threshold, TimeWindow       < 
+        [EnumMember]
+        ALG_RISE_PERCENT,            //"RisePecent"    ValidationTime,Threshold, TimeWindow      >
+
+        [EnumMember]
+        ALG_MIN_FALL_PERCENT,   //"MinFallPecent"    ValidationTime,Threshold, TimeWindow, BandTime  
+        [EnumMember]
+        ALG_MAX_FALL_PERCENT,   //"MaxFallPecent"    ValidationTime,Threshold, TimeWindow, BandTime  
+        [EnumMember]
+        ALG_MIN_RISE_PERCENT,   //"MinRisePecent"    ValidationTime,Threshold, TimeWindow, BandTime  
+        [EnumMember]
+        ALG_MAX_RISE_PERCENT,   //"MaxRisePecent"    ValidationTime,Threshold, TimeWindow, BandTime  
+
+        //Edge Trigger
+        [EnumMember]
+        ALG_GRADIENT,       //"Gradient"    Threshold, TimeWindow 
+        [EnumMember]
+        ALG_RANGE_IN,    //"RangeIn"    Threshold, TimeWindow 
+        [EnumMember]
+        ALG_PEAK,        //Peak     Threshold, TimeWindow, ValidationTime
+        [EnumMember]
+        ALG_VALLEY,      //Vallery  Threshold, TimeWindow, ValidationTime
+        [EnumMember]
+        ALG_RISE_FALL,    //"RiseFall"     Threshold, ValidationTime,ValidationValue,
+        [EnumMember]
+        ALG_FALL_RISE,    //"FallRise"     Threshold, ValidationTime,ValidationValue,
+
+    }
+
+    [DataContract]
+    public class EPDColumn
+    {
+        [DataMember]
+        public bool bNormalization { get; set; }
+        [DataMember]
+        public bool bControl { get; set; }
+        [DataMember]
+        public bool bPostponePercent { get; set; }
+        [DataMember]
+        public bool bEvtTrigger { get; set; }
+        [DataMember]
+        public bool bCriteriaPercent { get; set; }
+
+        [DataMember]
+        public int nCCDExposureTime { get; set; }
+
+        [DataMember]
+        public int nPreFilterTime { get; set; }
+        [DataMember]
+        public int nPostFilterTime { get; set; }
+
+        [DataMember]
+        public AlgorithmType algorithmType { get; set; }
+        [DataMember]
+        public double nCriteria { get; set; }
+        [DataMember]
+        public int nDelayTime { get; set; }
+        [DataMember]
+        public int nValidationTime { get; set; }
+        [DataMember]
+        public int nTimeWindow { get; set; }
+        [DataMember]
+        public int nMinimalTime { get; set; } // Version 2
+        [DataMember]
+        public int nPostponeTime { get; set; } // Version 2
+        [DataMember]
+        public int nValidationValue { get; set; } // Used for rise / fall algorithm
+        [DataMember]
+        public int nRefractionIndex { get; set; }// Only for IEP, always zero
+        [DataMember]
+        public float fEtchAmount { get; set; }  // Only for IEP, always zero
+
+
+        [DataMember]
+        public ushort[] nWaveLength { get; set; }
+        [DataMember]
+        public ushort[] nBinning { get; set; }
+
+        [DataMember]
+        public string cPSMode { get; set; } // Version 7
+        [DataMember]
+        public string cFunc { get; set; }
+
+        public EPDColumn()
+        {
+            nWaveLength = new ushort[8];
+            nBinning = new ushort[8];
+        }
+    }
+
+    [DataContract]
+    public class EPDConfig
+    {
+        [DataMember]
+        public int nParameterCount { get; set; }
+
+        [DataMember]
+        public EPDColumn[] Columns;
+
+        public bool FaultIfNoEPDTrigger { get; set; }
+
+
+        [DataMember]
+        public string cLogic { get; set; } // logic for multiple endpoints, resevered for future to support logic 'OR' Version 3
+
+        public EPDConfig()
+        {
+            Columns = new EPDColumn[3];
+            for (int i = 0; i < Columns.Length; i++)
+                Columns[i] = new EPDColumn();
+        }
+    }
+}

+ 465 - 0
Venus/Venus_RT/Devices/EPD/EPDDevice.cs

@@ -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));
+        }
+    }
+}

+ 196 - 0
Venus/Venus_RT/Devices/EPD/Interface/EPDClient.cs

@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+using System.Data;
+using EPInterface.Data;
+using System.ServiceModel;
+using Aitex.Core.Util;
+using Aitex.Core.WCF;
+using EPInterface.Datas;
+using MECF.Framework.Common.Utilities;
+
+namespace EPInterface
+{
+    public class EPDClient : Singleton<EPDClient>
+    {
+ 
+        private IEPDService _service;
+        public IEPDService Service
+        {
+            get
+            {
+                if (_service == null)
+                {
+ 
+                        _service = new EPDServiceClient();
+                }
+
+                return _service;
+            }
+        }
+    }
+
+    public class EPDServiceClient : ServiceClientWrapper<IEPDService>, IEPDService
+    {
+        public EPDServiceClient()
+            : base("Client_IEPDService", "EPDService")
+        {
+        }
+
+        public void SetModel(int channel,string name, string model)
+        {
+            Invoke(svc => {  svc.SetModel(channel, name, model); });
+        }
+
+        public int Heartbeat(int counter)
+        {
+            int result = 0;
+
+            Invoke(svc => { result = svc.Heartbeat(counter); });
+
+            return result;
+        }
+
+        public void Online()
+        {
+            Invoke(svc => { svc.Online(); });
+        }
+        public void Offline()
+        {
+            Invoke(svc => { svc.Offline(); });
+        }
+
+        public bool IsOnline()
+        {
+            bool result = false;
+            Invoke(svc => { result = svc.IsOnline(); });
+            return result;
+        }
+
+        public void SetOption(string name, string value)
+        {
+            Invoke(svc => { svc.SetOption(name, value); });
+        }
+
+        public string GetOption(string name )
+        {
+            string result = string.Empty;
+            Invoke(svc => { result = svc.GetOption(name); });
+            return result;
+        }
+
+        public void RecipeStart(int channel, string name)
+        {
+            Invoke(svc => { svc.RecipeStart(channel, name); });
+        }
+        public void RecipeStop(int channel)
+        {
+            Invoke(svc => { svc.RecipeStop(channel); });
+        }
+        public void Start(int channel,int idx, string name, string model)
+        {
+            Invoke(svc => {  svc.Start(channel, idx, name, model); });
+        }
+
+        public void StartByConfig(int channel, int idx, string name, EPDConfig config)
+        {
+            Invoke(svc => { svc.StartByConfig(channel, idx, name, config); });
+        }
+
+        public void Stop(int channel)
+        {
+            Invoke(svc => {  svc.Stop(channel); });
+        }
+
+        public void Pause(int channel)
+        {
+            Invoke(svc => {  svc.Pause(channel); });
+        }
+
+        public void Resume(int channel)
+        {             
+            Invoke(svc => {  svc.Resume(channel); });
+        }
+        public int QueryChannels()
+        {
+            int result = 0;
+            Invoke(svc => { result = svc.QueryChannels(); });
+            return result;
+        }
+
+        public List<string> QueryChannelNames()
+        {
+            List<string> result = new List<string>();
+            Invoke(svc => { result = svc.QueryChannelNames(); });
+            return result;
+        }
+
+        public string QueryChannelName(int channel)
+        {
+            string result = "";
+            Invoke(svc => { result = svc.QueryChannelName(channel); });
+            return result;
+        }
+
+        public Tuple<string,string> QueryModel(int channel)
+        {
+            Tuple<string, string> result = null;
+            Invoke(svc => { result = svc.QueryModel(channel); });
+            return result;
+        }
+
+        public double[] QueryWave(int channel)
+        {
+            double[] result = null;
+            Invoke(svc => { result = svc.QueryWave(channel); });
+            return result;
+        }
+
+        public CRawDataItem QueryData(int channel)
+        {
+            CRawDataItem result = default(CRawDataItem) ;
+            Invoke(svc => { result = svc.QueryData(channel); });
+            return result;
+        }
+
+        public EPDState QueryState(int channel)
+        {
+            EPDState result = EPDState.UnSampling;
+            Invoke(svc => { result = svc.QueryState(channel); });
+            return result;
+        }
+
+        public bool QueryRecordState(int channel)
+        {
+            bool result = false;
+            Invoke(svc => { result = svc.QueryRecordState(channel); });
+            return result;
+        }
+
+        public long QueryRecipeTime(int channel)
+        {
+            long result = 0;
+            Invoke(svc => { result = svc.QueryRecipeTime(channel); });
+            return result;
+        }
+        public List<long> QueryStepTime(int channel)
+        {
+            List<long> result = new List<long>();
+            Invoke(svc => { result = svc.QueryStepTime(channel); });
+            return result;
+        }
+
+        public List<string> QueryConfigList()
+        {
+            List<string> result = new List<string>();
+            Invoke(svc => { result = svc.QueryConfigList(); });
+            return result;
+        }
+
+        public string QueryConfig(string name)
+        {
+            string result = string.Empty;
+            Invoke(svc => { result = svc.QueryConfig(name); });
+            return result;
+        }
+    }
+}

+ 16 - 0
Venus/Venus_RT/Devices/EPD/Interface/IEPDCallback.cs

@@ -0,0 +1,16 @@
+using EPInterface.Data;
+using System.ServiceModel;
+
+namespace EPInterface
+{
+    [ServiceContract]
+
+    public interface IEPDCallback
+    {
+        [OperationContract(IsOneWay = true)]
+        void OnNotify(int channel, EPDEventType type, string message);   //信息反馈
+
+		[OperationContract(IsOneWay = true)]
+		void OnTrigger(int channel, string name, long ticket);     //Endpoint Trigger
+    }
+}

+ 17 - 0
Venus/Venus_RT/Devices/EPD/Interface/IEPDCallbackService.cs

@@ -0,0 +1,17 @@
+using System;
+using System.ServiceModel;
+using EPInterface.Data;
+
+namespace EPInterface
+{
+    [ServiceContract(CallbackContract = typeof(IEPDCallback))]
+    [ServiceKnownType(typeof(EPDEventType))]
+    public interface IEPDCallbackService
+    {
+        [OperationContract]
+        bool Register(Guid id);
+
+        [OperationContract(IsOneWay = true)]
+        void UnRegister(Guid id);
+    }
+}

+ 160 - 0
Venus/Venus_RT/Devices/EPD/Interface/IEPDService.cs

@@ -0,0 +1,160 @@
+using EPInterface.Data;
+using System;
+using System.Collections.Generic;
+using System.ServiceModel;
+using EPInterface.Datas;
+
+namespace EPInterface
+{
+
+    [ServiceContract]
+    [ServiceKnownType(typeof(EPDState))]
+    [ServiceKnownType(typeof(CRawDataItem))]
+    [ServiceKnownType(typeof(List<long>))]
+    public interface IEPDService
+	{
+  
+        /// <summary>
+        /// 设置当前model
+        /// </summary>
+        /// <param name="model"></param>
+        [OperationContract]
+        void SetModel(int channel, string name, string model);
+
+        [OperationContract]
+        int Heartbeat(int counter);
+
+        [OperationContract]
+        void Online();
+
+        [OperationContract]
+        void Offline();
+
+        [OperationContract]
+        bool IsOnline();
+
+        [OperationContract]
+        void SetOption(string name, string value);
+
+        [OperationContract]
+        string GetOption(string name);
+
+
+        /// <summary>
+        ///RecipeStart
+        /// </summary>
+        /// <param name="recipe"></param>
+        [OperationContract]
+        void RecipeStart(int channel, string name);
+
+        /// <summary>
+        ///RecipeStart
+        /// </summary>
+        /// <param name="model"></param>
+        [OperationContract]
+        void RecipeStop(int channel);
+
+        /// <summary>
+        /// Online 模式,开始计算
+        /// </summary>
+        /// <param name="model"></param>
+        [OperationContract]
+        void Start(int channel, int idx, string name, string model);
+
+        [OperationContract]
+        void StartByConfig(int channel, int idx, string name, EPDConfig config);
+
+        /// <summary>
+        /// online 模式,停止计算
+        /// </summary>
+        [OperationContract]
+        void Stop(int channel);        
+
+        /// <summary>
+        /// online模式,暂停
+        /// </summary>
+        [OperationContract]
+        void Pause(int channel);
+
+        [OperationContract]
+
+        /// <summary>
+        /// online模式,恢复计算
+        /// </summary>
+        void Resume(int channel);
+
+        /// <summary>
+        /// 获取硬件通道数
+        /// </summary>
+
+        [OperationContract]
+        int QueryChannels();
+
+        [OperationContract]
+        List<string> QueryChannelNames();
+
+        [OperationContract]
+        string QueryChannelName(int channel);
+
+        [OperationContract]
+        ///
+        /// 获取当前model <name,xml>
+        ///
+        Tuple<string,string> QueryModel(int channel);
+ 
+        /// <summary>
+        /// 获取 波长信息,频谱图 X 坐标
+        /// </summary>
+        /// <param name="wave"></param>
+        [OperationContract]
+        double[] QueryWave(int channel);
+
+        /// <summary>
+        /// 获取当前帧数据        
+        /// </summary>
+        /// <param name="data"></param>
+        /// <returns>False,为无数据。
+        /// </returns>
+        [OperationContract]
+        CRawDataItem QueryData(int channel);
+
+
+        [OperationContract]
+        ///
+        /// 获取当前State
+        ///
+        EPDState QueryState(int channel);
+        
+        [OperationContract]
+        bool QueryRecordState(int channel);
+
+        [OperationContract]
+        long QueryRecipeTime(int channel);
+
+        [OperationContract]
+        List<long> QueryStepTime(int channel);
+
+        [OperationContract]
+        List<string> QueryConfigList();
+        [OperationContract]
+        string QueryConfig(string name);
+    }
+
+    public interface IRecipeService
+    {
+        [OperationContract]
+        List<string> QueryRecipes();
+
+        [OperationContract]
+        string Load(string name);
+
+
+        [OperationContract]
+        void Save(string name, string recipe);
+
+
+        [OperationContract]
+        void Delete(string name);
+
+    }
+}

+ 108 - 0
Venus/Venus_RT/Devices/IODevices/IoBacksideHe.cs

@@ -0,0 +1,108 @@
+using System;
+using System.Xml;
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.IOCore;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.OperationCenter;
+using Venus_RT.Devices.IODevices;
+using Aitex.Core.RT.Device.Unit;
+
+namespace Venus_RT.Devices
+{
+    /// <summary>
+    /// 先送Setpoint, 然后再设置控制模式, 切换设置模式后立即生效
+    /// </summary>
+    public class IoBacksideHe : BaseDevice, IDevice
+    {
+        private readonly MfcBase1 _mfc;
+        private readonly IoValve _DownValve;
+        private AOAccessor _aoPressure;
+        private AOAccessor _aoCtrlMode;
+
+        public int MinFlowThreshold { get; set;}
+        public int MaxFlowThreshold { get; set; }
+
+        public bool OutOfRange { get; private set; }
+
+        public IoBacksideHe(string module, XmlElement node, string ioModule = "")
+        {
+            base.Module = module;
+            base.Name = node.GetAttribute("id");
+            base.Display = node.GetAttribute("display");
+            base.DeviceID = node.GetAttribute("schematicId");
+
+            _DownValve  = ParseDeviceNode<IoValve>(Module, "downvalve", node);
+            _mfc        = ParseDeviceNode<MfcBase1>(Module, "mfc", node);
+            _aoPressure = ParseAoNode("aoPressureSP", node, ioModule);
+            _aoCtrlMode = ParseAoNode("aoControlMode", node, ioModule);
+        }
+        public bool Initialize()
+        {
+            OutOfRange = false;
+            return true;
+        }
+        public void Monitor()
+        {
+            if(MinFlowThreshold > 0 && _mfc.FeedBack <= MinFlowThreshold)
+            {
+                OutOfRange = true;
+                LOG.Write(eEvent.ERR_BACKSIDE_HE, Module, $"Backside Helium Flow: {_mfc.FeedBack} exceed min threshold value: {MinFlowThreshold}");
+            }
+            else if(MaxFlowThreshold > 0 && _mfc.FeedBack >= MaxFlowThreshold)
+            {
+                OutOfRange = true;
+                LOG.Write(eEvent.ERR_BACKSIDE_HE, Module, $"Backside Helium Flow: {_mfc.FeedBack} exceed max threshold value: {MaxFlowThreshold}");
+            }
+            else
+            {
+                OutOfRange = false;
+            }
+
+            _DownValve.Monitor();
+            _mfc.Monitor();
+        }
+
+        public void Terminate()
+        {
+        }
+
+        public void Reset()
+        {
+        }
+
+        public bool SetBacksideHelium(int mTorr)
+        {
+            _SetRealFloat(_aoPressure, mTorr);
+            SetESCHeControlMode(true);
+            return true;
+        }
+
+        public bool SetFlowThreshold(int nMin, int nMax)
+        {
+            MinFlowThreshold = nMin;
+            MaxFlowThreshold = nMax;
+            return true;
+        }
+
+        public void Flow(double setpoint)
+        {
+            if (setpoint >= 0.01)
+            {
+                _DownValve.TurnValve(true, out _);
+                //this.FlowSP = setpoint;
+            }
+            else
+            {
+                _DownValve.TurnValve(false, out _);
+            }
+
+            _mfc.Ramp(setpoint, 1000);
+            SetESCHeControlMode(false);
+        }
+
+        public void SetESCHeControlMode(bool bPressureMode)
+        {
+            _SetRealFloat(_aoCtrlMode, bPressureMode ? 1 : 0);
+        }
+    }
+}