Browse Source

Endpoint Module.

sangwq 2 years ago
parent
commit
4e8a55f733

+ 3 - 0
Venus/Venus_Core/EventDefine.cs

@@ -34,5 +34,8 @@ namespace Aitex.Core.RT.Log{
 		ERR_BACKSIDE_HE = 1015,
 		INFO_BACKSIDE_HE = 1016,
 		WARN_BACKSIDE_HE = 1017,
+		ERR_PROCESS = 1018,
+		INFO_PROCESS = 1019,
+		WARN_PROCESS = 1020,
 	}
 }

+ 55 - 5
Venus/Venus_Core/Recipe.cs

@@ -176,6 +176,15 @@ namespace Venus_Core
         public Func<RecipeStep, RState> checker;
         [JsonIgnore]
         public Func<RecipeStep, RState> starter;
+        [JsonIgnore]
+        public Func<RecipeStep, RState> ender;
+
+        private int m_StepNo;
+        public int StepNo
+        {
+            get { return m_StepNo; }
+            set { m_StepNo = value; InvokePropertyChanged("StepNo"); }
+        }
 
         private StepType m_StepType;
         [JsonConverter(typeof(StringEnumConverter))]
@@ -192,6 +201,13 @@ namespace Venus_Core
             set { m_Time = value; InvokePropertyChanged("Time"); }
         }
 
+        private string m_EPDConfigName;
+        public string EPDConfigName
+        {
+            get { return m_EPDConfigName; }
+            set { m_EPDConfigName = value; InvokePropertyChanged("EPDConfigName"); }
+        }
+
         private int m_MinEndPointTime;
         public int MinEndPointTime
         {
@@ -206,6 +222,13 @@ namespace Venus_Core
             set { m_MaxEndPointTime = value; InvokePropertyChanged("MaxEndPointTime"); }
         }
 
+        private int m_OverEtchPercent;
+        public int OverEtchPercent
+        {
+            get { return m_OverEtchPercent; }
+            set { m_OverEtchPercent = value;InvokePropertyChanged("OverEtchPercent"); }
+        }
+
         private bool m_CycleStart;
         public bool CycleStart
         {
@@ -239,10 +262,24 @@ namespace Venus_Core
         {
             return _stepTimer.ElapsedMilliseconds; 
         }
+        public void StartStepTimer()
+        {
+            _stepTimer.Restart();
+        }
+
+        private long _lastEPDStepTimne = 0;
+        public void RecordEPDStepTime()
+        {
+            _lastEPDStepTimne = _stepTimer.ElapsedMilliseconds;
+        }
+
+        public long GetLastEPDStepTime()
+        {
+            return _lastEPDStepTimne;
+        }
 
         public RState Start()
         {
-            _stepTimer.Restart();
             starter(this);
 
             foreach (var unit in lstUnit)
@@ -266,21 +303,32 @@ namespace Venus_Core
             if (checker(this) == RState.End)
                 return RState.End;
 
+            bool bStable = true;
             foreach (var unit in lstUnit)
             {
                 var processUnit = unit as ProcessUnitBase;
                 if (processUnit != null)
                 {
                     var state = processUnit.Run(this);
-                    if (state != RState.Running)
-                        return state;
+                    if(Type == StepType.Stable)
+                    {
+                        if (state != RState.Running && state != RState.End)
+                            return state;
+
+                        if (state == RState.Running)
+                            bStable = false;
+                    }
+                    else
+                    {
+                        if (state != RState.Running)
+                            return state;
+                    }
                 }
                 else
                     return RState.Failed;
-
             }
 
-            return RState.Running;
+            return (Type == StepType.Stable && bStable) ? RState.End : RState.Running;
         }
 
         public void End()
@@ -293,6 +341,8 @@ namespace Venus_Core
                     processUnit.End(this);
                 }
             }
+
+            ender(this);
         }
 
         public double RampFactor()

+ 1 - 0
Venus/Venus_Core/VenusDevice.cs

@@ -89,6 +89,7 @@
         ESCHV,
         PendulumValve,
         TurboPump,
+        EndPoint,
     }
 
     public enum StateData

+ 27 - 0
Venus/Venus_RT/Config/LogDefine.json

@@ -295,5 +295,32 @@
     "GlobalDescription_en": "BacksideHelium warning: {0}",
     "Module": "PM",
     "Note": "BacksideHelium warning"
+  },
+  {
+    "Id": 1018,
+    "Level": "Error",
+    "LogEnum": "ERR_PROCESS",
+    "GlobalDescription_zh": "Process alarm: {0}",
+    "GlobalDescription_en": "Process alarm: {0}",
+    "Module": "PM",
+    "Note": "Process Alarm"
+  },
+  {
+    "Id": 1019,
+    "Level": "Info",
+    "LogEnum": "INFO_PROCESS",
+    "GlobalDescription_zh": "Process log: {0}",
+    "GlobalDescription_en": "Process log: {0}",
+    "Module": "PM",
+    "Note": "Process log"
+  },
+  {
+    "Id": 1020,
+    "Level": "Warning",
+    "LogEnum": "WARN_PROCESS",
+    "GlobalDescription_zh": "Process warning: {0}",
+    "GlobalDescription_en": "Process warning: {0}",
+    "Module": "PM",
+    "Note": "Process warning"
   }
 ]

+ 3 - 6
Venus/Venus_RT/Devices/DeviceManager.cs

@@ -5,10 +5,10 @@ using Aitex.Core.Common;
 using Aitex.Core.RT.Device;
 using Aitex.Core.RT.OperationCenter;
 using Aitex.Core.RT.SCCore;
-using Aitex.RT.Device.Custom;
 using MECF.Framework.Common.Equipment;
 using Venus_RT.Modules;
 using Venus_RT.Devices;
+using Venus_RT.Devices.EPD;
 
 namespace Venus_RT.Instances
 {
@@ -93,17 +93,14 @@ namespace Venus_RT.Instances
             AddCustomModuleDevice(new AdixenTurboPump(mod));
             AddCustomModuleDevice(new PendulumValve(mod));
 
+            AddCustomModuleDevice(new EPDClient(mod));
+
             AddCustomDevice(new JetPM(mod));
 
             SmallPinWaferSize = MapWaferSize(SC.GetValue<int>($"System.SmallWafer"));
             MediumPinWaferSize = MapWaferSize(SC.GetValue<int>($"System.MidWafer"));
             BigPinWaferSize = MapWaferSize(SC.GetValue<int>($"System.BigWafer"));
 
-
-            if (SC.GetValue<bool>($"{mod}.EPD.IsEnabled") && SC.GetValue<bool>("System.SetUp.EPDInstalled"))
-            {
-                AddCustomModuleDevice(new EPDDevice(mod.ToString(), "EPD"));
-            }
         }
 
         private WaferSize MapWaferSize(int value)

+ 103 - 0
Venus/Venus_RT/Devices/EPD/Data/ByteReader.cs

