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.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 Int16 LBADAlive { get; set; } public class LBAD_Raw_Data { public Int32 ChannelSize { get; set; } public Int32 BeadSize { get; set; } } 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 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 } public unsafe struct stCellInfoData { public fixed byte chLotID[40]; // 40byte public fixed byte chCellID[40]; // 40byte public Int16 nRecipeID; public Int16 nModelID; } //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 void Initialize() { _Client.AddParser(MESSAGE_PC_LBAD_READY_STATUS, new StreamNumberParser(), new StreamNumberParser()); _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()); _Client.AddParser(MESSAGE_PC_LBAD_CELL_INFO, new StreamStringParser(), new StreamNumberParser()); _Sub.AddParser(MESSAGE_LBAD_ALIVE, new StreamJsonParser()); _Sub.AddParser(MESSAGE_LBAD_FEATURE_DATA, new StreamJsonParser()); _Sub.AddParser(MESSAGE_LBAD_RAW_DATA, new StreamJsonParser()); } 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) { Stopwatch sw = Stopwatch.StartNew(); byte[] arrCellinfo = null; int sendresult = MakeInfoData(lotID, cellID, recipeNo, modelID, 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 SetWeldTriggerOn() { Stopwatch sw = Stopwatch.StartNew(); //int requestData = 4000; var replyData = _Client.RequestNReply(eRequestData.Welding_Trigger, MESSAGE_PC_LBAD_WELDING_TRIGGER); if (replyData != null) { var replyParsed = (int)replyData; 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!"); sw.Stop(); 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 strLotID, string strCellID, int nRecipeID, int nModelID, out byte[] arrSendData) { stCellInfoData SendData = new stCellInfoData(); SendData.nRecipeID = Convert.ToInt16(nRecipeID); SendData.nModelID = Convert.ToInt16(nModelID); // data // LotID, CellID 는 39자를 넘지 말것. 마지막에 /0 을 같이 보내자 // string byte 를 memory copy 하면 제대로 복사가 되지 않는다. 일일히 넣어줘야한다. if (strLotID.Length > 39 || strCellID.Length > 39) { arrSendData = null; return E2.INVALID_ARGUMENT; } byte* PtrLotID = SendData.chLotID; { for (int i = 0; i < strLotID.Length; i++) { SendData.chLotID[i] = Convert.ToByte(strLotID[i]); } SendData.chLotID[strLotID.Length] = Convert.ToByte('\0'); } byte* PtrCellID = SendData.chCellID; { for (int i = 0; i < strCellID.Length; i++) { SendData.chCellID[i] = Convert.ToByte(strCellID[i]); } SendData.chCellID[strCellID.Length] = Convert.ToByte('\0'); } arrSendData = StructToBytes(SendData); return E1.OK; } 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 static 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_ALIVE: //var replyParsed = data as LBADAlive; break; default: break; } } } }