2026-01-26 18:56:53 +09:00

369 lines
15 KiB
C#

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;
using System.Windows;
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<LBAD_Feature_Data> LBADFeatureDataList = new List<LBAD_Feature_Data>();
private List<LBAD_Raw_Data> LBADRawDataList = new List<LBAD_Raw_Data>();
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<int>(), new StreamNumberParser<int>());
//Welding Tirgger ON을 삭제하고 Cell정보를 보낼 때 LBAD Start
_Client.AddParser(MESSAGE_PC_LBAD_WELDING_TRIGGER, new StreamNumberParser<int>(), new StreamNumberParser<int>());
_Client.AddParser(MESSAGE_PC_LBAD_WELDING_STOP, new StreamNumberParser<int>(), new StreamNumberParser<int>());
//용접 완료 후 LBAD측에 JudgeComplete를 요청한뒤 JudgeFlag를 회신 받는다
_Client.AddParser(MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE, new StreamNumberParser<int>(), new StreamNumberParser<int>());
//byte[] 형태의 경우 AddParser 추가하면 안됨
//_Client.AddParser(MESSAGE_PC_LBAD_CELL_INFO, new StreamStringParser(), new StreamNumberParser<int>());
//_Sub.AddParser(MESSAGE_LBAD_ALIVE, new StreamJsonParser<int>());
_Sub.AddParser(MESSAGE_LBAD_ALARM_STATUS, new StreamNumberParser<int>());
_Sub.AddParser(MESSAGE_LBAD_FEATURE_DATA, new StreamJsonParser<LBAD_Feature_Data>());
_Sub.AddParser(MESSAGE_LBAD_RAW_DATA, new StreamJsonParser<LBAD_Raw_Data>());
_Sub.AddParser(MESSAGE_LBAD_MES_REPORT, null);
_Sub.SetListener(OnSubscribe);
}
public bool SendReadyStatus(eRequestData data)
{
if (ApplicationExt.IsSimulation)
return true;
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)
{
if (ApplicationExt.IsSimulation)
return true;
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()
{
if (ApplicationExt.IsSimulation)
return true;
//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>(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;
}
}
}
}