@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPD.Data
+{
+    class ByteReader
+    {
+        private byte[] buffer;
+        private int idx;
+        private int length;
+        public int Length => length;
+
+        public ByteReader(byte[] data, int len)
+        {
+            buffer = new byte[len];
+            Buffer.BlockCopy(data, 0, buffer, 0, len);
+            length = len;
+            idx = 0;
+        }
+
+        public void Append(byte[] data, int len)
+        {
+            var buf = new byte[Length + len];
+            Buffer.BlockCopy(buffer, 0, buf, 0, Length);
+            Buffer.BlockCopy(data, 0, buf, Length, len);
+            buffer = buf;
+            length += len;
+        }
+
+        public bool Reset(int curIdx = 0)
+        {
+            if (curIdx < length)
+                idx = curIdx;
+            return idx == curIdx;
+        }
+
+        public bool ReadByte(out byte data)
+        {
+            data = buffer[idx];
+            idx++;
+
+            return true;
+        }
+
+        public bool ReadBytes(byte[] buf, int len)
+        {
+            if (idx + len <= length)
+            {
+                Buffer.BlockCopy(buffer, (int)idx, buf, 0, (int)len);
+                idx += len;
+                return true;
+            }
+            return false;
+        }
+
+        public bool ReadInt(out int data)
+        {
+            data = 0;
+            if (idx + 4 <= length)
+            {
+                data = BitConverter.ToInt32(buffer, (int)idx);
+                idx += 4;
+                return true;
+            }
+            return false;
+        }
+
+        public bool ReadUInt16(out UInt16 data)
+        {
+            data = 0;
+            if (idx + 2 <= length)
+            {
+                data = BitConverter.ToUInt16(buffer, (int)idx);
+                idx += 2;
+                return true;
+            }
+            return false;
+        }
+
+        public bool ReadInt64(out Int64 data)
+        {
+            data = 0;
+            if (idx + 8 <= length)
+            {
+                data = BitConverter.ToInt64(buffer, (int)idx);
+                idx += 8;
+                return true;
+            }
+            return false;
+        }
+
+        public bool ReadString(out string str, int len)
+        {
+            str = System.Text.Encoding.Default.GetString(buffer, idx, len);
+            str = str.Substring(0, str.IndexOf('\0'));
+            idx += len;
+            return true;
+        }
+    }
+}

+ 67 - 0
Venus/Venus_RT/Devices/EPD/Data/ByteStructConverter.cs

@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPD.Data
+{
+    public class ByteStructConverter
+    {
+        public static T ToStruct<T>(byte[] by) where T : struct
+        {
+            int objectSize = Marshal.SizeOf(typeof(T));
+            if (objectSize > by.Length) return default(T);
+
+            // Allocate some unmanaged memory.
+            IntPtr buffer = Marshal.AllocHGlobal(objectSize);
+
+            // Copy the read byte array (byte[]) into the unmanaged memory block.
+            Marshal.Copy(by, 0, buffer, objectSize);
+
+            // Push the memory into a new struct of type (T).
+            T returnStruct = (T)Marshal.PtrToStructure(buffer, typeof(T));
+
+            // Free the unmanaged memory block.
+            Marshal.FreeHGlobal(buffer);
+
+            return returnStruct;
+        }
+
+        public static object ToStruct(byte[] buffer, Type t)
+        {
+            int objectSize = Marshal.SizeOf(t);
+            if (objectSize > buffer.Length) return null;
+
+            // Allocate some unmanaged memory.
+            IntPtr buf = Marshal.AllocHGlobal(objectSize);
+
+            // Copy the read byte array (byte[]) into the unmanaged memory block.
+            Marshal.Copy(buffer, 0, buf, objectSize);
+
+            // Push the memory into a new struct of type (T).
+            object result = Marshal.PtrToStructure(buf, t);
+
+            // Free the unmanaged memory block.
+            Marshal.FreeHGlobal(buf);
+
+            return result;
+        }
+
+        public static byte[] Struct2Bytes(object o)
+        {
+            // create a new byte buffer the size of your struct
+            byte[] buffer = new byte[Marshal.SizeOf(o)];
+            // pin the buffer so we can copy data into it w/o GC collecting it
+            GCHandle bufferHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+            // copy the struct data into the buffer
+            Marshal.StructureToPtr(o, bufferHandle.AddrOfPinnedObject(), false);
+            // free the GC handle
+            bufferHandle.Free();
+
+            return buffer;
+        }
+
+    }
+}

+ 206 - 0
Venus/Venus_RT/Devices/EPD/Data/CytAsciiDefine.cs

@@ -0,0 +1,206 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Documents;
+
+namespace EPD.Data
+{
+    public class CytAsciiDefine
+    {
+        public static Dictionary<int, string> ErrorMap = new Dictionary<int, string>()
+        {
+            {0x0000, "OK"},
+
+            {0x1000, "Format error"},
+            {0x1001, "Parameter invalid"},
+            {0x1002, "EPD not online"},
+            {0x1003, "EPD is initializing"},
+            {0x1004, "EPD config file has invalid setting"},
+            {0x1005, "EPD calculation failed"},
+            {0x1006, "EPD not remote"},
+
+            { 0x1100, "Device has other error" },
+            { 0x1101, "Device nvalid" },
+            { 0x1102, "No Device" },
+            { 0x1103, "Device close failed" },
+            { 0x1104, "Device: order not implemented" },
+            { 0x1105, "Device: a features not found" },
+            { 0x1106, "Device: transfer Error" },
+            { 0x1107, "Device: bad user buffer" },
+            { 0x1108, "Device: out of bounds" },
+            { 0x1109, "Device Saturated" },
+
+            {0x2000, "EPD device error"},
+        };
+
+        public static Dictionary<int, string> StateMap = new Dictionary<int, string>()
+        {
+            {0, "OK"},
+            {1, "SYNTAX ERROR: The number of zero limiters in the header is not correct."},
+            {2, "SYNTAX ERROR: The number of zero limiters in the body is not correct."},
+            {3, "SYNTAX ERROR: The numbers of items in the body do not match the header."},
+            {4, "SYNTAX ERROR: Correct value is required."},
+            {5, "SYNTAX ERROR: Recognizable text character is required"},
+            {6, "MESSAGE ERROR: The numbers of items in the body do not conform to specifications."},
+            {7, "HEADER ERROR: Numerical value is beyond the defined range."},
+            {8, "HEADER ERROR: The text is not of defined size."},
+            {9, "BODY ERROR: Numerical value is beyond the deined range."},
+            {10, "BODY ERROR: The tetx is not of defined size."},
+            {11, "Sensor ID does not match the current sensor setting."},
+            {12, "WatchDog Code: The remote control system is not ready."},
+            {13, "WatchDog Code: The sensor subsystem is not ready"},
+            {14, "WatchDog Code: The cluster control subsystem is not ready."},
+            {15, "WatchDog Code: The available hard disk capacity has reached its upper limit."},
+            {16, "WatchDog Code: Mismatch between step names occurred."},
+            {17, "Step name is missing."},
+            {18, "Required action is beyond the defined range."},
+            {19, "Warning is raised in the sensor subsystem."},
+        };
+    }
+
+    public enum CytAsciiEvent
+    {
+        Event_Message = 0x01,
+
+        Event_SystemError = 0x02,
+
+        Event_StepStart = 0x03,
+
+        Event_StepDelay = 0x04,
+
+        Event_StepNormalize = 0x05,
+
+        Event_StepSatisfied = 0x06,
+
+        Event_StepTrigger = 0x07,
+
+        Event_StepStop = 0x08,
+
+        Event_TrendData = 0x10,
+    }
+
+    public enum CytAsciiCommand
+    {
+        Reset = 0x00,
+
+        RecipeStart = 0x01,
+        RecipeStop = 0x02,
+
+        Complete = 0x03,
+
+        Start = 0x04,
+        Stop = 0x05,
+
+        SetWaferInfo = 0x06,
+
+        QueryCfgList = 0x07,
+
+        QueryState = 0x08,
+
+        QueryVer = 0x09,
+
+        Event = 0x0A,
+
+        Connect = 0x10,
+        Disconnect = 0x11,
+
+        HeartBeat = 0x20,
+
+        QueryData = 0x30,
+
+        QueryRunStatus = 0x31,
+
+        SetRunStatus = 0x32,
+
+        QueryOperateMode = 0x33,
+
+        SetOperateMode = 0x34,
+    }
+
+    public class CytAsciiData
+    {
+        public int MessageID { get; set; }
+        public int CommandID { get; set; }
+        public string SenderID { get; set; }
+        public string ReceiveID { get; set; }
+        public List<string> Params { get; set; }
+
+        public CytAsciiData(int messageID, int commandID, string senderID, string receiveID)
+        {
+            MessageID = messageID;
+            CommandID = commandID;
+            SenderID = senderID;
+            ReceiveID = receiveID;
+            Params = new List<string>();
+        }
+
+        private List<string> ToList()
+        {
+            var lst = new List<string> {MessageID.ToString(), CommandID.ToString(), SenderID, ReceiveID, Params.Count.ToString() };
+            lst.AddRange(Params);
+            return lst;
+        }
+
+        public byte[] ToBytes()
+        {
+            var lst = ToList();
+
+            var length = 2;
+            foreach (var item in lst)
+                length += item.Length + 1;
+
+            var pos = 0;
+            var bytes = new byte[length];
+            foreach (var item in lst)
+            {
+                Buffer.BlockCopy(Encoding.UTF8.GetBytes(item), 0, bytes, pos, item.Length);
+                pos += item.Length + 1;
+            }
+            bytes[pos] = 0x0D;
+            bytes[pos + 1] = 0x0A;
+
+            return bytes;
+        }
+
+        public static List<string> ToList(byte[] bytes, int index, ref int len)
+        {
+            var lst = new List<string>();
+            var startIndex = index;
+
+            for (int i = index; i < index + len - 1; i++)
+            {
+                if (bytes[i] == 0)
+                {
+                    lst.Add(Encoding.UTF8.GetString(bytes, startIndex, i - startIndex));
+                    startIndex = i + 1;
+                }
+                else if (bytes[i] == 0x0D && bytes[i + 1] == 0x0A)
+                {
+                    len = i - index + 2;
+                    return lst;
+                }
+            }
+            len = -1;
+            return lst;
+        }
+    }
+
+    public struct CytAsciiResponseHeader
+    {
+        public static readonly int Size = Marshal.SizeOf(typeof(ResponseHeader));
+
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int token;
+        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
+        public byte channel;
+        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
+        public byte command;
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int errorcode;
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int length;
+    }
+}

