using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Text.RegularExpressions; using Aitex.Core.RT.Event; using Aitex.Sorter.Common; using MECF.Framework.Common.Equipment; using VirgoCommon; namespace VirgoRT.Device.YASKAWA { [Serializable] public sealed class EfemMessage : IEfemMessage, ICloneable { public enum MsgHead { MOV, GET, ACK, INF, SET, CAN, ABS, EVT, NAK } public const char EOF = ';'; public EfemOperation Operation { get; set; } public MsgHead Head { get; set; } public ModuleName Port { get; set; } public IList Data { get; set; } public MsgDirection Direct { get; set; } = MsgDirection.To; public IList Parameters { get; set; } public string RawString { get; set; } public string Factor { get; set; } // NAK, CAN public override string ToString() { string sPara = ToParamString(); string sOP = EfemConstant.OperationString[Operation]; string context = $"{Head}:{sOP}{sPara}"; context += EOF; return context; } public string ToParamString() { string res = string.Empty; if (Parameters == null || Parameters.Count <= 0) return res; foreach (var para in Parameters) { res += '/' + para; } return res; } public object Clone() { using (Stream objStream = new MemoryStream()) { IFormatter formatter = new BinaryFormatter(); formatter.Serialize(objStream, this); objStream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(objStream) as EfemMessage ?? throw new InvalidOperationException(); } } } [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)] public class HardwareCmdAttribute : Attribute { public string Member { get; set; } public HardwareCmdAttribute(string str) { this.Member = str; } } [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field)] public class InterpretationAttribute : Attribute { public string Comment { get; set; } public InterpretationAttribute(string str) { this.Comment = str; } } public static class Extension { private const string ERROR_MSG = "hardware message invalid"; /// /// NAK:FACTOR|Message*; /// ACK:Message*; /// CAN:Message*|Factor/Place; /// ABS:Message*|Error/Parameter1/Parameter2; /// EVT:Message*; /// public static EfemMessage ToMessage(this string context) { if (string.IsNullOrEmpty(context) || context.Length < 3) throw new ArgumentNullException(ERROR_MSG); EfemMessage msg = new EfemMessage { RawString = context, Direct = MsgDirection.From, Data = new List() }; // remove EOT string messageBody = context.Substring(0, context.IndexOf(EfemMessage.EOF) + 1); try { // split up the string string[] sBodies = messageBody.Split(Constant.delimiters); string sHead = sBodies[0]; string sBasic = sBodies[1]; string sPort = sBodies[2]; // Head if (Enum.TryParse(sHead, true, out EfemMessage.MsgHead msgHead)) { msg.Head = msgHead; } switch (msg.Head) { case EfemMessage.MsgHead.ACK: case EfemMessage.MsgHead.MOV: case EfemMessage.MsgHead.INF: case EfemMessage.MsgHead.EVT: // Basic msg.Operation = EfemConstant.ToOperation(sBasic); // Port if (Regex.IsMatch(sPort, @"P[123]$")) { msg.Port = sPort.ToModule(); } else if (Regex.IsMatch(sPort, @"ALIGN\d?", RegexOptions.IgnoreCase)) { msg.Port = sPort.ToModule(); } else { msg.Port = ModuleName.EFEM; } // Data switch (msg.Operation) { case EfemOperation.StateTrack: case EfemOperation.GetWaferInfo: msg.Data.Add(sBodies[3]); break; case EfemOperation.CarrierId: msg.Data.Add(msg.RawString.Substring("INF:CSTID/P1/".Length)); break; case EfemOperation.SigStatus: msg.Data.Add(sBodies[2]); msg.Data.Add(sBodies[3]); msg.Data.Add(sBodies[4]); break; case EfemOperation.Ready: msg.Data.Add(sBodies[2]); break; default: break; } break; case EfemMessage.MsgHead.NAK: msg.Operation = EfemConstant.ToOperation(sBasic); // Port if (Regex.IsMatch(sPort, @"P[123]$")) { msg.Port = sPort.ToModule(); } else if (Regex.IsMatch(sPort, @"ALIGN\d?", RegexOptions.IgnoreCase)) { msg.Port = sPort.ToModule(); } else { msg.Port = ModuleName.EFEM; } msg.Factor = sBodies[1]; msg.Data.Add(sBodies[2]); msg.Operation = EfemConstant.ToOperation(sBasic); break; case EfemMessage.MsgHead.CAN: msg.Operation = EfemConstant.ToOperation(sBasic); // Port if (Regex.IsMatch(sPort, @"P[123]$")) { msg.Port = sPort.ToModule(); } else if (Regex.IsMatch(sPort, @"ALIGN\d?", RegexOptions.IgnoreCase)) { msg.Port = sPort.ToModule(); } else { msg.Port = ModuleName.EFEM; } // CAN:ERROR/CLEAR; int a1 = messageBody.IndexOf(':'); int a3 = messageBody.IndexOf(';'); int a2 = messageBody.IndexOf('|'); if (a2 > 0) { msg.Data.Add(messageBody.Substring(a1 + 1, a2 - a1 - 1)); msg.Factor = messageBody.Substring(a2 + 1, a3 - a2 - 1); } else { int a8 = messageBody.IndexOf('/'); msg.Factor = sBodies[2]; } break; case EfemMessage.MsgHead.ABS: msg.Operation = EfemConstant.ToOperation(sBasic); // Port if (Regex.IsMatch(sPort, @"P[123]$")) { msg.Port = sPort.ToModule(); } else if (Regex.IsMatch(sPort, @"ALIGN\d?", RegexOptions.IgnoreCase)) { msg.Port = sPort.ToModule(); } else { msg.Port = ModuleName.EFEM; } // ABS:INIT/ALL|ERROR/SYSTEM_FF10/UNDEFINITION; //ABS: UNLOAD / ALIGN3 / ARM2 / WS4 | ERROR / RB | 2B90 int a4 = messageBody.IndexOf('|'); int a5 = messageBody.IndexOf(';'); string errStr = messageBody.Substring(a4 + 1, a5 - a4 - 1); string[] a6 = errStr.Split('/', '|'); if (a6.Length < 3) { throw new ApplicationException($"ABS 格式错误 {errStr}"); } msg.Factor = a6[0]; msg.Data.Add(a6[1]); msg.Data.Add(a6[2]); break; } } catch (Exception ex) { EV.PostAlarmLog(ModuleName.EFEM.ToString(), $"收到[{context}],解析出错[{ex.Message}]"); } return msg; } public static string ToHWString(this Position pos) { string res = string.Empty; if (ModuleHelper.IsAligner(pos.Module) || ModuleHelper.IsCooling(pos.Module)) { res = Constant.ModuleString[pos.Module]; } else { string sMod = Constant.ModuleString[pos.Module]; int num = pos.Slot + 1; string sSlot = num.ToString("D2"); res = $"{sMod}{sSlot}"; } return res; } public static string ToHWString(this ModuleName mod) { return Constant.ModuleString[mod]; } public static ModuleName ToModule(this string str) { if (Constant.StringModule.ContainsKey(str)) return Constant.StringModule[str]; throw new ApplicationException($"Cannot translate {str}"); } public static string HardwareCmd(this Enum val) { FieldInfo fi = val.GetType().GetField(val.ToString()); return fi.GetCustomAttributes(typeof(HardwareCmdAttribute), false) is HardwareCmdAttribute[] attrs && attrs.Length > 0 ? attrs[0].Member : val.ToString(); } public static string Interpretation(this Enum val) { FieldInfo fi = val.GetType().GetField(val.ToString()); return fi.GetCustomAttributes(typeof(InterpretationAttribute), false) is InterpretationAttribute[] attrs && attrs.Length > 0 ? attrs[0].Comment : val.ToString(); } } /// /// constant define /// public class Constant { public static readonly char[] delimiters = { ':', '/', '>', '|', ';' }; public const string ALL = "ALL"; public const string MAP = "MAPDT"; public const string SYS = "SYSTEM"; public const string STOWER = "STOWER"; public static readonly Dictionary ModuleString = new Dictionary { [ModuleName.EFEM] = "ALL", [ModuleName.EfemRobot]= "ROB", [ModuleName.LP1] = "P1", [ModuleName.LP2] = "P2", [ModuleName.PMA] = "LLA", [ModuleName.PMB] = "LLB", [ModuleName.Aligner1] = "ALIGN3", [ModuleName.Aligner2] = "ALIGN4", [ModuleName.Cooling1] = "ALIGN", [ModuleName.Cooling2] = "ALIGN2" }; public static readonly Dictionary StringModule = new Dictionary { ["ALL"] = ModuleName.EFEM, ["System"] = ModuleName.EFEM, ["P1"] = ModuleName.LP1, ["P2"] = ModuleName.LP2, ["LLA"] = ModuleName.PMA, ["LLB"] = ModuleName.PMB, ["ALIGN"] = ModuleName.Cooling1, ["ALIGN2"] = ModuleName.Cooling2, ["ALIGN3"] = ModuleName.Aligner1, ["ALIGN4"] = ModuleName.Aligner2, }; public static readonly Dictionary ArmString = new Dictionary { [Hand.Blade1] = "ARM2", [Hand.Blade2] = "ARM1", [Hand.Both] = "ARM3" }; public static readonly Dictionary ExtendPosString = new Dictionary { //[ExtendPos.GetReady] = "G1", //[ExtendPos.GetTarget] = "GB", //[ExtendPos.GetBack] = "G4", //[ExtendPos.PutReady] = "P1", //[ExtendPos.PutTarget] = "PB", //[ExtendPos.PutBack] = "P4" }; public static readonly Dictionary FactorString = new Dictionary { { "MSG", "Message error" }, { "NOREAD", "Communication not established" }, { "NOINITCMPL", "Initialization not completed" }, { "NOORGCMPL", "Not org finished" }, { "VAC", "Vac error" }, { "AIR", "Air error" }, { "EMS", "EMS pushed" }, { "ERROR", "Has Error" }, { "BUSY", "Busy (Module in use)" }, { "CMPL", "Already Completed" }, { "NOMAPCMPL", "Not Mapped" }, { "NONWAF", "NO wafer on the position" }, { "WAFER", "Wafer already existed" }, { "NONPOD", "No Pod" }, { "POD", "Pod Exist" }, { "NOTINMOTION","Not in Motion" }, { "HOLD", "Holded" }, { "NOHOLD", "Not hold" }, { "LLCLOSE", "LL door closed state" }, { "LLNOEXTEND", "LL not extended" }, { "REMOVE", "remove" }, { "MAINTENANCE","In maintain mode or EFEM offline" }, { "NOLINK", "not config" }, { "ARMEXTEND", "arm extended" }, { "POWER", "power off" }, { "PRTWAF", "TODO" }, { "NOPOS", "Not at valid position" }, { "NOFUNC", "not support" }, { "PARAM_NG", "Invalid parameters are included" } }; public static readonly Dictionary ABS_PARA1 = new Dictionary { { "SYSTEM_FF01", "No specific route data or malfunction" }, { "SYSTEM_FF10", "Operation time out" }, { "SYSTEM_FF11", "Communication time out between PC for robot control and controller" }, { "SYSTEM_FF12", "Encoder battery error" }, { "SYSTEM_FF13", "Loss of encoder information" }, { "SYSTEM_FF20", "No data file of route" }, { "SYSTEM_FF21", "Serial number in route data is wrong" }, { "SYSTEM_FF22", "No line for serial number in route data" }, { "SYSTEM_FF23", "Discrepancy in serial number between route data and teaching data" }, { "SYSTEM_FF30", "Failure of pre-operation movement at the wrist in the robot" }, { "SYSTEM_FF83", "Inability of the robot to return origin" }, { "SYSTEM_FFB0", "Operation malfunction at fan in robot" }, { "SYSTEM_FFF0", "Alarm at Z-axis driver in the robot" }, { "SYSTEM_FFFF", "Robot control program malfunction" } }; } }