using Aurora; using Aurora.Api; using Aurora.Api.Devices; using Aurora.Util.HislogPlus; using Hermes; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; namespace AuroraLBAD.Service { public class StreamLightLBAD { static readonly Logger Log = new Logger("StreamLightLBAD"); public StreamLightClient _Client { get; set; } public StreamLightSub _Sub { get; set; } private List LBADFeatureDataList = new List(); private List LBADRawDataList = new List(); public static double[] LaserPowerByLBAD = new double[100]; //headnum 크기면 되지만 우선 100으로 해둠 public Int16 LBADAlive { get; set; } public Int16? LBADAlarmStatus { get; set; } [StructLayout(LayoutKind.Sequential, Pack = 1)] public class LBAD_Raw_Data { public Int32 ChannelSize { get; set; } public Int32 BeadSize { get; set; } } [StructLayout(LayoutKind.Sequential, Pack = 1)] public class LBAD_Feature_Data { public string LotID { get; set; } public string CellID { get; set; } public Int16 RecipeID { get; set; } public Int16 ResultFlag { get; set; } public double Avg { get; set; } public double Min { get; set; } public double Max { get; set; } public double Stddev { get; set; } public double RateDifference { get; set; } public double CenterX { get; set; } public double CenterY { get; set; } public double Circularity { get; set; } public double OuterDiameter { get; set; } } public class LBAD_Report_Data { public string LotID { get; set; } public string CellID { get; set; } public Int16 HeadNum { get; set; } public double AVG { get; set; } } public enum eRequestData { Ready_Off = 1000, Ready_On = 1001, Welding_Trigger = 4000, Welding_Stop = 5000, Judge_Complete = 6000 } public enum eJudgeFlag { NO_RESPONSE = -1, OK = 6000, WARNING, NA, NG, ERROR } [StructLayout(LayoutKind.Sequential, Pack = 1)] public unsafe struct stCellInfoData { public fixed byte LotID[40]; // 40byte public fixed byte CellID[40]; // 40byte public Int16 RecipeID; public Int16 ModelID; public Int16 JigNo; } public unsafe struct stLBADReportData { public fixed byte LotID[40]; // 40byte public fixed byte CellID[40]; // 40byte public Int16 HeadNum; public double AVG; } //StreamLight Client LBAD public const string MESSAGE_PC_LBAD_READY_STATUS = "PcLBADReadyStatus"; public const string MESSAGE_PC_LBAD_WELDING_TRIGGER = "PcLBADWeldingTrigger"; public const string MESSAGE_PC_LBAD_WELDING_STOP = "PcLBADWeldingStop"; public const string MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE = "PcLBADJudgementComplete"; public const string MESSAGE_PC_LBAD_CELL_INFO = "PcLBADCellInfo"; //StreamLight Sub LBAD public const string MESSAGE_LBAD_FEATURE_DATA = "LBADFeatureData"; public const string MESSAGE_LBAD_RAW_DATA = "LBADRawData"; public const string MESSAGE_LBAD_ALIVE = "LBADAlive"; public const string MESSAGE_LBAD_ALARM_STATUS = "LBADAlarmStatus"; public const string MESSAGE_LBAD_MES_REPORT = "LBADMESReport"; public void Initialize() { _Client.AddParser(MESSAGE_PC_LBAD_READY_STATUS, new StreamNumberParser(), new StreamNumberParser()); //Welding Tirgger ON을 삭제하고 Cell정보를 보낼 때 LBAD Start _Client.AddParser(MESSAGE_PC_LBAD_WELDING_TRIGGER, new StreamNumberParser(), new StreamNumberParser()); _Client.AddParser(MESSAGE_PC_LBAD_WELDING_STOP, new StreamNumberParser(), new StreamNumberParser()); //용접 완료 후 LBAD측에 JudgeComplete를 요청한뒤 JudgeFlag를 회신 받는다 _Client.AddParser(MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE, new StreamNumberParser(), new StreamNumberParser()); //byte[] 형태의 경우 AddParser 추가하면 안됨 //_Client.AddParser(MESSAGE_PC_LBAD_CELL_INFO, new StreamStringParser(), new StreamNumberParser()); //_Sub.AddParser(MESSAGE_LBAD_ALIVE, new StreamJsonParser()); _Sub.AddParser(MESSAGE_LBAD_ALARM_STATUS, new StreamNumberParser()); _Sub.AddParser(MESSAGE_LBAD_FEATURE_DATA, new StreamJsonParser()); _Sub.AddParser(MESSAGE_LBAD_RAW_DATA, new StreamJsonParser()); _Sub.AddParser(MESSAGE_LBAD_MES_REPORT, null); _Sub.SetListener(OnSubscribe); } public bool SendReadyStatus(eRequestData data) { Stopwatch sw = Stopwatch.StartNew(); //int requestData = 1001; Log.I($"StreamLight {MESSAGE_PC_LBAD_READY_STATUS} Request"); var reply = _Client.RequestNReply(data, MESSAGE_PC_LBAD_READY_STATUS); if (reply != null) { var replyParsed = (int)reply; Log.I($"StreamLight {MESSAGE_PC_LBAD_READY_STATUS} Reply Result : {replyParsed}"); sw.Stop(); Log.I($"StreamLight {MESSAGE_PC_LBAD_READY_STATUS} Complete time : {sw.ElapsedMilliseconds}"); } else { Log.E($"StreamLight {MESSAGE_PC_LBAD_READY_STATUS} reply is null!"); sw.Stop(); return false; } return true; } public bool SendCellInfo(string lotID, string cellID, int recipeNo, int modelID, int jigNo) { Stopwatch sw = Stopwatch.StartNew(); byte[] arrCellinfo = null; int sendresult = MakeInfoData(lotID, cellID, recipeNo, modelID, jigNo, out arrCellinfo); var replyData = _Client.RequestNReply(arrCellinfo, MESSAGE_PC_LBAD_CELL_INFO); if (replyData != null) { if (replyData is byte[] bytes && bytes.Length > 0) { int replyParsed = bytes[0]; // 첫 바이트만 가져와서 int로 사용 Log.I($"StreamLight {MESSAGE_PC_LBAD_CELL_INFO} Reply Result : {replyParsed}"); sw.Stop(); Log.I($"StreamLight {MESSAGE_PC_LBAD_CELL_INFO} Complete time : {sw.ElapsedMilliseconds}"); return true; } } else { Log.E($"StreamLight {MESSAGE_PC_LBAD_CELL_INFO} reply is null!"); sw.Stop(); return false; } return false; } public bool SendWeldingTrigger() { //Welding Stop On Stopwatch sw = Stopwatch.StartNew(); //int requestData = 5000; var reply = _Client.RequestNReply(eRequestData.Welding_Trigger, MESSAGE_PC_LBAD_WELDING_TRIGGER); if (reply != null) { var replyParsed = (int)reply; Log.I($"StreamLight {MESSAGE_PC_LBAD_WELDING_TRIGGER} Reply Result : {replyParsed}"); sw.Stop(); Log.I($"Stream {MESSAGE_PC_LBAD_WELDING_TRIGGER} Complete time : {sw.ElapsedMilliseconds}"); return true; } else { Log.E($"StreamLight {MESSAGE_PC_LBAD_WELDING_TRIGGER} reply is null!"); } return false; } public bool SendWeldingStop() { //Welding Stop On Stopwatch sw = Stopwatch.StartNew(); //int requestData = 5000; var reply = _Client.RequestNReply(eRequestData.Welding_Stop, MESSAGE_PC_LBAD_WELDING_STOP); if (reply != null) { var replyParsed = (int)reply; Log.I($"StreamLight {MESSAGE_PC_LBAD_WELDING_STOP} Reply Result : {replyParsed}"); sw.Stop(); Log.I($"Stream {MESSAGE_PC_LBAD_WELDING_STOP} Complete time : {sw.ElapsedMilliseconds}"); return true; } else { Log.E($"StreamLight {MESSAGE_PC_LBAD_WELDING_STOP} reply is null!"); } return false; } public eJudgeFlag CheckJudgeComplete() { Stopwatch sw = Stopwatch.StartNew(); //int requestData = 6000; var reply = _Client.RequestNReply(eRequestData.Judge_Complete, MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE); if (reply != null) { var replyParsed = (int)reply; Log.I($"StreamLight {MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE} Reply Result : {replyParsed}"); //JudgeFlag에 따라서 시퀀스 결정 //(OK = 6000, WARNING = 6001, NA = 6002, NG = 6003, ERROR = 6004) if (Enum.IsDefined(typeof(eJudgeFlag), replyParsed)) { sw.Stop(); Log.I($"Stream {MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE} Complete time : {sw.ElapsedMilliseconds}"); return (eJudgeFlag)replyParsed; } Log.E($"StreamLight {MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE} reply is abnormal value : {replyParsed}!"); sw.Stop(); } else { Log.E($"StreamLight {MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE} reply is null!"); sw.Stop(); } return eJudgeFlag.NO_RESPONSE; } private static unsafe int MakeInfoData(string LotID, string CellID, int RecipeID, int ModelID, int JigNo, out byte[] arrSendData) { stCellInfoData SendData = new stCellInfoData(); SendData.RecipeID = Convert.ToInt16(RecipeID); SendData.ModelID = Convert.ToInt16(ModelID); SendData.JigNo = Convert.ToInt16(JigNo); // data // LotID, CellID 는 39자를 넘지 말것. 마지막에 /0 을 같이 보내자 // string byte 를 memory copy 하면 제대로 복사가 되지 않는다. 일일히 넣어줘야한다. if (LotID.Length > 39 || CellID.Length > 39) { arrSendData = null; return E2.INVALID_ARGUMENT; } byte* PtrLotID = SendData.LotID; { for (int i = 0; i < LotID.Length; i++) { SendData.LotID[i] = Convert.ToByte(LotID[i]); } SendData.LotID[LotID.Length] = Convert.ToByte('\0'); } byte* PtrCellID = SendData.CellID; { for (int i = 0; i < CellID.Length; i++) { SendData.CellID[i] = Convert.ToByte(CellID[i]); } SendData.CellID[CellID.Length] = Convert.ToByte('\0'); } arrSendData = StructToBytes(SendData); return E1.OK; } public static unsafe stLBADReportData BytesToLBADReportDataStruct(byte[] bytes) { stLBADReportData data = new stLBADReportData(); for (int i=0; i<40; i++) { data.LotID[i] = bytes[i]; } for (int i=0; i<40; i++) { data.CellID[i] = bytes[i + 40]; } data.HeadNum = BitConverter.ToInt16(bytes, 80); data.AVG = BitConverter.ToDouble(bytes, 82); return data; } public static byte[] StructToBytes(T structure) where T : struct { int size = Marshal.SizeOf(typeof(T)); byte[] bytes = new byte[size]; IntPtr ptr = Marshal.AllocHGlobal(size); try { Marshal.StructureToPtr(structure, ptr, true); Marshal.Copy(ptr, bytes, 0, size); } finally { Marshal.FreeHGlobal(ptr); } return bytes; } private void OnSubscribe(string from, string topic, object data) { switch (topic) { case MESSAGE_LBAD_FEATURE_DATA: var replyParsed = data as LBAD_Feature_Data; Log.I($"StreamLight LBAD Feature Data : " + $"LotID {replyParsed.LotID}, CellID {replyParsed.CellID}, RecipeID {replyParsed.RecipeID}, ResultFlag {replyParsed.ResultFlag}, " + $"Avg {replyParsed.Avg}, Min {replyParsed.Min}, Max {replyParsed.Max}, Stddev {replyParsed.Stddev}" + $"RateDifference {replyParsed.RateDifference}, CenterX {replyParsed.CenterX}, CenterY {replyParsed.CenterY}, " + $"Circularity {replyParsed.Circularity}, OuterDiameter {replyParsed.OuterDiameter}"); break; case MESSAGE_LBAD_ALARM_STATUS: LBADAlarmStatus = data != null ? (Int16?)Convert.ToInt16(data) : null; Log.I($"StreamLight LBAD Alarm Status : {LBADAlarmStatus}"); break; case MESSAGE_LBAD_MES_REPORT: Log.I($"Received Data : {data}"); //byte[] data_byte = BitConverter.GetBytes(data); if (data is byte[] bytes && bytes.Length > 0) { Log.I($"Received Data Size : {bytes.Length}"); var replyParsed_LBAD = BytesToLBADReportDataStruct(bytes); unsafe { char[] LotID = new char[40]; char[] CellID = new char[40]; for (int i=0; i<40; i++) { LotID[i] = (char)bytes[i]; } for (int i=40; i<80; i++) { CellID[i-40] = (char)bytes[i]; } string LotID_string = new string(LotID); string CellID_string = new string(CellID); Log.I($"StreamLight LBAD Report Data : LotID {LotID_string}"); Log.I($"StreamLight LBAD Report Data : HeadNum {replyParsed_LBAD.HeadNum}"); Log.I($"StreamLight LBAD Report Data : LaserPower_AVG {replyParsed_LBAD.AVG}"); if (replyParsed_LBAD.HeadNum > 0) LaserPowerByLBAD[replyParsed_LBAD.HeadNum] = replyParsed_LBAD.AVG; else { Log.E($"Head Number is {replyParsed_LBAD.HeadNum}"); } } } break; default: break; } } } }