+ 217 - 0
Venus/Venus_RT/Devices/EPD/Data/EPDDefine.cs

@@ -0,0 +1,217 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPD.Data
+{
+    public class EPDDefine
+    {
+        public static Dictionary<int, string> ErrorMap = new Dictionary<int, string>()
+        {
+            {0x0000, "OK"},
+
+            {0x1000, "Format error"},
+            {0x1001, "Parameter invalid"},
+            {0x1002, "EPD not online"},
+            {0x1003, "EPD is initializing"},
+            {0x1004, "EPD config file has invalid setting"},
+            {0x1005, "EPD calculation failed"},
+            {0x1006, "EPD not remote"},
+
+            { 0x1100, "Device has other error" },
+            { 0x1101, "Device nvalid" },
+            { 0x1102, "No Device" },
+            { 0x1103, "Device close failed" },
+            { 0x1104, "Device: order not implemented" },
+            { 0x1105, "Device: a features not found" },
+            { 0x1106, "Device: transfer Error" },
+            { 0x1107, "Device: bad user buffer" },
+            { 0x1108, "Device: out of bounds" },
+            { 0x1109, "Device Saturated" },
+
+            {0x2000, "EPD device error"},
+        };
+
+        public static Dictionary<int, string> StateMap = new Dictionary<int, string>()
+        {
+            {0, "Device not ready"},
+            {1, "Running"},
+            {2, "Paused"},
+            {3, "Idle"},
+        };
+
+        public static Dictionary<int, string> RunStatusMap = new Dictionary<int, string>()
+        {
+            {1, "Monitor"},
+            {2, "Monitor Save"},
+            {3, "Capture"},
+            {4, "Process"},
+        };
+
+        public static Dictionary<int, string> ModeMap = new Dictionary<int, string>()
+        {
+            {1, "Local"},
+            {2, "Remote"},
+        };
+    }
+
+    public enum EPDEvent
+    {
+        Event_Message = 0x01,
+
+        Event_SystemError = 0x02,
+
+        Event_StepStart = 0x03,
+
+        Event_StepDelay = 0x04,
+
+        Event_StepNormalize = 0x05,
+
+        Event_StepSatisfied = 0x06,
+
+        Event_StepTrigger = 0x07,
+
+        Event_StepStop = 0x08,
+
+        Event_TrendData = 0x10,
+    }
+
+    public enum EPDCommand
+    {
+        Reset = 0x00,
+
+        RecipeStart = 0x01,
+        RecipeStop = 0x02,
+
+        Complete = 0x03,
+
+        Start = 0x04,
+        Stop = 0x05,
+
+        SetWaferInfo = 0x06,
+
+        QueryCfgList = 0x07,
+
+        QueryState = 0x08,
+
+        QueryVer = 0x09,
+
+        Event = 0x0A,
+
+        Connect = 0x10,
+        Disconnect = 0x11,
+
+        QueryChannelCount = 0x12,
+
+        HeartBeat = 0x20,
+
+        QueryData = 0x30,
+
+        QueryRunStatus = 0x31,
+
+        SetRunStatus = 0x32,
+
+        QueryOperateMode = 0x33,
+
+        SetOperateMode = 0x34,
+
+        QueryCurrentConfig = 0x40,
+        SetCurrentConfig = 0x41,
+
+        GetSensorStatus = 0x101,
+        StartMeasurement = 0x102,
+        GetRecipesList = 0x103,
+        StopMeasurement = 0x104,
+        AsciiEvent = 0x105,
+        
+        QueryChannalNames = 0x110
+    }
+
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 10, CharSet = CharSet.Ansi)]
+    public struct PacketHeader
+    {
+        public static readonly int Size = Marshal.SizeOf(typeof(PacketHeader));
+
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int token;
+        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
+        public byte channel;
+        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
+        public byte command;
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int length;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 14, CharSet = CharSet.Ansi)]
+    public struct ResponseHeader
+    {
+        public static readonly int Size = Marshal.SizeOf(typeof(ResponseHeader));
+
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int token;
+        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
+        public byte channel;
+        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
+        public byte command;
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int errorcode;
+        [MarshalAs(UnmanagedType.U4, SizeConst = 4)]
+        public int length;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 1792, CharSet = CharSet.Ansi)]
+    public struct WaferData
+    {
+        public static readonly int Size = Marshal.SizeOf(typeof(WaferData));
+
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] WaferID;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Recipe;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] LotName;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Cassette;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Slot;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] ToolID;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] WorkFlow;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Custom1;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Custom2;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Custom3;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Custom4;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Custom5;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Date;
+        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
+        public char[] Time;
+
+        public WaferData(int _)
+        {
+            WaferID = new char[128];
+            Recipe = new char[128];
+            LotName = new char[128];
+            Cassette = new char[128];
+            Slot = new char[128];
+            ToolID = new char[128];
+            WorkFlow = new char[128];
+            Custom1 = new char[128];
+            Custom2 = new char[128];
+            Custom3 = new char[128];
+            Custom4 = new char[128];
+            Custom5 = new char[128];
+            Date = new char[128];
+            Time = new char[128];
+        }
+    }
+}

+ 517 - 0
Venus/Venus_RT/Devices/EPD/Data/EPDSocketClient.cs

