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

584 lines
24 KiB
C#

using Aurora.Api;
using AuroraIO;
using AuroraLaserScanner;
using AuroraLBAD;
using AuroraLBAD.Service;
using AuroraLBAD.Services;
using AuroraOCT;
using CoreUnit.Control.UserControl.Canvas;
using CoreUnit.Scanner.UserControl.ScannerCanvas;
using DB;
using Hermes;
using Matrox.MatroxImagingLibrary;
using OpenCvSharp.Dnn;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VisionCommon;
using WeldingCommon.Alarm;
using WeldingCommon.Data;
using WeldingCommon.Devices;
using WeldingCommon.Recipes;
using WeldingCommon.Settings;
using WeldingCommon.Utils;
using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo;
using static AuroraIO.Vendor.PAIX.PAIXIO;
using static AuroraLBAD.Service.StreamLightLBAD;
namespace WeldingCommon.Sequences
{
class SeqTeachingRun
{
static readonly Logger Log = new Logger("SeqTeachingRun");
const string SEQ_ENTER = "[SeqTeachingRun] SEQ_ENTER"; // inputParam 으로 받자.
const string SEQ_CHECK_INIT_CONDITION = "[SeqTeachingRun] Check Init Condition"; // Check Init Condition
const string SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ = "[SeqTeachingRun] Set Sequence Param (UploadWeldingList Seq)";
const string SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ = "[SeqTeachingRun] Process (UploadWeldingList Seq)";
const string SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ = "[SeqTeachingRun] Get Sequence Result (UploadWeldingList Seq)";
const string SEQ_SET_BLOW_ON = "[SeqTeachingRun] Set Blow On";
const string SEQ_STREAM_LBAD_BEFORE_WELDLINES = "[SeqTeachingRun] StreamLBAD Before Weldlines";
const string SEQ_SET_USE_MOVING_JIG = "[SeqTeachingRun] Set Use Moving Jig";
const string SEQ_EXECUTE_WELDLINES = "[SeqTeachingRun] Execute WeldLines";
const string SEQ_SET_BLOW_OFF = "[SeqTeachingRun] Set Blow Off";
const string SEQ_STREAM_LBAD_AFTER_WELDLINES = "[SeqTeachingRun] StreamLBAD After Weldlines";
const string SEQ_SAVE_RESULT_DATA = "[SeqTeachingRun] Save Result Data";
const string SEQ_WRITE_COMPLETION_PLC_BIT = "[SeqTeachingRun] Write Completion PLC Bit";
const string SEQ_COMPLETE = "[SeqTeachingRun] SEQ_COMPLETE"; // 완료
static class ContextKey
{
public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqTeachingRun";
public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqTeachingRun";
}
private SeqTeachingRun_InitParam _InitParam;
private Scanner _Scanner;
private OCT _OCT;
private IODevice _IO;
private PLCController _PLC;
//private StreamLBADClient _StreamLBADClient;
private StreamLightLBAD _StreamLightLBAD;
private SystemParameter _SystemParameter;
private eListNo _WeldingScannerListNo;
private List<IDrawObject> _SelectedObjectList;
private TimeSpan _WeldingSpanTime; //TeacingRun 시퀀스에서 용접 소요시간
public SequenceBuilder Builder()
{
var sb = new SequenceBuilder(1000, true);
////var sb = new SequenceBuilder(1000, false); // With log
sb.AddAction(SEQ_ENTER, Sequence_Enter);
sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기
sb.AddAction(SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams);
sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, SequenceManager.GetSeqBuilder_SeqUploadWeldingList());
sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult);
sb.AddAction(SEQ_STREAM_LBAD_BEFORE_WELDLINES, StreamLBADBeforeWeldlines);
sb.AddAction(SEQ_SET_USE_MOVING_JIG, SetUseMovingJig);
sb.AddAction(SEQ_EXECUTE_WELDLINES, ExecuteWeldLines);
sb.AddAction(SEQ_STREAM_LBAD_AFTER_WELDLINES, StreamLBADAfterWeldlines);
sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함.
sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write
sb.AddAction(SEQ_COMPLETE, Complete);
sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION]
> sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ]
> sb[SEQ_STREAM_LBAD_BEFORE_WELDLINES] > sb[SEQ_SET_USE_MOVING_JIG] >sb[SEQ_EXECUTE_WELDLINES]
> sb[SEQ_STREAM_LBAD_AFTER_WELDLINES]
> sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_COMPLETE]);
sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final);
sb.OnStopped(OnStopped);
sb.OnForceError(OnForceError);
//sb.Draw("SeqTeachingRun.pdf");
return sb;
}
private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("SeqTeachingRun Start");
_InitParam = param.Param as SeqTeachingRun_InitParam;
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
InitDev(context);
if (!CheckDeviceReady(context)) { //Check Device
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_DeviceNotReady);
Log.E("The devices are not active.");
return null;
}
if (InitSequenceVariable(context) == false) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_InitVariableError);
return null;
}
return SequenceAction.Ok();
}
private bool InitSequenceVariable(SequenceContext context)
{
_WeldingScannerListNo = eListNo.LIST1;
if (_InitParam.IsObjectSelectAll) {
_SelectedObjectList = WeldingRecipeManager.GetWeldingActivateLayerObjects();
} else {
_SelectedObjectList = WeldingRecipeManager.GetWeldingPage().ScannerCanvasCtrlTeaching.Canvas.Model.SelectedObjects;
}
//Status.GetProcessingWeldingData().WeldingScannerListNo = _WeldingScannerListNo;
//Status.GetProcessingWeldingData().SelectedDrawObjectList = _SelectedObjectList;
return true;
}
private void InitDev(SequenceContext context)
{
//Device Init
_Scanner = context.GetLaserScanner();
_OCT = context.GetOCT();
_IO = context.GetIOUnit();
_PLC = context.GetPLC();
if (_InitParam.UseStream) {
_StreamLightLBAD = new StreamLightLBAD();
_StreamLightLBAD._Client = context.GetStreamLightClient();
_StreamLightLBAD._Sub = context.GetStreamLightSub();
//_StreamLBADClient = context.GetLBADClient();
}
// @geprecated 각형배터리에서 문제 생겨서 추가한 코드임
// Scanner를 다시 초기화해주면 시간이 오래걸린다.. 굳이 수동용접시 마다 해줄 필요가 없음
//if (E1.OK != _Scanner.Open())
//{
// Log.E("Scanner open Error");
// return SequenceAction.Error();
//}
//if (E1.OK != _Scanner.Initialize(_scanner.Config))
//{
// Log.E("Scanner Initialize Error");
// return SequenceAction.Error();
//}
}
private bool CheckDeviceReady(SequenceContext context)
{
// deviceListToCheck는 장비에 맞게 설정해야 하므로 TeachingRun에서는 필요없을듯
List<string> deviceListToCheck = new List<string>() { CommonDeviceIds.Scanner};
//List<string> deviceListToCheck = new List<string>() { CommonDeviceIds.Scanner, CommonDeviceIds.OCT, CommonDeviceIds.IODevice };
if (_InitParam.UseStream) {
deviceListToCheck.Add(CommonServiceIds.StreamLightLBADClient);
deviceListToCheck.Add(CommonServiceIds.StreamLightLBADSub);
}
bool isAllDeviceActivated = true;
foreach (string item in deviceListToCheck) {
if (!context.Devices[item].IsActivated) {
isAllDeviceActivated = isAllDeviceActivated && false;
}
}
return isAllDeviceActivated;
}
private bool CheckCondition()
{
if (CheckCondition_Interlock()) {
if (InitializeDeviceStatus()) {
return true;
}
}
return false;
}
private bool CheckCondition_Interlock()
{
// PLC, Data, IO 등 Interlock checking
return true;
}
private bool InitializeDeviceStatus()
{
//Vision Correction File 선택
//_Scanner.SelectCorrectionTable(1);
_Scanner.SelectCorrectionTable(2);
if (_InitParam.UseStream) {
if (InitializeStream() == false) {
Log.E("Fail Initialize Stream");
return false;
}
}
return true;
}
private string CheckInitCondition(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (!CheckCondition()) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_CheckInitCondition);
Log.E("Condition is not in state to start sequence.");
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false);
return SEQ_COMPLETE;
}
var startTime = DateTime.Now;
context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime);
Log.D("TeachingRun Sequence CheckInitCondition");
return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ;
}
private bool InitializeStream()
{
if (!_StreamLightLBAD._Client.IsActivated) {
int ret = _StreamLightLBAD._Client.Activate();
Thread.Sleep(500);
if (ret != E1.OK) {
Log.E($"StreamLightClient activate failed");
return false;
}
}
if (!_StreamLightLBAD._Sub.IsActivated) {
int ret = _StreamLightLBAD._Sub.Activate();
Thread.Sleep(500);
if (ret != E1.OK) {
Log.E($"StreamLightSub activate failed");
return false;
}
}
_StreamLightLBAD.Initialize();
return true;
}
private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
// TeachingRun 에서는 CellMeasureCenterOffset 사용하지 않음. 0 으로 넘기자
SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() {
NeedSetLaserTiming = _InitParam.NeedSetLaserTiming,
IncludeListStartEnd = true,
CellMeasureCenterOffsetX = _SystemParameter.CVScannerOffsetX,
CellMeasureCenterOffsetY = _SystemParameter.CVScannerOffsetY,
SelectedDrawObjectList = _SelectedObjectList,
WeldingScannerListNo = _WeldingScannerListNo,
Rotation_Degree = 0, // 장비에 맞게 수정 필요
};
return SequenceAction.Ok(seqParams);
}
private SequenceActionResult GetUploadWeldingList_SeqResult(SequenceContext context, string id, SequenceActionResult param)
{
var resultOutParam = param.Param as SeqUploadWeldingList_OutParam;
if (resultOutParam.Success == false) {
return null;
}
return SequenceAction.Ok();
}
private SequenceActionResult StreamLBADBeforeWeldlines(SequenceContext context, string id, SequenceActionResult param)
{
if (_InitParam.UseStream == false || _SystemParameter.BypassModeLBAD) {
return SequenceAction.Ok();
}
if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_On) == false) {
Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null"));
return null;
}
string strDate = DateTime.Now.Date.ToString("yyyyMMdd");
string strTime = DateTime.Now.ToString("HHmmss");
if (_StreamLightLBAD.SendCellInfo("LotID" + strDate, "CellID" + strDate + strTime, 1, 4680, 1) == false) {
Log.I(string.Format($"[LBAD Interface]SendCellInfo null"));
return null;
}
return SequenceAction.Ok();
}
private SequenceActionResult StreamLBADAfterWeldlines(SequenceContext context, string id, SequenceActionResult param)
{
if (_InitParam.UseStream == false || _SystemParameter.BypassModeLBAD) {
return SequenceAction.Ok();
}
if (_StreamLightLBAD.SendWeldingStop() == false) {
Log.I(string.Format($"[LBAD Interface]SendWeldingStop null"));
return null;
}
//string judgeResult = "";
//var judgeFlag = _StreamLightLBAD.CheckJudgeComplete();
//if (judgeFlag == StreamLightLBAD.eJudgeFlag.NO_RESPONSE) {
// Log.I(string.Format($"[LBAD Interface]CheckJudgeComplete null"));
// return null;
//} else {
// switch ((int)judgeFlag) {
// case 6000:
// judgeResult = "OK";
// break;
// case 6001:
// judgeResult = "WARNING";
// break;
// case 6002:
// judgeResult = "NA";
// break;
// case 6003:
// judgeResult = "NG";
// break;
// case 6004:
// judgeResult = "ERROR";
// break;
// default:
// judgeResult = "NG";
// break;
// }
//}
//Log.I(string.Format($"LBAD Judge Flag : {judgeFlag} Result : {judgeResult}"));
////Status.GetProcessingWeldingData().LBAD_Judge = (judgeFlag).ToString();
//Log.I(string.Format($"[LBAD Interface]CheckJudgeComplete OK"));
return SequenceAction.Ok();
}
private SequenceActionResult SetUseMovingJig(SequenceContext context, string id, SequenceActionResult param)
{
// TODO : SMW용
if (_InitParam.UseMovingJig && (_PLC.IsActivated && !_PLC.IsSimul)) {
// PC_JIG_LOAD_COMPLETE_ACK는 무조건 True로 만들어줘야 Jig 유닛이 오른쪽으로 회피해있음
_PLC.WriteBit((int)_PLC.PC_JIG_LOAD_COMPLETE_ACKAddr, true);
Log.I(string.Format($"[PLCInterface]SET PC_JIG_LOAD_COMPLETE_ACK true"));
DateTime _InterfaceStartTime = DateTime.Now;
int plc_TIMEOUT = 5;
while (!context.Stopped) {
if (_PLC.ReadBit((int)_PLC.PLC_WELD_REQEUSTAddr).data) {
Log.I(string.Format($"[PLCInterface]GET PLC_WELD_REQEUST True"));
_PLC.WriteBit((int)_PLC.PC_WELD_REQEUST_ACKAddr, true);
Log.I(string.Format($"[PLCInterface]SET PC_WELD_REQEUST_ACK True"));
break;
}
TimeSpan _InterfaceSpanTime = DateTime.Now.Subtract(_InterfaceStartTime);
if (_InterfaceSpanTime.TotalMilliseconds > (plc_TIMEOUT * 1000)) {
Log.E($"TIMEOUT_PLC_WELD_REQEUST : {plc_TIMEOUT} sec");
return null;
}
Thread.Sleep(10);
}
}
return SequenceAction.Ok();
}
private SequenceActionResult ExecuteWeldLines(SequenceContext context, string id, SequenceActionResult param)
{
Log.D(string.Format($"[TeachingRun] Welding Start"));
DateTime weldingTime = DateTime.Now;
// TODO : List Overflow check
if(_Scanner.GetListSpace() > 0) {
_Scanner.ExecuteList(_WeldingScannerListNo);
_Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut);
} else {
Log.E("[TechingRun] List memory OverFlow");
}
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] Welding Complete Time : {_WeldingSpanTime.TotalMilliseconds}"));
double dExecuteListTime = _Scanner.GetTime();
Log.I(string.Format($"[TeachingRun] ExecuteList Time : {dExecuteListTime * 1000} msec"));
weldingTime = DateTime.Now;
// OCT File Fomrat Record Test
//SaveOCTFileFormat();
// File Format Record
if (_Scanner.Config.UseRecordSignal) {
string dataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog("Normal", true);
_Scanner.SaveRecord(dataFilePath);
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] SaveScannerRecordFileForamt Time : {_WeldingSpanTime.TotalMilliseconds}"));
}
weldingTime = DateTime.Now;
_Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] Complete Scanner Move Origin Time : {_WeldingSpanTime.TotalMilliseconds}"));
//용접 완료 후 CorrectionFile Vision으로 원복
_Scanner.SelectCorrectionTable(1);
// 자동이 아닌 경우는 위에 쌓은 데이터를 모두 지워줌(실처리 데이터는 자동에서만 사용하도록 함)
Status.GetProcessingWeldingData().APD.Clear();
return SequenceAction.Ok();
}
private void SaveOCTFileFormat()
{
List<string> MetaDataWeldName = new List<string>(_SelectedObjectList.Count);
List<string> MetaDataWeldType = new List<string>(_SelectedObjectList.Count);
List<int> MetaDataWeldCoordinateID = new List<int>(_SelectedObjectList.Count);
int iCoordinateID = 0;
for (int i = 0; i < _SelectedObjectList.Count; i++) {
if (_SelectedObjectList.ElementAt(i).Type == "Dot") {
MetaDataWeldCoordinateID.Add(0);
} else {
MetaDataWeldCoordinateID.Add(++iCoordinateID);
}
MetaDataWeldName.Add(_SelectedObjectList.ElementAt(i).Type + (i + 1));
MetaDataWeldType.Add(_SelectedObjectList.ElementAt(i).Type);
//MetaDataWeldCoordinateID.Add(m_selectedObjects.ElementAt(i).No + 1)
}
string dataFilePath = string.Empty;
bool isRecordLDD = true;
if (isRecordLDD) {
dataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog_Coordinate("CoordinateFile", false);
} else {
dataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog("CoordinateFile", true);
}
_Scanner.SaveRecord(dataFilePath, true, MetaDataWeldCoordinateID, iCoordinateID);
dataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog_MetaData("Metadata", false);
_OCT.SaveMetaData(dataFilePath, MetaDataWeldName, MetaDataWeldType, MetaDataWeldCoordinateID);
string coordinateFolderPath = ProjectUtil.GetDataLogSetting().GetDirPath_ScannerRecordLog_Coordinate();
string metaDataFolderPath = ProjectUtil.GetDataLogSetting().GetDirPath_ScannerRecordLog_MetaData();
string coordinateFilePath = _Scanner.GetLatestFile(coordinateFolderPath);
string metaDataFilePath = _Scanner.GetLatestFile(metaDataFolderPath);
// zip file path 지정
dataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog_LddFileFormat("LDDFileFormat", false);
_Scanner.SaveRecordZip(dataFilePath, coordinateFilePath, metaDataFilePath);
MetaDataWeldName.Clear();
MetaDataWeldType.Clear();
MetaDataWeldCoordinateID.Clear();
}
private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param)
{
// 결과 Data 를 업데이트 함.
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true);
return SequenceAction.Ok();
}
private SequenceActionResult WriteCompletionPLCBit(SequenceContext context, string id, SequenceActionResult param)
{
// 완료 비트 write 하는 Node 추가 필요. format 유지, SeqScoutNormal 없음
Log.D("TeachingRun Sequence WriteCompletionPLCBit");
return SequenceAction.Ok();
}
private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param)
{
RestoreDeviceStatus();
Log.I("TeachingRun Complete");
context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult);
SeqTeachingRun_OutParam result = new SeqTeachingRun_OutParam() { Success = seqResult };
if (seqResult == false) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_ResultFailed);
}
Log.D("TeachingRun Sequence Complete");
return SequenceAction.Ok(result);
}
private bool RestoreDeviceStatus()
{
return true;
}
private void OnForceError(SequenceContext arg1, string arg2)
{
//List 실행 종료
_Scanner.StopExecution();
//Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND);
//Scanner 원점 복귀
_Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
Log.I("SequenceAction Ok : OnForceError");
}
private void OnStopped(SequenceContext obj)
{
//List 실행 종료
_Scanner.StopExecution();
//Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND);
//Scanner 원점 복귀
_Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
Log.I("SequenceAction Ok : OnStopped");
}
}
// TODO: UseStream 으로 TeachingRun 을 할 필요가 있을까?? 확인 필요
public class SeqTeachingRun_InitParam : InitParam
{
public bool IsObjectSelectAll { get; set; } // All == true, Selected Object == false
public bool UseStream { get; set; }
public bool UseBlow { get; set; }
public int IntervalTimeBlowOn { get; set; }
public int IntervalTimeBlowOff { get; set; }
public bool NeedSetLaserTiming { get; set; } // For SubSequence, SeqUploadWeldingList
public bool UseMovingJig { get; set; }
}
public class SeqTeachingRun_OutParam : OutParam
{
}
}