2026-06-05 13:44:39 +09:00

552 lines
23 KiB
C#

using Aurora.Api;
using Aurora.Api.Services;
using AuroraIO;
using AuroraLaserScanner;
using AuroraLBAD;
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.Settings;
using WeldingCommon.Utils;
using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo;
using static AuroraIO.Vendor.PAIX.PAIXIO;
namespace WeldingCommon.Sequences
{
public 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_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 StreamLBADClient _StreamLBADClient;
private SystemParameter _SystemParameter;
private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언
private eListNo _WeldingScannerListNo;
private List<IDrawObject> _SelectedObjectList;
private static 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, CommonSequenceManager.GetSeqBuilder_SeqUploadWeldingList());
sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult);
sb.AddAction(SEQ_SET_BLOW_ON, SetBlowOn);
sb.AddAction(SEQ_STREAM_LBAD_BEFORE_WELDLINES, StreamLBADBeforeWeldlines);
sb.AddAction(SEQ_EXECUTE_WELDLINES, ExecuteWeldLines);
sb.AddAction(SEQ_SET_BLOW_OFF, SetBlowOff);
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_SET_BLOW_ON] > sb[SEQ_STREAM_LBAD_BEFORE_WELDLINES] > sb[SEQ_EXECUTE_WELDLINES]
> sb[SEQ_SET_BLOW_OFF] > 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)
{
var mainPage = CommonHelper.GetMainPage();
_ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching;
_WeldingScannerListNo = eListNo.LIST1;
if (_InitParam.IsObjectSelectAll) {
_SelectedObjectList = _ScannerCanvasUserCtrl.Model.ActiveLayer.Objects;
} else {
_SelectedObjectList = _ScannerCanvasUserCtrl.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();
if (_InitParam.UseStream) {
_StreamLBADClient = context.GetLBADClient();
}
// @geprecated 각형배터리에서 문제 생겨서 추가한 코드임
// Scanner를 다시 초기화해주면 시간이 오래걸린다.. 굳이 수동용접시 마다 해줄 필요가 없음
//if (E1.OK != _scanner._scannerObj.Open())
//{
// Log.E("Scanner open Error");
// return SequenceAction.Error();
//}
//if (E1.OK != _scanner._scannerObj.Initialize(_scanner.Config))
//{
// Log.E("Scanner Initialize Error");
// return SequenceAction.Error();
//}
}
private bool CheckDeviceReady(SequenceContext context)
{
List<string> deviceListToCheck = new List<string>() { CommonDeviceIds.Scanner, /*CommonDeviceIds.OCT, */CommonDeviceIds.IODevice };
if (_InitParam.UseStream) {
deviceListToCheck.Add(CommonServiceIds.StreamLBADClient);
}
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._scannerObj.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()
{
// TODO: 이거는 원래 source 에 없었는데, 언제 반영 되었는지 확인 필요. 그리고 맞는 방법으로 되었는지 확인 필요함.
// TODO: 우선은 주석 처리 해 둠. 2025.03.05
// TODO: StreamLBADClient 의 생성자에 해당 Message 가 있음. 이걸 좀 바꿔야 함. 생성할때 보내는 것이 맞는지 명시적으로 보내는것이 맞는지.
//// CELL ID와 LOT ID는 CellTacker로부터 받은 정보를 LBAD측으로 용접 전 줘야함, Reply는 로그만 남기자
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_CELL_ID, new StreamStringParser(), new StreamNumberParser<int>());
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_LOT_ID, new StreamStringParser(), new StreamNumberParser<int>());
////LBAD측이랑 통신하기 전 항상 ReadyStauts에 대한 Request하고 LBAD에서는 Ready가 되있으면 Reply를 한다..
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_READY_STATUS, new StreamNumberParser<int>(), new StreamNumberParser<int>());
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_WELDING_TRIGGER, new StreamNumberParser<int>(), new StreamNumberParser<int>());
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_WELDING_STOP, new StreamNumberParser<int>(), new StreamNumberParser<int>());
////용접 완료 후 LBAD측에 JudgeComplete를 요청한뒤 JudgeFlag를 회신 받는다
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_JUDGEMENT_COMPLETE, new StreamNumberParser<int>(), new StreamNumberParser<int>());
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_CELL_ID, new StreamStringParser(), new StreamNumberParser<int>());
//_StreamLBADClient.AddParser(StreamMessageNames.MESSAGE_PC_LBAD_LOT_ID, new StreamStringParser(), new StreamNumberParser<int>());
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 = 0.0,
CellMeasureCenterOffsetY = 0.0,
SelectedDrawObjectList = _SelectedObjectList,
WeldingScannerListNo = _WeldingScannerListNo,
};
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 SetBlowOn(SequenceContext context, string id, SequenceActionResult param)
{
if (_InitParam.UseBlow == false) {
return SequenceAction.Ok();
}
int intervalTime = _InitParam.IntervalTimeBlowOn;
_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.SeamWeldingN2BlowSol), true);
_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.SeamWeldingOpticAirKnifeSol), true);
Thread.Sleep(intervalTime);
Log.D(string.Format($"[TeachingRun] Blow ON Complete"));
return SequenceAction.Ok();
}
private SequenceActionResult StreamLBADBeforeWeldlines(SequenceContext context, string id, SequenceActionResult param)
{
if (_InitParam.UseStream == false) {
return SequenceAction.Ok();
}
if (!_StreamLBADClient.CheckReadyStatus()) {
return null;
}
if (!_StreamLBADClient.SendCellID(1)) {
return null;
}
if (!_StreamLBADClient.SendLotID(2)) {
return null;
}
if (!_StreamLBADClient.SetWeldTriggerOn()) {
return null;
}
return SequenceAction.Ok();
}
private SequenceActionResult StreamLBADAfterWeldlines(SequenceContext context, string id, SequenceActionResult param)
{
if (_InitParam.UseStream == false) {
return SequenceAction.Ok();
}
//Welding Stop On
if (!_StreamLBADClient.SendWeldingStop()) {
return null;
}
//JudgeFlag에 따라서 시퀀스 결정
var judgeFlag = _StreamLBADClient.CheckJudgeComplete();
if (judgeFlag == StreamLBADClient.eJudgeFlag.NO_RESPONSE) {
return null;
}
return SequenceAction.Ok();
}
private SequenceActionResult ExecuteWeldLines(SequenceContext context, string id, SequenceActionResult param)
{
Log.D(string.Format($"[TeachingRun] Welding Start"));
DateTime weldingTime = DateTime.Now;
_Scanner.ExecuteList(_WeldingScannerListNo);
_Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut);
_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"));
//Welding Stop On
if (_InitParam.UseStream) {
if (!_StreamLBADClient.SendWeldingStop()) {
return null;
}
}
// OCT File Fomrat Record Test
//SaveOCTFileFormat();
// File Format Record Test
weldingTime = DateTime.Now;
//SaveScannerRecordFileForamt();
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] SaveScannerRecordFileForamt Time : {_WeldingSpanTime.TotalMilliseconds}"));
weldingTime = DateTime.Now;
_Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
//용접 완료 후 CorrectionFile Vision으로 원복
_Scanner.SelectCorrectionTable(1);
_WeldingSpanTime = DateTime.Now.Subtract(weldingTime);
Log.D(string.Format($"[TeachingRun] Change SelectCorrectionTable Time : {_WeldingSpanTime.TotalMilliseconds}"));
return SequenceAction.Ok();
}
private void SaveScannerRecordFileForamt()
{
_Scanner.SaveRecord("Normal");
}
private void SaveOCTFileFormat()
{
// TODO: ian0513.kim, DataPath 옮겨야 함.
var DefaultPath = ConfigurationManager.AppSettings.Get("DATA_PATH");
string fullPath = Path.Combine(DefaultPath, "_device", "_scanner", "Record");
string coordinateFolderpath = fullPath + string.Format("\\Coordinate");
string metaDataFolderpath = fullPath + string.Format("\\Metadata");
if (Directory.Exists(coordinateFolderpath) == false) {
Directory.CreateDirectory(coordinateFolderpath);
}
if (Directory.Exists(metaDataFolderpath) == false) {
Directory.CreateDirectory(metaDataFolderpath);
}
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)
}
_Scanner.SaveRecord("CoordinateFile", true, MetaDataWeldCoordinateID, iCoordinateID);
_OCT.SaveMetaData(MetaDataWeldName, MetaDataWeldType, MetaDataWeldCoordinateID);
string coordinateFilepath = _Scanner.GetLatestFile(coordinateFolderpath);
string metaDataFilepath = _Scanner.GetLatestFile(metaDataFolderpath);
_Scanner.SaveRecordZip(coordinateFilepath, metaDataFilepath);
MetaDataWeldName.Clear();
MetaDataWeldType.Clear();
MetaDataWeldCoordinateID.Clear();
}
private SequenceActionResult SetBlowOff(SequenceContext context, string id, SequenceActionResult param)
{
if (_InitParam.UseBlow == false) {
return SequenceAction.Ok();
}
int intervalTime = _InitParam.IntervalTimeBlowOff;
Thread.Sleep(intervalTime);
_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.SeamWeldingN2BlowSol), false);
_IO._iOUnit.WriteDoBit(Convert.ToInt32(ePDOAddress.SeamWeldingOpticAirKnifeSol), false);
Log.D(string.Format($"[TeachingRun] Blow OFF Complete"));
return SequenceAction.Ok();
}
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 IsUseExternalObjects { get; set; }
public List<CoreUnit.Control.UserControl.Canvas.IDrawObject> ExternalObjects { get; set; }
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 double CellCenterOffsetX { get; set; } = 0;
public double CellCenterOffsetY { get; set; } = 0;
}
public class SeqTeachingRun_OutParam : OutParam
{
}
}