@@ -0,0 +1,517 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace EPD.Data
+{
+    public class EPDSocketClient
+    {
+        public Action OnConnect;
+        public Action OnDisconnect;
+
+        public Action<string, byte[]> OnSocketSend;
+        public Action<string, byte[], int> OnSocketReceive;
+        public Action<EPDCommand, object> OnEPDReply;
+        public Action<int/*Command*/, int/*Error code*/> OnError;
+
+        private SocketClientWrapper socketWrapper;
+        
+        private object lockerReceive = new object();
+        private ByteReader dataReader;
+        private ResponseHeader dataHeader;
+
+        private string channel;
+        public string Channel
+        {
+            get => channel;
+            set
+            {
+                if (string.IsNullOrEmpty(value))
+                    return;
+                channel = value;
+
+                if (byte.TryParse(value, out byte res))
+                    _channel = res;
+                else
+                    _channel = 0;
+            }
+        }
+
+        private byte _channel;
+
+        public EPDSocketClient()
+        {
+            socketWrapper = new SocketClientWrapper();
+            socketWrapper.OnDataChanged += Received;
+            socketWrapper.OnConnected += Connected;
+            socketWrapper.OnDisconnected += Disconnected;
+        }
+
+        private void Received(byte[] data, int offset, int size)
+        {
+            lock (lockerReceive)
+            {
+                dataHeader.token = 0;
+
+                if (dataReader == null)
+                {
+                    if (data[0] == 1 && data[1] == 1 && data[2] == 0 && data[3] == 0)
+                    {
+                        dataReader = new ByteReader(data, size);
+
+                        if (size >= ResponseHeader.Size)
+                        {
+                            var buf = new byte[ResponseHeader.Size];
+                            dataReader.ReadBytes(buf, ResponseHeader.Size);
+                            dataHeader = ByteStructConverter.ToStruct<ResponseHeader>(buf);
+                        }
+                    }
+                    else if (data[size - 3] == 0 && data[size - 2] == 0x0D && data[size - 1] == 0x0A)
+                    {
+                        var lst = CytAsciiData.ToList(data, 0, ref size);
+                        if (size > 0 && lst.Count >= 4)
+                        {
+                            var cmd = ProcessAsciiReceived(lst);
+                            OnSocketReceive?.Invoke(cmd, data, size);
+                        }
+                    }
+                }
+                else
+                    dataReader.Append(data, size);
+
+                if (dataHeader.token != 0)
+                {
+                    if (dataReader.Length >= dataHeader.length + 14)
+                    {
+                        var cmd = ProcessReceived();
+                        OnSocketReceive?.Invoke(cmd, data, dataHeader.length + 14);
+                    }
+                    else
+                        OnSocketReceive?.Invoke("", data, data.Length);
+                }
+            }
+        }
+
+        private string ProcessAsciiReceived(List<string> lst)
+        {
+            switch (lst[1].Trim())
+            {
+                case "101":
+                    OnEPDReply?.Invoke(EPDCommand.GetSensorStatus, lst.GetRange(5, 2));
+                    return "GetSensorStatus";
+                case "102":
+                    OnEPDReply?.Invoke(EPDCommand.StartMeasurement, lst.GetRange(5, lst.Count - 5));
+                    return "StartMeasurement";
+                case "103":
+                    OnEPDReply?.Invoke(EPDCommand.GetRecipesList, lst.GetRange(5, lst.Count - 5));
+                    return "GetRecipesList";
+                case "104":
+                    OnEPDReply?.Invoke(EPDCommand.StopMeasurement, lst.GetRange(5, lst.Count - 5));
+                    return "StopMeasurement";
+                case "5":
+                    OnEPDReply?.Invoke(EPDCommand.AsciiEvent, lst[5]);
+                    return lst[5];
+                case "110":
+                    OnEPDReply?.Invoke(EPDCommand.QueryChannalNames, lst.GetRange(8, lst.Count-8));
+                    return "AllChannelNames";
+            }
+            return "";
+        }
+
+        private string ProcessReceived()
+        {
+            if (dataHeader.errorcode != 0)
+                OnError?.Invoke(dataHeader.command, dataHeader.errorcode);
+
+            switch (dataHeader.command)
+            {
+                case (int)EPDCommand.QueryCfgList:
+                    if (dataReader.ReadInt(out int count))
+                    {
+                        var cfgs = new List<string>();
+                        var tmp = new byte[256];
+                        for (int i = 0; i < count; i++)
+                        {
+                            if (dataReader.ReadBytes(tmp, 256))
+                            {
+                                var name = Encoding.UTF8.GetString(tmp);
+                                name = name.Substring(0, name.IndexOf('\0'));
+                                cfgs.Add(name);
+                            }
+                        }
+                        OnEPDReply?.Invoke(EPDCommand.QueryCfgList, cfgs);
+                    }
+                    break;
+                case (int)EPDCommand.QueryState:
+                    if (dataReader.ReadUInt16(out ushort state))
+                        OnEPDReply?.Invoke(EPDCommand.QueryState, state);
+                    break;
+                case (int)EPDCommand.QueryOperateMode:
+                    if (dataReader.ReadUInt16(out ushort mode))
+                        OnEPDReply?.Invoke(EPDCommand.QueryOperateMode, mode);
+                    break;
+                case (int)EPDCommand.QueryRunStatus:
+                    if (dataReader.ReadUInt16(out ushort runStatus))
+                        OnEPDReply?.Invoke(EPDCommand.QueryRunStatus, runStatus);
+                    break;
+                case (int)EPDCommand.QueryVer:
+                    {
+                        var tmp = new byte[32];
+                        if (dataReader.ReadBytes(tmp, 32))
+                        {
+                            var ver = Encoding.UTF8.GetString(tmp);
+                            OnEPDReply?.Invoke(EPDCommand.QueryVer, ver.Substring(0, ver.IndexOf('\0')));
+                        }
+                    }
+                    break;
+                case (int)EPDCommand.Event:
+                    dataReader.ReadInt(out int type);
+                    dataReader.ReadInt64(out long ticket);
+                    var paras = new byte[16];
+                    if (type == 17)
+                        Console.WriteLine();
+                    dataReader.ReadBytes(paras, 16);
+                    dataReader.ReadString(out string desc, 256);
+
+                    var lst = new List<object> { type, ticket, paras, desc};
+                    OnEPDReply?.Invoke(EPDCommand.Event, lst);
+                    break;
+                case (int)EPDCommand.Connect:
+                    OnEPDReply?.Invoke(EPDCommand.Connect, null);
+                    break;
+                case (int)EPDCommand.Disconnect:
+                    OnEPDReply?.Invoke(EPDCommand.Disconnect, null);
+                    break;
+                case (int)EPDCommand.QueryChannelCount:
+                    if (dataReader.ReadByte(out byte res))
+                        OnEPDReply?.Invoke(EPDCommand.QueryChannelCount, res);
+                    break;
+                case (int)EPDCommand.Start:
+                    OnEPDReply?.Invoke(EPDCommand.Start, null);
+                    break;
+                case (int)EPDCommand.RecipeStart:
+                    OnEPDReply?.Invoke(EPDCommand.RecipeStart, null);
+                    break;
+                case (int)EPDCommand.QueryCurrentConfig:
+                    if (dataReader.ReadString(out string cfg, dataHeader.length))
+                        OnEPDReply?.Invoke(EPDCommand.QueryCurrentConfig, cfg);
+                    break;
+                default:
+                    break;
+            }
+            dataReader = null;
+
+            return ((EPDCommand)dataHeader.command).ToString();
+        }
+
+        public void Connect(string ip, int port)
+        {
+            socketWrapper.Connect(ip, port);
+        }
+
+        public void Disconnect()
+        {
+           socketWrapper.Disconnect();
+        }
+
+        private void Connected()
+        {
+            OnConnect?.Invoke();
+        }
+
+        private void Disconnected()
+        {
+            OnDisconnect?.Invoke();
+        }
+
+        public void ConnectEPD()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.Connect, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+            OnSocketSend?.Invoke("Connect", data);
+        }
+
+        public void DisconnectEPD()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.Disconnect, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+            OnSocketSend?.Invoke("Disconnect", data);
+        }
+
+        public void QueryState()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.QueryState, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryState", data);
+        }
+
+        public void QueryVersion()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.QueryVer, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryVer", data);
+        }
+
+        public void QueryMode()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.QueryOperateMode, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryOperateMode", data);
+        }
+
+        public void ResetEPD()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.Reset, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("Reset", data);
+        }
+
+        public void SendHeartBeat()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = 0, command = (byte)EPDCommand.HeartBeat, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("HeartBeat", data);
+        }
+
+        public void SetMode(byte mode) // 1 local; 2 remote
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.SetOperateMode, length = 4 };
+
+            var data = new byte[PacketHeader.Size + 4];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+            Buffer.BlockCopy(new byte[] { mode, 0, 0, 0 }, 0, data, PacketHeader.Size, 4);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("SetOperateMode", data);
+        }
+
+        public void QueryRunStatus()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.QueryRunStatus, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryRunStatus", data);
+        }
+
+        public void SetRunStatus(byte sta) // 1 Monitor; 2 Save; 3:Capture; 4:Process
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.SetRunStatus, length = 4 };
+
+            var data = new byte[PacketHeader.Size + 4];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+            Buffer.BlockCopy(new byte[] { sta, 0, 0, 0 }, 0, data, PacketHeader.Size, 4);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryOperateMode", data);
+        }
+
+        public void QueryConfigList()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.QueryCfgList, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryCfgList", data);
+        }
+
+        public void RecipeStart(int channel, string name)
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.RecipeStart, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("RecipeStart", data);
+        }
+
+        public void RecipeStop(int channel)
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.RecipeStop, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("RecipeStop", data);
+        }
+
+        public void Start(byte channel, string name)
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.Start, length = 256 };
+
+            var data = new byte[PacketHeader.Size + 256];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            char[] chars = name.ToCharArray();
+            Encoding.UTF8.GetBytes(chars, 0, chars.Length, data, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("Start", data);
+        }
+
+        public void Stop(byte channel)
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.Stop, length = 0 };
+
+            var data = new byte[PacketHeader.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("Stop", data);
+        }
+
+        public void SetWaferInfo(WaferData wafer)
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.SetWaferInfo, length = WaferData.Size };
+
+            var data = new byte[PacketHeader.Size + WaferData.Size];
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            var buffer = ByteStructConverter.Struct2Bytes(wafer);
+            Buffer.BlockCopy(buffer, 0, data, PacketHeader.Size, WaferData.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("SetWaferInfo", data);
+        }
+
+        public void QueryCurrentConfig()
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.QueryCurrentConfig, length = 0 };
+            var data = new byte[PacketHeader.Size];
+
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("QueryCurrentConfig", data);
+        }
+
+        public void SetCurrentConfig(string content)
+        {
+            var header = new PacketHeader() { token = 0x0101, channel = _channel, command = (byte)EPDCommand.SetCurrentConfig, length = 256 };
+            var data = new byte[PacketHeader.Size + header.length];
+
+            Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+
+            char[] chars = content.ToCharArray();
+            Encoding.UTF8.GetBytes(chars, 0, chars.Length, data, PacketHeader.Size);
+
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("SetCurrentConfig", data);
+        }
+
+        #region ASCII
+
+        private int messageID = 3845;
+        public void GetSensorStatus()
+        {
+            var ascii = new CytAsciiData(messageID++, 1, "H", Channel);
+
+            var data = ascii.ToBytes();
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("GetSensorStatus", data);
+        }
+
+        public void GetRecipeList()
+        {
+            var ascii = new CytAsciiData(messageID++, 3, "H", Channel);
+
+            var data = ascii.ToBytes();
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("GetRecipesList", data);
+        }
+
+        public void StartMeasurement(string recipe)
+        {
+            var ascii = new CytAsciiData(messageID++, 2, "H", Channel);
+            ascii.Params = new List<string> { recipe, "5", "Lot ID", "Cassette ID", "Techno ID" };
+
+            var data = ascii.ToBytes();
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("StartMeasurement", data);
+        }
+
+        public void StopMeasurement()
+        {
+            var ascii = new CytAsciiData(messageID++, 4, "H", Channel);
+            ascii.Params = new List<string> { "STOP" };
+            
+            var data = ascii.ToBytes();
+            socketWrapper.Send(data);
+
+            OnSocketSend?.Invoke("StopMeasurement", data);
+        }
+
+        #endregion
+
+        public void QueryChannels(bool isAscii)
+        {
+            byte[] data;
+            if (isAscii)
+            {
+                var ascii = new CytAsciiData(messageID++, 10, "H", "");
+                data = ascii.ToBytes();
+            }
+            else
+            {
+                var header = new PacketHeader() { token = 0x0101, channel = 0, command = (byte)EPDCommand.QueryChannelCount, length = 0 };
+                data = new byte[PacketHeader.Size];
+                Buffer.BlockCopy(ByteStructConverter.Struct2Bytes(header), 0, data, 0, PacketHeader.Size);
+            }
+            socketWrapper.Send(data);
+            OnSocketSend?.Invoke("QueryChannels", data);
+        }
+    }
+}

