369 lines
15 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|