+ 24 - 0
Venus/Venus_RT/Devices/EPD/Data/LogItem.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPD.Data
+{
+    public class LogItem
+    {
+        public string Time { get; set; }
+        public string Sender { get; set; }
+        public string Type { get; set; }
+        public string Message { get; set; }
+
+        public LogItem(string time, string sender, string type, string message)
+        {
+            Time = time;
+            Sender = sender;
+            Type = type;
+            Message = message;
+        }
+    }
+}

+ 55 - 0
Venus/Venus_RT/Devices/EPD/Data/NotifyObject.cs

@@ -0,0 +1,55 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace EPD.Data
+{
+    public class NotifyObject : INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        protected void Set<T>(ref T property, T value, [CallerMemberName] string memberName = "")
+        {
+            property = value;
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
+        }
+
+        public void RaisePropertyChanged(string memberName)
+        {
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
+        }
+    }
+
+    public class NotifyControl : UserControl, INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        public void Set<T>(ref T field, T newValue, [CallerMemberName] string propertyName = "")
+        {
+            field = newValue;
+            RaisePropertyChanged(propertyName);
+        }
+
+        public void RaisePropertyChanged(string memberName)
+        {
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
+        }
+    }
+
+    public class NotifyWindow : Window, INotifyPropertyChanged
+    {
+        public event PropertyChangedEventHandler PropertyChanged;
+
+        public void Set<T>(ref T field, T newValue, [CallerMemberName] string propertyName = "")
+        {
+            field = newValue;
+            RaisePropertyChanged(propertyName);
+        }
+
+        public void RaisePropertyChanged(string memberName)
+        {
+            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(memberName));
+        }
+    }
+}

+ 181 - 0
Venus/Venus_RT/Devices/EPD/Data/SocketClientWrapper.cs

@@ -0,0 +1,181 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPD.Data
+{
+    public class SocketClientWrapper : IDisposable
+    {
+        public Action OnConnected;
+        public Action OnDisconnected;
+        public Action<byte[] /*data*/, int /*offset*/, int /*size*/> OnDataChanged;
+
+        private static Object _lockerSender = new Object();
+
+        public class ClientStateObject
+        {
+            public const int BufferSize = 16384;
+            public Socket WorkSocket = null;
+            public byte[] CacheBufferByteData = new byte[BufferSize];
+        }
+
+        private Socket socket;
+
+        public bool IsConnected { get { return socket != null && socket.Connected; } }
+
+        public SocketClientWrapper()
+        {
+        }
+
+        ~SocketClientWrapper()
+        {
+            Dispose();
+        }
+
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="address">127.0.0.1:15000</param>
+        public void Connect(string ip, int port)
+        {
+            try
+            {
+                var remote = new IPEndPoint(IPAddress.Parse(ip), port);
+
+                Dispose();
+
+                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+                socket.BeginConnect(remote, ConnectCallback, socket);
+
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Trace.Write("Error in Connect, " + ex.Message);
+            }
+
+        }
+        private void ConnectCallback(IAsyncResult ar)
+        {
+            try
+            {
+                var client = (Socket)ar.AsyncState;
+                client.EndConnect(ar);
+
+                if (OnConnected != null)
+                    OnConnected();
+
+                System.Diagnostics.Trace.Write("Connected");
+
+                var state = new ClientStateObject();
+                state.WorkSocket = socket;
+
+                socket.BeginReceive(state.CacheBufferByteData, 0, ClientStateObject.BufferSize, 0, ReceiveCallback, state);
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Trace.Write("Error in ConnectCallback, " + ex.Message);
+            }
+        }
+
+        public void Disconnect()
+        {
+            Dispose();
+
+            if (OnDisconnected != null)
+                OnDisconnected();
+        }
+
+        private void ReceiveCallback(IAsyncResult ar)
+        {
+            try
+            {
+                if (!IsConnected)
+                    return;
+
+                ClientStateObject state = (ClientStateObject)ar.AsyncState;
+                Socket client = state.WorkSocket;
+
+                int bytesRead = client.EndReceive(ar);
+                if (bytesRead > 0)
+                {
+                    if (OnDataChanged != null)
+                        OnDataChanged(state.CacheBufferByteData, 0, bytesRead);
+
+                    client.BeginReceive(state.CacheBufferByteData, 0, ClientStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
+                }
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Trace.Write("Error in ReceiveCallback, " + ex.Message);
+
+                Disconnect();
+            }
+        }
+
+        public bool Send(byte[] byteData)
+        {
+            try
+            {
+                if (!IsConnected)
+                    return false;
+
+                lock (_lockerSender)
+                {
+                    socket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), socket);
+                }
+
+                return true;
+
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Trace.Write("Error int Write, " + ex.Message);
+            }
+
+            return false;
+        }
+
+        private void SendCallback(IAsyncResult ar)
+        {
+            try
+            {
+                lock (_lockerSender)
+                {
+                    Socket client = (Socket)ar.AsyncState;
+
+                    client.EndSend(ar);
+                }
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Trace.Write("Error in SendCallback, " + ex.Message);
+            }
+        }
+
+        public void Dispose()
+        {
+            try
+            {
+                if (socket != null)
+                {
+                    if (IsConnected)
+                    {
+                        socket.Shutdown(SocketShutdown.Both);
+                    }
+
+                    socket.Close();
+                    socket.Dispose();
+                    socket = null;
+                }
+            }
+            catch (Exception ex)
+            {
+                System.Diagnostics.Trace.Write("Error in Dispose, " + ex.Message);
+            }
+        }
+    }
+}

+ 55 - 0
Venus/Venus_RT/Devices/EPD/Data/StepItem.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace EPD.Data
+{
+    public class StepItem: NotifyObject
+    {
+        private string index;
+        public string Index { get => index; set => Set(ref index, value, "Index"); }
+
+        private bool running = false;
+        public bool Running { get => running; set => Set(ref running, value, "Running"); }
+
+        public string Name { get; set; }
+        public int SelectedEndByIndex { get; set; }
+
+        private List<string> configs;
+        public List<string> Configs { get => configs; set => Set(ref configs, value, "Configs"); } 
+       
+        public string SelectedConfig { get; set; }
+
+        private double currentTime;
+        public double CurrentTime { get => currentTime; set => Set(ref currentTime, value, "CurrentTime"); }
+        
+        public string MaxTime { get; set; }
+        public string Time { get; set; }
+        public bool ErrorIfNoEPD { get; set; }
+
+        private int epdStatus;
+        public int EPDStatus { get => epdStatus; set => Set(ref epdStatus, value, "EPDStatus"); }
+
+        public StepItem(string name)
+        {
+            Name = name;
+            SelectedEndByIndex = 0;
+            MaxTime = "10";
+        }
+
+        public static void Sort(Collection<StepItem> items)
+        {
+            for (int i = 0; i < items.Count; i++)
+                items[i].Index = $"Step {i + 1}";
+        }
+
+        public static void UpdateConfigs(Collection<StepItem> items, List<string> configs)
+        {
+            for (int i = 0; i < items.Count; i++)
+                items[i].Configs = configs;
+        }
+    }
+}

+ 261 - 0
Venus/Venus_RT/Devices/EPD/EPDClient.cs

@@ -0,0 +1,261 @@
+using System.Text;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Venus_RT.Modules;
+using MECF.Framework.Common.Communications;
+using MECF.Framework.Common.Equipment;
+using Venus_Core;
+using Aitex.Core.RT.SCCore;
+using Aitex.Core.RT.Event;
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Log;
+using Aitex.Core.Util;
+using EPD.Data;
+
+namespace Venus_RT.Devices.EPD
+{
+    
+    class EPDClient : IDevice
+    {
+        public enum EDPStatus
+        {
+            Idle,
+            Running,
+            Error,
+        }
+
+        private EPDSocketClient _socketClient;
+        private string _ip;
+        private int _port;
+        private int _channel;
+        private Stopwatch _heartBeatTimer = new Stopwatch();
+        private bool _isHeartBeatReceived;
+        private readonly R_TRIG _epdIdle = new R_TRIG();
+
+        public bool Captured { get; private set; }
+
+        public bool IsEPDConnected { get; private set; }
+
+        public EDPStatus Status { get; private set; }
+
+        public List<string> CFGFileList { get; private set; }
+
+        public string Module { get; set; }
+        public string Name { get; set; }
+        public string EPDVersion { get; private set; }
+        public string EPDState { get; private set; }
+        public string SensorStatus { get; private set; }
+        public string EPDRunStatus { get; private set; }
+        public string EPDMode { get; private set; }
+
+        public EPDClient(ModuleName mod)
+        {
+            Name = VenusDevice.EndPoint.ToString();
+            Module = mod.ToString();
+
+            _ip = "";
+            _port = 123;
+            _channel = 0;
+
+            _socketClient = new EPDSocketClient();
+            _socketClient.OnConnect += OnConnect;
+            _socketClient.OnDisconnect += OnDisConnect;
+            _socketClient.OnSocketSend += OnEPDSocketSend;
+            _socketClient.OnSocketReceive += OnEPDSocketReceive;
+            _socketClient.OnEPDReply += OnEPDReply;
+            _socketClient.OnError += OnError;
+        }
+
+        public bool Initialize()
+        { 
+            _socketClient.Connect(_ip, _port);
+            _socketClient.ConnectEPD();
+            _socketClient.SetMode(2);       // 1 local; 2 remote
+            _socketClient.SetRunStatus(3);  // 1 Monitor; 2 Save; 3:Capture; 4:Process
+            _socketClient.QueryConfigList();
+            return true;
+        }
+
+        public void Monitor()
+        {
+            HeartBeat();
+        }
+
+        public void Reset()
+        { }
+
+        public void Terminate()
+        {
+            Status = EDPStatus.Idle;
+            _socketClient.DisconnectEPD();
+            _socketClient.Disconnect();
+        }
+
+        public void RecipeStart()
+        {
+            _socketClient.RecipeStart(_channel, "");
+            Status = EDPStatus.Running;
+        }
+
+        public void RecipeStop()
+        {
+            _socketClient.RecipeStop(_channel);
+            Status = EDPStatus.Idle;
+        }
+
+        public void StepStart(string cfgName)
+        {
+            _socketClient.Start((byte)_channel, cfgName);
+            Status = EDPStatus.Running;
+        }
+
+        public void StepStop()
+        {
+            _socketClient.Stop((byte)_channel);
+        }
+
+        private void HeartBeat()
+        {
+            _epdIdle.CLK = Status == EDPStatus.Idle;
+            if (_epdIdle.Q)
+            {
+                _socketClient.SendHeartBeat();
+                _heartBeatTimer.Restart();
+                _isHeartBeatReceived = false;
+            }
+            else if(_epdIdle.M)
+            {
+                if(_heartBeatTimer.ElapsedMilliseconds > 5000)
+                {
+                    if (!_isHeartBeatReceived)
+                    {
+                        LOG.Write(eEvent.ERR_ENDPOINT, Module, "HeartBeat Error, EndPoint Device did not response in 5 seconds");
+                    }
+
+                    _socketClient.SendHeartBeat();
+                    _heartBeatTimer.Restart();
+                    _isHeartBeatReceived = false;
+                }
+            }
+            else
+            {
+                _heartBeatTimer.Stop();
+            }
+        }
+
+        private void OnConnect()
+        {
+            LOG.Write(eEvent.INFO_ENDPOINT, Module, "Endpoint connected");
+        }
+
+        private void OnDisConnect()
+        {
+            LOG.Write(eEvent.INFO_ENDPOINT, Module, "Endpoint disconnected");
+        }
+
+        private void OnEPDSocketReceive(string type, byte[] data, int length)
+        {
+            var content = StringJoin(" ", data, length);
+            LOG.Write(eEvent.INFO_ENDPOINT, Module, $"Endpoint Receive: {content}");
+        }
+
+        private void OnEPDSocketSend(string type, byte[] data)
+        {
+            var content = StringJoin(" ", data, data.Length);
+            LOG.Write(eEvent.INFO_ENDPOINT, Module, $"Endpoint Send: {content}");
+        }
+
+        private void OnError(int command, int errorCode)
+        {
+            var strError = errorCode.ToString();
+            if (EPDDefine.ErrorMap.ContainsKey(errorCode))
+                strError = EPDDefine.ErrorMap[errorCode];
+
+            string ErrorInfo = $"EndPoint Command {(EPDCommand)command} Failed: {strError}";
+            LOG.Write(eEvent.ERR_ENDPOINT, Module, ErrorInfo);
+        }
+
+        private void OnEPDReply(EPDCommand cmd, object obj)
+        {
+            switch (cmd)
+            {
+                case EPDCommand.SetWaferInfo:
+                    break;
+                case EPDCommand.QueryCfgList:
+                    if (obj is List<string>)
+                        CFGFileList = (List<string>)obj;
+                    break;
+                case EPDCommand.QueryState:
+                    if (obj is ushort state && EPDDefine.StateMap.ContainsKey(state))
+                        EPDState = EPDDefine.StateMap[state];
+                    break;
+                case EPDCommand.QueryVer:
+                    EPDVersion = obj.ToString();
+                    break;
+                case EPDCommand.Connect:
+                    IsEPDConnected = true;
+                    break;
+                case EPDCommand.HeartBeat:
+                    _isHeartBeatReceived = true;
+                    break;
+                case EPDCommand.QueryRunStatus:
+                    if (obj is ushort sta && EPDDefine.RunStatusMap.ContainsKey(sta))
+                    {
+                        EPDRunStatus = EPDDefine.RunStatusMap[sta];
+                    }
+                    break;
+                case EPDCommand.QueryOperateMode:
+                    if (obj is ushort mode && EPDDefine.ModeMap.ContainsKey(mode))
+                    {
+                        EPDMode = EPDDefine.ModeMap[mode];
+                    }
+                    break;
+                case EPDCommand.GetSensorStatus:
+                    if (obj is List<string> statusLst && statusLst.Count >= 2)
+                    {
+                        SensorStatus = statusLst[0];
+                    }
+                    break;
+                case EPDCommand.GetRecipesList:
+                    if (obj is List<string> objs)
+                    {
+                        CFGFileList = objs.GetRange(3, objs.Count - 3);
+                        SensorStatus = objs[0];
+                    }
+                    break;
+                case EPDCommand.Event:
+                    var lst = obj as List<object>;
+                    if (lst[0] is int type && type == 7)
+                        Captured = true;
+                    break;
+                case EPDCommand.AsciiEvent:
+                    if (obj.ToString() == "ENDPOINT")
+                        Captured = true;
+                    break;
+                case EPDCommand.QueryChannelCount:
+                case EPDCommand.QueryChannalNames:
+                case EPDCommand.RecipeStart:
+                case EPDCommand.RecipeStop:
+                case EPDCommand.Start:
+                case EPDCommand.Stop:
+                default:
+                    break;
+            }
+        }
+
+        private string StringJoin(string separator, byte[] values, int length)
+        {
+            if (values == null || values.Length == 0)
+                return string.Empty;
+
+            if (separator == null)
+                separator = string.Empty;
+
+            var sb = new StringBuilder($"{values[0]:X2}");
+            for (int i = 1; i < length; i++)
+                sb.Append($"{separator}{values[i]:X2}");
+
+            return sb.ToString();
+        }
+    }
+}

+ 23 - 48
Venus/Venus_RT/Devices/JetPM.cs

@@ -3,14 +3,16 @@ using Aitex.Core.RT.Device;
 using Aitex.Core.RT.Device.Unit;
 using Aitex.Core.RT.Event;
 using Aitex.Core.RT.SCCore;
-using Aitex.RT.Device.Custom;
 using Aitex.Core.RT.Log;
 using MECF.Framework.Common.Device.Bases;
 using MECF.Framework.Common.Equipment;
 using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.PMs;
 using System;
+using System.Collections.Generic;
 using Venus_Core;
 using Venus_RT.Devices.IODevices;
+using Venus_RT.Devices.EPD;
+
 namespace Venus_RT.Devices
 {
     enum ValveType
@@ -119,6 +121,14 @@ namespace Venus_RT.Devices
         private readonly IoGasStick _gasLineN2;
         private readonly IoBacksideHe _backsideHe;
 
+        // EndPoint 
+        private readonly EPDClient _epdClient;
+
+        public List<string> EPDCfgList => _epdClient.CFGFileList;
+        public bool EPDCaptured => _epdClient.Captured;
+        public bool EPDConnected => _epdClient.IsEPDConnected;
+
+
         // 盖子的状态
         public bool IsLidClosed => _Lid.OFFFeedback;
         public bool IsLidLoadlockClosed => _LidLoadlock.OFFFeedback;
@@ -303,7 +313,7 @@ namespace Venus_RT.Devices
             _PM_SlitDoor_Closed     = DEVICE.GetDevice<IoSensor>($"{Module}.SensorSlitDoorClosed");
             _TurboPumpInterlock     = DEVICE.GetDevice<IoSensor>($"{Module}.TurboPumpInterlock");
 
-        _ForelineTC = DEVICE.GetDevice<IoHeater>($"{Module}.ForelineHeater");
+            _ForelineTC = DEVICE.GetDevice<IoHeater>($"{Module}.ForelineHeater");
 
             _SignalTower = DEVICE.GetDevice<IoSignalTower>($"{Module}.SignalTower");
 
@@ -336,7 +346,7 @@ namespace Venus_RT.Devices
 
             _ESCHV          = DEVICE.GetDevice<ESC5HighVoltage>($"{Module}.{VenusDevice.ESCHV}");
             _TurboPump      = DEVICE.GetDevice<AdixenTurboPump>($"{Module}.{VenusDevice.TurboPump}");
-            _pendulumValve  = DEVICE.GetDevice<PendulumValve>($"{ Module}.{VenusDevice.PendulumValve}");
+            _pendulumValve  = DEVICE.GetDevice<PendulumValve>($"{Module}.{VenusDevice.PendulumValve}");
 
             if (SC.GetValue<bool>($"{mod}.Chiller.EnableChiller") &&
                 SC.GetValue<int>($"{mod}.Chiller.CommunicationType") == (int)CommunicationType.RS232 &&
@@ -366,6 +376,8 @@ namespace Venus_RT.Devices
             {
                 _Match = DEVICE.GetDevice<AdTecMatch>($"{Module}.{VenusDevice.Match}");
             }
+
+            _epdClient = DEVICE.GetDevice<EPDClient>($"{Module}.{VenusDevice.EndPoint}");
         }
 
 
@@ -778,63 +790,26 @@ namespace Venus_RT.Devices
 
 
         #region EndPoint
-
-        public bool CheckEndPoint()
-        {
-            EPDDevice epd = DEVICE.GetDevice<EPDDevice>($"{Module}.EPD");
-
-            if (epd == null)
-                return false;
-
-            return epd.IsEnd;
-        }
-
-
-        public void StartEndPoint(string config, int index)
+        public void EPDRecipeStart()
         {
-            EPDDevice epd = DEVICE.GetDevice<EPDDevice>($"{Module}.EPD");
-
-            if (epd == null)
-                return;
-
-            epd.StepStart(config, index);
-
+            _epdClient.RecipeStart();
         }
 
-        public void StopEndPoint()
+        public void EPDRecipeStop()
         {
-            EPDDevice epd = DEVICE.GetDevice<EPDDevice>($"{Module}.EPD");
-
-            if (epd == null)
-                return;
-
-            epd.StepStop();
-
+            _epdClient.RecipeStop();
         }
 
-        public void EndPointRecipeStop()
+        public void EPDStepStart(string cfgName)
         {
-            EPDDevice epd = DEVICE.GetDevice<EPDDevice>($"{Module}.EPD");
-
-            if (epd == null)
-                return;
-
-            epd.RecipeStop();
-
+            _epdClient.StepStart(cfgName);
         }
 
-        public void EndPointRecipeStart(string recipeName)
+        public void EPDStepStop()
         {
-            EPDDevice epd = DEVICE.GetDevice<EPDDevice>($"{Module}.EPD");
-
-            if (epd == null)
-                return;
-
-            epd.RecipeStart(recipeName);
-
+            _epdClient.StepStop();
         }
 
-
         #endregion
 
         public void SetBacksideHeFlow(double flow)

+ 34 - 50
Venus/Venus_RT/Modules/PMs/ProcessDefine.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 //using System.
 using Venus_RT.Devices;
 using Venus_RT.Modules;
-using Aitex.Core.Util;
+using Aitex.Core.RT.Log;
 using Venus_Core;
 //#pragma warning disable 0436
 
@@ -133,6 +133,7 @@ namespace Venus_RT.Modules.PMs
             var ProcessUnit = unit as TCPUnit;
             if(ProcessUnit.MaxReflectedPower > 0 && Chamber.ReflectPower > ProcessUnit.MaxReflectedPower)
             {
+                LOG.Write(eEvent.ERR_PROCESS, Chamber.Module, $"Step:{step.StepNo} failed, RF Reflect Power:{Chamber.ReflectPower} exceeds the Max Limit:{ProcessUnit.MaxReflectedPower}");
                 return RState.Failed;
             }
 
@@ -171,6 +172,7 @@ namespace Venus_RT.Modules.PMs
             var ProcessUnit = unit as BiasUnit;
             if (ProcessUnit.BiasMaxReflectedPower > 0 && Chamber.BiasReflectPower > ProcessUnit.BiasMaxReflectedPower)
             {
+                LOG.Write(eEvent.ERR_PROCESS, Chamber.Module, $"Step:{step.StepNo} failed, Bias Reflect Power:{Chamber.BiasReflectPower} exceeds the Max Limit:{ProcessUnit.BiasMaxReflectedPower}");
                 return RState.Failed;
             }
 
@@ -246,7 +248,8 @@ namespace Venus_RT.Modules.PMs
         {
             if(Chamber.BackSideHeOutOfRange)
             {
-                //return RState.Failed;
+                LOG.Write(eEvent.ERR_PROCESS, Chamber.Module, $"Step:{step.StepNo} failed, Backside Helium out of range.");
+                return RState.Failed;
             }
 
             return RState.Running;
@@ -292,6 +295,13 @@ namespace Venus_RT.Modules.PMs
 
         private static RState stepStarter(RecipeStep step)
         {
+            step.StartStepTimer();
+            switch (step.Type)
+            {
+                case StepType.EndPoint:
+                    Chamber.EPDStepStart(step.EPDConfigName);
+                    break;
+            }
             return RState.Running;
         }
 
@@ -300,65 +310,39 @@ namespace Venus_RT.Modules.PMs
             switch(step.Type)
             {
                 case StepType.Time:
-                    return step.ElapsedTime() > step.Time * 1000 ? RState.End : RState.Running;
+                    return step.ElapsedTime() >= step.Time * 1000 ? RState.End : RState.Running;
+                case StepType.OverEtch:
+                    return step.ElapsedTime() >= (step.GetLastEPDStepTime() * step.OverEtchPercent / 100) ? RState.End : RState.Running;
                 case StepType.EndPoint:
-                    return Chamber.CheckEndPoint() ? RState.End : RState.Running;
+                    if (step.ElapsedTime() > step.MaxEndPointTime * 1000)
+                    {
+                        LOG.Write(eEvent.ERR_PROCESS, Chamber.Module, $"Step:{step.StepNo} timeout, did not capture endpoint signal in {step.MaxEndPointTime} seconds");
+                        return RState.Timeout;
+                    }
+                    else
+                        return Chamber.EPDCaptured ? RState.End : RState.Running;
+                
             }
 
             return RState.Running;
         }
 
-        public static bool LoadStepFuns(RecipeStep step)
-        {
-            step.starter = stepStarter;
-            step.checker = stepChecker;
-            return true;
-        }
-
-        #region EndPoint
-
-        public static bool CheckEndPoint()
-        {
-            return Chamber.CheckEndPoint();
-        }
-
-
-        public static void StartEndPoint(string config, int index)
-        {
-            Chamber.StartEndPoint(config, index);
-        }
-
-        public static void StopEndPoint()
+        private static RState stepEnder(RecipeStep step)
         {
-            Chamber.StopEndPoint();
-        }
-
-        public static void EndPointRecipeStop()
-        {
-            Chamber.EndPointRecipeStop();
-
-        }
-
-        public static void EndPointRecipeStart(string recipeName)
-        {
-            Chamber.EndPointRecipeStart(recipeName);
+            if(step.Type == StepType.EndPoint)
+            {
+                Chamber.EPDStepStop();
+            }
 
+            return RState.End;
         }
 
-
-        #endregion
-    }
-
-    public class test
-    {
-        test()
+        public static bool LoadStepFuns(RecipeStep step)
         {
-            //PressureUnitByPressureMode mode = new PressureUnitByPressureMode();
-            //mode.Start();
-            //mode.Run();
-            //mode.
-           
+            step.starter = stepStarter;
+            step.checker = stepChecker;
+            step.ender = stepEnder;
+            return true;
         }
     }
-
 }

+ 10 - 7
Venus/Venus_RT/Venus_RT.csproj

@@ -105,13 +105,16 @@
     <Compile Include="Devices\DataDefine.cs" />
     <Compile Include="Devices\DeviceManager.cs" />
     <Compile Include="Devices\EdwardsPump.cs" />
-    <Compile Include="Devices\EPD\Datas\DataItemDefine.cs" />
-    <Compile Include="Devices\EPD\Datas\EPDConfig.cs" />
-    <Compile Include="Devices\EPD\EPDDevice.cs" />
-    <Compile Include="Devices\EPD\Interface\EPDClient.cs" />
-    <Compile Include="Devices\EPD\Interface\IEPDCallback.cs" />
-    <Compile Include="Devices\EPD\Interface\IEPDCallbackService.cs" />
-    <Compile Include="Devices\EPD\Interface\IEPDService.cs" />
+    <Compile Include="Devices\EPD\Data\ByteReader.cs" />
+    <Compile Include="Devices\EPD\Data\ByteStructConverter.cs" />
+    <Compile Include="Devices\EPD\Data\CytAsciiDefine.cs" />
+    <Compile Include="Devices\EPD\Data\EPDDefine.cs" />
+    <Compile Include="Devices\EPD\Data\EPDSocketClient.cs" />
+    <Compile Include="Devices\EPD\Data\LogItem.cs" />
+    <Compile Include="Devices\EPD\Data\NotifyObject.cs" />
+    <Compile Include="Devices\EPD\Data\SocketClientWrapper.cs" />
+    <Compile Include="Devices\EPD\Data\StepItem.cs" />
+    <Compile Include="Devices\EPD\EPDClient.cs" />
     <Compile Include="Devices\ESC5HighVoltage.cs" />
     <Compile Include="Devices\FinsPlc.cs" />
     <Compile Include="Devices\IODevices\IoBacksideHe.cs" />