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

1378 lines
63 KiB
C#

using Aurora;
using Aurora.Api;
using Aurora.Api.Devices;
using Aurora.Api.Manager;
using Aurora.View;
using AuroraCellTracker;
using AuroraIO;
using AuroraLaser;
using AuroraLaser.Trumpf;
using AuroraLaserScanner;
using AuroraLBAD;
using AuroraLight;
using AuroraOCT;
using AuroraPLC;
using CoreUnit.Control.UserControl.Canvas;
using CoreUnit.Scanner.UserControl.ScannerCanvas;
using DB;
using DB.Recipe;
using Hermes;
using Hermes.Functional.Option;
using ILSOS_PrismaticScanner.Device;
using ILSOS_PrismaticScanner.Models;
using Matrox.MatroxImagingLibrary;
using MilCommon;
using MilUI;
using OpenCvSharp;
using OpenCvSharp.Dnn;
using OpenCvSharp.WpfExtensions;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Remoting.Contexts;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms.VisualStyles;
using System.Windows.Media.Imaging;
using VisionCommon;
using WeldingCommon;
using WeldingCommon.Alarm;
using WeldingCommon.Data;
using WeldingCommon.Devices;
using WeldingCommon.Models;
using WeldingCommon.Sequences;
using WeldingCommon.Sequences.Main;
using WeldingCommon.Settings;
using WeldingCommon.Utils;
using WeldingCommon.Views;
using WeldingCommon.Vision.WeldingObjectGenerator;
using static Aurora.Api.SequenceBuilder;
using static AuroraIO.AJINEXTEK.AjinIO.IOModuleInfo;
using static AuroraIO.Vendor.PAIX.PAIXIO;
using static ILSOS_PrismaticScanner.Device.PLCControllerPrismaticScanner;
using static WeldingCommon.Devices.PLCController;
using static WeldingCommon.Sequences.Main.SeqExecutePlcBitInterface_InitParam;
namespace ILSOS_PrismaticScanner.Sequences
{
/// <summary>
/// 각형 스캐너 타입 자동 운전을 위한 시퀸스
/// </summary>
class SeqAutoRunPrismaticScanner
{
static readonly Logger Log = new Logger("SeqAutoRunPrismaticScanner");
#region 시퀸스 변수명
const string SEQ_ENTER = "SEQ_ENTER"; // inputParam 으로 받자.
const string SEQ_CHECK_INIT_CONDITION = "Check Init Condition"; // Check Init Condition
const string SEQ_FORK_CHECK_SEQ_SETTING = "Fork Check Seq Setting";
const string SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ = "Set Sequence Param (ExecutePlcPageChangeOrWeldRequestInterface Seq)";
const string SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST = "Process (Wait Page Change Request Or Weld Request)";
const string SEQ_GET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ = "Get Sequence Result (ExecutePlcPageChangeOrWeldRequestInterface Seq)";
const string SEQ_CHECK_CELL_INFO_RECEIVE = "Check Cell Info Receive"; // 셀 정보 수신 확인
const string SEQ_CHECK_WELDDING_RETRY = "Check Welding Retry";
const string SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ = "Get Sequence Result (ReceiveCellInfo Seq)";
const string SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ = "Process (ReceiveCellInfo Seq)";
const string SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ = "Set Sequence Param (ReceiveCellInfo Seq)";
const string SEQ_JOIN_CELL_INFO_RECEIVE = "Complete Check Cell Info Receive";
const string SEQ_CHECK_VISION_COMPENSION_USE = "Check Vision Compensation Use"; // 비전 보상 사용 확인
const string SEQ_DO_VISION_JOB = "Do Vision Job"; // 비전 작업 진행
const string SEQ_JOIN_VISION_COMPENSION = "Complete Vision Compensation";
const string SEQ_CHECK_SAVE_WELD_IMAGE = "Check Save Weld Image";
const string SEQ_SAVE_WELD_IMAGE = "Save Weld Image"; // 용접 후 이미지 저장
const string SEQ_JOIN_CHECK_SEQ_SETTING = "Join Check Seq Setting";
const string SEQ_SEND_EVENT_SENDER = "Event Sender"; // Image Q 저장
const string SEQ_SEND_EVENT_SENDER2 = "Event Sender2"; // Image Q 저장
const string SEQ_EVENT_Q_SAVE_FILE = "EventQ Save File";
const string SEQ_SAVE_IMAGES = "Save Images";
const string SEQ_CHECK_VISION_COMPLETE = "Check Vision Complete";
const string SEQ_NG_RESULT_PROCESS = "Ng Result Process";
const string SEQ_GENERATE_WELDING_OBJECT = "Generate Welding Object"; // 용접 객체 생성
const string SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ = "Set Params Component Teachingrun Seq"; // 용접 리스트 업로드 파라미터 설정
const string SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ = "Process (Component Teachingrun Seq)"; // 용접 리스트 업로드 시퀀스 처리
const string SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ = "Get Result Component Teachingrun Seq"; // 용접 리스트 업로드 결과 가져오기
const string SEQ_GET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ = "Get Sequence Result (PLC Weld Complete Interface Seq)";
const string SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ = "Process (PLC Weld Complete Interface Seq)";
const string SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ = "Set Sequence Param (PLC Weld Complete Interface Seq)";
const string SEQ_SAVE_RESULT_DATA = "Save Result Data"; // 결과 데이터 저장
const string SEQ_COMPLETE = "SEQ_COMPLETE"; // 완료
#endregion
static class ContextKey
{
public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqAutoRunPrismaticScanner";
public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqAutoRunPrismaticScanner";
}
#region 변수 정의
// 디바이스 정의
private Scanner _Scanner;
private Laser _Laser;
private PLCControllerPrismaticScanner _PLC;
private IODevice _IO;
private LightDevice _Light;
// 비전 정의
private CameraAgentBase _CameraAgent;
private ICameraDisplay _CameraDisplay;
private PatternAgentBase _PatternAgent;
private List<PatternInfo> _Patterns;
private PatternInfo _PatternInfo;
private PatternResult _PatternResult;
private FieldScanParams _FieldScanParams;
private object _GrabImage;
private MIL_ID _GrabImageMilId;
private int _RetryCount = 0;
private double _OffsetXfromModelFinder = 0;
private double _OffsetYfromModelFinder = 0;
// 시스템 및 용접 파라미터 정의
private SystemParameter _SystemParameter;
private static SequenceSetting _SequenceSetting;
private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언
private List<IDrawObject> _SelectedDrawObjectList;
private static List<IDrawObject> _UnpackDrawObject;
private SeqAutoRunPrismaticScanner_InitParam _InitParam;
// 생산 데이터 정의
private uint _TodayProduct;
private uint _TotalProduct;
private DateTime _AutoRunStartTime;
private DateTime _WeldStartTime;
private DateTime _WeldEndTime;
private TimeSpan _WeldSpanTime;
private TimeSpan _InterfaceSpanTime;
private DateTime _InterfaceStartTime;
private string _LotId = "N/A";
private string _CellId = "N/A";
const int timeoutSeconds = 10;
private bool _FirstUpdateStep = true;
private bool _IsWeldingRetry = false;
private int _CurrentWeldingStep = 0;
private int _TotalWeldingStep = 0;
private int _PreviousTotalWeldingStep = 0;
private int _PageNo = 1;
private MIL_ID _ResultImageMilId;
private bool _SuccessVisionResult = false;
const int PLC_TIMEOUT = 60; // sec, Timeout은 시퀀스 정지가 아니라 타임아웃 시간마다 로그만 남기도록 한다
public string EVENT_TRIGGER_SAVE_FILE = "EventTriggerSaveFile";
// 공정 시간 측정
private static DateTime _WeldReqTime;
private static DateTime _WeldComTime;
private List<int> lightValues = new List<int> { 255, 255, 255, 255 };
private (double X, double Y) TargetObjectCenterPosition = (X: 0.0, Y: 0.0);
#endregion
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); //분기
///// PLC_USE_PAGE_CHANGE_INTERFACE
sb.AddAction(SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, SetPlcPageChangeOrWeldRequest_SeqParams);
sb.AddSubsequence(SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST, SequenceManager.GetSeqBuilder_SeqWaitWeldOrChangePage());
sb.AddAction(SEQ_GET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, GetPlcPageChangeOrWeldRequest_SeqParams);
///// Fork
sb.AddFork(SEQ_FORK_CHECK_SEQ_SETTING);
///// Check & Cell Load
sb.AddDecision(SEQ_CHECK_CELL_INFO_RECEIVE, CheckCellInfoReceive);
sb.AddDecision(SEQ_CHECK_WELDDING_RETRY, CheckWeldingRetry);
sb.AddAction(SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ, SetReceiveCellInfo_SeqParams);
sb.AddSubsequence(SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ, SequenceManager.GetSeqBuilder_SeqReceiveCellInfo());
sb.AddAction(SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ, GetReceiveCellInfo_SeqResult);
sb.AddAction(SEQ_JOIN_CELL_INFO_RECEIVE, JoinCellInfoReceive);
///// Check & Use Model Finder
sb.AddDecision(SEQ_CHECK_VISION_COMPENSION_USE, CheckVisionCompensationUse);
sb.AddAction(SEQ_DO_VISION_JOB, DoVisionJob);
sb.AddAction(SEQ_JOIN_VISION_COMPENSION, JoinVisionCompensation);
sb.AddDecision(SEQ_CHECK_SAVE_WELD_IMAGE, CheckSaveWeldImage);
sb.AddAction(SEQ_SAVE_WELD_IMAGE, SaveWeldImage);
///// Event Q
sb.AddEventSender(SEQ_SEND_EVENT_SENDER, SendEvent);
sb.AddEventSender(SEQ_SEND_EVENT_SENDER2, SendEvent);
sb.AddEventQ(SEQ_EVENT_Q_SAVE_FILE, 2);
sb.AddAction(SEQ_SAVE_IMAGES, SaveImagesEvent);
///// Join
sb.AddJoin(SEQ_JOIN_CHECK_SEQ_SETTING);
sb.AddDecision(SEQ_CHECK_VISION_COMPLETE, CheckVisionComplete);
sb.AddAction(SEQ_NG_RESULT_PROCESS, NgResultProcess);
///// Weld Start / Complete
sb.AddAction(SEQ_GENERATE_WELDING_OBJECT, GenerateWeldingObject);
sb.AddAction(SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ, SetComponentTeachingrun_SeqParams);
sb.AddSubsequence(SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ, SequenceManager.GetSeqBuilder_SeqComponentTeachingRun());
sb.AddAction(SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ, GetComponentTeachingrun_SeqResult);
///// Check Welding Retry Request;
sb.AddAction(SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, SetPlcWeldCompleteInterface_SeqParams);
sb.AddSubsequence(SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ, SequenceManager.GetSeqBuilder_SeqExecutePlcBitInterface());
sb.AddAction(SEQ_GET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, GetPlcWeldCompleteInterface_SeqParams);
sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함.
sb.AddAction(SEQ_COMPLETE, Complete);
// [Action] 초기 세팅
sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION]
// [Decision] PLC에서 페이지 변경 및 용접 요청 들어온 경우
> sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ] > sb[SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST] > sb[SEQ_GET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]
//// Fork1
// [Decision] 셀 ID를 받을 수 있는 설정인 경우
> sb[SEQ_FORK_CHECK_SEQ_SETTING]);
sb.Flows(sb[SEQ_FORK_CHECK_SEQ_SETTING] > sb[SEQ_CHECK_CELL_INFO_RECEIVE] > sb[SEQ_CHECK_WELDDING_RETRY] > sb[SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_JOIN_CELL_INFO_RECEIVE]
//// Join
> sb[SEQ_JOIN_CHECK_SEQ_SETTING] > sb[SEQ_CHECK_VISION_COMPLETE]);
// 비전 실패
sb.Flows(sb[SEQ_CHECK_VISION_COMPLETE] > sb[SEQ_NG_RESULT_PROCESS] > sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]);
// 비전 성공
sb.Flows(sb[SEQ_CHECK_VISION_COMPLETE] > sb[SEQ_GENERATE_WELDING_OBJECT]
//// [Action] 용접 시작
> sb[SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ] > sb[SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ] > sb[SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ] > sb[SEQ_CHECK_SAVE_WELD_IMAGE] > sb[SEQ_SAVE_WELD_IMAGE] > sb[SEQ_SEND_EVENT_SENDER2].Send(EVENT_TRIGGER_SAVE_FILE)
//// [Decision] 용접 완료 혹은 재시작 (GET_PARAMS 에서 진행)
> sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ] > sb[SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ] > sb[SEQ_GET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]
// [Action] 결과값 입력
> sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]);
///// Fork2 & Join
// [Decision] 비전 보상이 필요한 경우
// [Decision] OFFSET 입력 혹은 재시도 결정
sb.Flows(sb[SEQ_FORK_CHECK_SEQ_SETTING] > sb[SEQ_CHECK_VISION_COMPENSION_USE] > sb[SEQ_DO_VISION_JOB] > sb[SEQ_SEND_EVENT_SENDER].Send(EVENT_TRIGGER_SAVE_FILE) > sb[SEQ_JOIN_VISION_COMPENSION] > sb[SEQ_JOIN_CHECK_SEQ_SETTING]);
// Scout Image Save Event Seq
sb.Flows(sb[SEQ_EVENT_Q_SAVE_FILE].On(EVENT_TRIGGER_SAVE_FILE) > sb[SEQ_SAVE_IMAGES] > sb.FlowFinal);
// SEQ_CHECK_INIT_CONDITION
sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final);
// SEQ_CHECK_CELL_INFO_RECEIVE
sb.Flows(sb[SEQ_CHECK_CELL_INFO_RECEIVE] > sb[SEQ_JOIN_CELL_INFO_RECEIVE]);
sb.Flows(sb[SEQ_CHECK_WELDDING_RETRY] > sb[SEQ_JOIN_CELL_INFO_RECEIVE]);
// SEQ_CHECK_VISION_COMPENSION_USE
sb.Flows(sb[SEQ_CHECK_VISION_COMPENSION_USE] > sb[SEQ_JOIN_VISION_COMPENSION]);
sb.Flows(sb[SEQ_CHECK_SAVE_WELD_IMAGE] > sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]);
sb.OnForceError(OnForceError);
sb.OnStopped(OnStopped);
//sb.Draw("SeqAutoRunPrismaticScanner.pdf");
return sb;
}
private string CheckWeldingRetry(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (_IsWeldingRetry) return SEQ_JOIN_CELL_INFO_RECEIVE;
else return SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ;
}
#region 시퀸스 함수
#region 1. 디바이스 및 변수 상태 확인
private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("SeqAutoRunPrismaticScanner Start");
_InitParam = param.Param as SeqAutoRunPrismaticScanner_InitParam;
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
_SequenceSetting = context.GetSetting<SequenceSetting>(CommonSettingIds.Sequence);
InitDev(context);
if (!CheckDeviceReady(context)) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_DeviceNotReady);
Log.E("The devices are not active.");
return null;
}
if (InitSequenceVariable(context) == false) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_InitVariableError);
return null;
}
// Setting 값
_PageNo = _SequenceSetting.LastPageNo;
Log.I($"Previous Page No is {_PageNo}");
Log.I($"Sequence Setting Info is 1) ImageAcquire : {_SequenceSetting.UseImageAcquire}, 2) UseCellOffset : {_SequenceSetting.UseCellOffset}.");
return SequenceAction.Ok();
}
private string CheckInitCondition(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
Status.SetWorkingPosition(eWorkingPosition.CHECKING_CONDITION);
if (!CheckCondition(context)) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_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);
// 자동 운전 시, 비전 보상 유무에 따른 조명 값 변경
if (_SequenceSetting.UseCellOffset) UpdateAutoLightValue(_SequenceSetting.CompensationLightValue);
else UpdateAutoLightValue(_SequenceSetting.LightValue);
// Light ALL ON
if (!_Light.CheckLightOnAndValue(lightValues[0])) {
_Light.SetLightValue(lightValues);
Log.I($"Light All On !");
}
// 레이저 레디 상태가 아니면, 레디 시켜야 함.
if (!_Laser.GetLaserReady()) {
_Laser.LaserOn();
Log.I($"Start Laser ON in CheckInitCondition");
}
// 너무 빨리 되어서 문제인 것인가 ?
var res = context.RetryUntilSuccess(_Laser.GetLaserReady, timeoutSeconds * 1000, 10);
if (res == E2.TIMEOUT) {
Log.E("Laser Ready Fail When Autorun");
return null;
}
InitializePlcBits();
// CheckInitCondition 완료된다면..
_PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true);
_PLC.WriteWord(eWordOutput.PC_PAGE_NO, (short)_PageNo);
Log.I("Complete CheckInitCondition.");
return SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ;
}
#endregion
#region 2. Page 변경 시퀸스 ~ 3. Weld Req 시퀸스
private SequenceActionResult SetPlcPageChangeOrWeldRequest_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
Log.I(string.Format("PLC Interface(PageChange, WeldReq) Start; (REQ,ACK); ({0},{1});({2},{3})",
(int)eBitInput.PLC_PAGE_CHANGE_REQ, (int)eBitOutput.PC_PAGE_CHANGE_COMPLETE, (int)eBitInput.PLC_WELD_READY_REQ, (int)eBitOutput.PC_WELD_READY_ACK));
Status.SetWorkingPosition(eWorkingPosition.LOADING_CELL);
// PLC 인터페이스 전까지 테스트
SeqWaitWeldOrChangePage_InitParam seqParams = new SeqWaitWeldOrChangePage_InitParam() {
CurrentPageNo = (short)_PageNo,
NeedPageNo = _SequenceSetting.UsePageChangeMode,
TimeoutSec = 10,
Models = _ScannerCanvasUserCtrl.Model
};
return SequenceAction.Ok(seqParams);
}
private SequenceActionResult GetPlcPageChangeOrWeldRequest_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
// PageChangeOrWeldRequest 탈출 조건은 Weld Request가 동작했을 때임
// 해당 단계까지 왔다면 용접 요청까지 받은 상태
var resultOutParam = param.Param as SeqWaitWeldOrChangePage_OutParam;
_WeldReqTime = resultOutParam._WeldReqTime;
if (resultOutParam.Success == false) {
Log.E("Failed Page Change & Weld Request Interface");
return null;
}
Log.I($"Complete Page Change & Weld Request Interface");
// 처음 인터페이스할 때, Word Data 받아옴.
_PreviousTotalWeldingStep = _TotalWeldingStep;
_PageNo = resultOutParam.PLCPageNo;
// Total은 항상 같으므로 상관 없을 듯.
_TotalWeldingStep = Convert.ToInt16(_PLC.ReadWord(eWordInput.PLC_STEP_NO).data);
_SequenceSetting.LastPageNo = _PageNo;
context.Settings.Save(CommonSettingIds.Sequence, _SequenceSetting, "SeqAutoRunPrismaticScanner_New");
// 용접 전에 NG 및 FDC 정보를 초기화한다.
Status.GetProcessingWeldingData().IsVisionNG = false;
_PLC.WriteBit(eBitOutput.PC_WELD_NG_JUDGE, false);
var UploadDatas = Status.GetProcessingWeldingData().UploadProcessDatas;
foreach (var data in UploadDatas)
data.Clear();
_PLC.UploadReportDatas(UploadDatas);
// PLC에게 값을 써주어야 함.
//_PLC.WriteWord((int)eWordOutput.PC_PAGE_NO, (short)_PageNo, PLCBase.ePLCDevice.D);
Log.I(string.Format("Read Words; Page No: {0}; Step No: {1}", _PageNo, _TotalWeldingStep));
if (_PageNo > _ScannerCanvasUserCtrl.Model.Layers.Count() && _PageNo > 0) {
Log.E(string.Format("Page No isn't proper. ;Page No : {0}; Layers Count: {1}", _PageNo, _ScannerCanvasUserCtrl.Model.Layers.Count()));
return null;
}
Status.GetProcessingWeldingData().CellId = _PLC.CellId();
Status.GetProcessingWeldingData().LotId = _PLC.LotId();
if (_FirstUpdateStep) {
_FirstUpdateStep = false;
return SequenceAction.Ok();
}
if (_PreviousTotalWeldingStep != _TotalWeldingStep) {
Log.E(string.Format("SeqCheckWeldingComplete Error;Changed Step No; Prev: {0}; Current: {1}", _PreviousTotalWeldingStep, _TotalWeldingStep));
return null;
}
return SequenceAction.Ok();
}
#endregion
#region 3. 용접 요청 인터페이스 (PLC <-> PC)
private SequenceActionResult SetPlcWeldRequest_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
Log.I(string.Format("Test1; REQ: {0}; ACK: {1}", (int)eBitInput.PLC_WELD_READY_REQ, (int)eBitOutput.PC_WELD_READY_ACK));
// PLC 인터페이스 전까지 테스트
SeqExecutePlcBitInterface_InitParam seqParams = new SeqExecutePlcBitInterface_InitParam() {
GetAddress = eCommonBitInput.PLC_WELD_READY_REQ,
SetAddress = eCommonBitOutput.PC_WELD_READY_ACK,
TimeoutSec = 60,
FirstTimeoutSec = 6 * 60 * 60, // 2시간동안 대기,
IsSetMode = false
};
return SequenceAction.Ok(seqParams);
}
private SequenceActionResult GetPlcWeldRequest_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
// PLC 인터페이스 전까지 테스트
var resultOutParam = param.Param as SeqExecutePlcBitInterface_OutParam;
if (resultOutParam.Success == false) {
return null;
}
return SequenceAction.Ok();
}
#endregion
#region 4.1. 셀 데이터 받기
private string CheckCellInfoReceive(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (_SequenceSetting.UseCellReport) {
Log.I(string.Format("Check Cell Info Receive"));
return SEQ_CHECK_WELDDING_RETRY;
}
else {
Log.I(string.Format("Pass Cell Info Receive"));
return SEQ_JOIN_CELL_INFO_RECEIVE;
}
}
private SequenceActionResult SetReceiveCellInfo_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
SeqReceiveCellInfo_InitParam seqParams = new SeqReceiveCellInfo_InitParam() {
};
return SequenceAction.Ok(seqParams);
}
private SequenceActionResult GetReceiveCellInfo_SeqResult(SequenceContext context, string id, SequenceActionResult param)
{
var resultOutParam = param.Param as SeqReceiveCellInfo_OutParam;
if (resultOutParam.Success == false) {
return null;
}
return SequenceAction.Ok();
}
private SequenceActionResult JoinCellInfoReceive(SequenceContext context, string id, SequenceActionResult param)
{
return SequenceAction.Ok();
}
#endregion
#region 4.2. 비전 보상 진행
private string CheckVisionCompensationUse(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (_SequenceSetting.UseImageAcquire) {
Log.I(string.Format("Use Image Acquire"));
// 비전 보상 유무에 따른 조명 값 변경
if (_SequenceSetting.UseCellOffset) UpdateAutoLightValue(_SequenceSetting.CompensationLightValue);
else UpdateAutoLightValue(_SequenceSetting.LightValue);
if (!_Light.CheckLightOnAndValue(lightValues[0])) {
_Light.SetLightValue(lightValues);
Log.I($"Light All On !");
}
return SEQ_DO_VISION_JOB;
}
else {
// Welding Object로 넘겨주기 전에는 Offset 값을 써주어야 됨 !!
_RetryCount = 0;
_OffsetXfromModelFinder = 0;
_OffsetYfromModelFinder = 0;
Log.I(string.Format("Unuse Image Acquire"));
return SEQ_JOIN_VISION_COMPENSION;
}
}
private static SeqVisionData_Param visionParam;
private SequenceActionResult DoVisionJob(SequenceContext context, string id, SequenceActionResult param)
{
// 1. Pattern은 0번부터 시작이며, Page 숫자 OR 그 이상으로 Pattern이 있어야 함.
_PatternInfo = _Patterns[_PageNo - 1];
// 2.1. 스캐너 이동을 객체 기반의 중앙 기반 이동
FindObjectsCenter(out TargetObjectCenterPosition.X, out TargetObjectCenterPosition.Y);
Log.I(string.Format($"Move Scanner - ScannerPosX : {TargetObjectCenterPosition.X},ScannerPosY : {TargetObjectCenterPosition.Y} "));
_Scanner.JumpAbs(TargetObjectCenterPosition.X, TargetObjectCenterPosition.Y);
// 2.2. 이동까지 기다려주어야 함.
//250701 Vision Image 흔들림으로 인해 20ms로 변경
Thread.Sleep(20);
// 3.1. CameraDisplay의 Overlay된 것들 모두 클리어해줌.
_CameraDisplay.ClearMeasureGraphics();
_CameraDisplay.ClearFindGraphics();
_CameraDisplay.ClearTrainGraphics();
// 3.2. StopLive GrabSync를 통해서 이미지를 취득함.
object image = null;
_CameraAgent.StopLive();
_CameraAgent.GrabSync(_CameraDisplay, ref image);
// 4. 패턴 찾기 및 Score 기반으로 용접 여부 판단 변수 업데이트
bool isHigherLimitScore = false;
// 첫 번째 Offset 값을 사용하고 싶을 경우 UseExistOffset 활용할 것. 그러나 활용도는 매우 낮을 듯 함. 250701 기준 EL도 사용하지 않음.
if (_SequenceSetting.UseExistOffset && _PageNo != 1) {
Log.I(string.Format($"UseExistOffset : X(mm) : {_OffsetXfromModelFinder}, Y(mm) : {_OffsetYfromModelFinder}"));
if (_PatternResult != null && _PatternResult.Status == eFoundStatus.FoundOK) isHigherLimitScore = IsHigherLimitScore(_SequenceSetting.FirstLimitScore, _PatternInfo.PatternType);
}
else {
_PatternResult = _PatternAgent.Find(_PatternInfo, image, _CameraDisplay);
Log.I(string.Format($"First Model Find : {_PatternResult.Status.ToString()}"));
// 첫 번째 실패하면, 한 번 더 찾을 수 있는 로직 구성. (UseSubPatternOnFailure 사용)
if (_PatternResult.Status != eFoundStatus.FoundOK && _SequenceSetting.UseSubPatternOnFailure) {
_PatternInfo = _Patterns[_PageNo - 1 + _TotalWeldingStep];
_PatternResult = _PatternAgent.Find(_PatternInfo, image, _CameraDisplay);
Log.I(string.Format($"Second Model Find : {_PatternResult.Status.ToString()}"));
if (_PatternResult.Status == eFoundStatus.FoundOK)
isHigherLimitScore = IsHigherLimitScore(_SequenceSetting.SecondLimitScore, _PatternInfo.PatternType);
}
else {
if (_PatternResult.Status == eFoundStatus.FoundOK)
isHigherLimitScore = IsHigherLimitScore(_SequenceSetting.FirstLimitScore, _PatternInfo.PatternType);
}
}
// 4.1. 패턴 Found 여부 업데이트
if (_PatternResult != null && _PatternResult.Status == eFoundStatus.FoundOK)
Status.GetProcessingWeldingData().IsVisionNG = false;
else
Status.GetProcessingWeldingData().IsVisionNG = true;
// 5. 이미지 저장 관련 EventQueue 사용을 위한 Parameter 생성
MIL_ID temp1;
MIL_ID temp2;
// 5.1. 만약, Draw 중 문제 생길 경우 Log Error 발생하도록 수정
try {
if (!Status.GetProcessingWeldingData().IsVisionNG)
_PatternAgent.Draw(_PatternInfo, _PatternResult, _CameraDisplay);
temp1 = MilEnvironment.CloneImage((MIL_ID)image);
temp2 = MilEnvironment.GetOverlayImage((IMilCameraDisplay)_CameraDisplay);
Log.I($"Vision Draw Successed.");
}
catch (Exception ex) {
temp1 = MIL.M_NULL;
temp2 = MIL.M_NULL;
Log.E($"Vision Draw Error Occured !!!!!!;{ex.Message}");
}
// 6. Offset 값 추출 및 인터락 체크
if (!Status.GetProcessingWeldingData().IsVisionNG) {
// 6.1. 보상 필요할 경우 Offset 값 구하기
if (_SequenceSetting.UseCellOffset) {
double offsetX = 0;
double offsetY = 0;
#region 기존 Offset 값 추출 코드. 뒤 코드 가독성이 좋아보여서 수정하였음. 문제 시 해당 코드 사용
//foreach (BaseResult baseResult in _PatternResult.Results) {
// if (_PatternInfo.PatternType == ePatternType.ImageModelFinder || _PatternInfo.PatternType == ePatternType.ShapeModelFinder) {
// var result = baseResult as ModelFinderResult;
// offsetX = result.ImagePosition.X;
// offsetY = result.ImagePosition.Y;
// } else if (_PatternInfo.PatternType == ePatternType.PatternMatching) {
// var result = baseResult as PatternMatchingResult;
// offsetX = result.ImagePosition.X;
// offsetY = result.ImagePosition.Y;
// } else {
// Log.E("Pattern Type is not proper !!! ");
// return null;
// }
// break;
//}
#endregion
// 6.1.1. 첫 번째 결과를 보상 값으로 활용함. 해당 구문 활용하여 추후 Score가 제일 높은 것을 고를 수도 있을 듯 함.
BaseResult baseResult = _PatternResult.Results.FirstOrDefault();
if (baseResult != null) {
switch (_PatternInfo.PatternType) {
case ePatternType.ImageModelFinder:
case ePatternType.ShapeModelFinder:
offsetX = (baseResult as ModelFinderResult).ImagePosition.X;
offsetY = (baseResult as ModelFinderResult).ImagePosition.Y;
break;
case ePatternType.PatternMatching:
offsetX = (baseResult as PatternMatchingResult).ImagePosition.X;
offsetY = (baseResult as PatternMatchingResult).ImagePosition.Y;
break;
default:
Log.E("Pattern Type is not proper !!! ");
return null;
}
}
TranslateImageToField(offsetX, offsetY, out double _PatternOffsetX, out double _PatternOffsetY);
// 6.1.2. Offset 값의 Distance 및 Score 따른 보상 진행 여부 결정
double distance = Math.Sqrt(_PatternOffsetX * _PatternOffsetX + _PatternOffsetY * _PatternOffsetY);
// 260317 현장 작업으로 주석함
if (distance < _SequenceSetting.CellOffsetLimit /*|| isHigherLimitScore*/) {
_OffsetXfromModelFinder = _PatternOffsetX;
_OffsetYfromModelFinder = _PatternOffsetY;
_SuccessVisionResult = true;
Log.I(string.Format($"UseCellOffset Mode : {_SequenceSetting.UseCellOffset}; Can Compensate ; - PatternOffsetX(mm) : {_PatternOffsetX}, PatternOffsetY(mm) : {_PatternOffsetY}, Distance(mm) : {distance}, Vision Offset OK"));
}
else {
_OffsetXfromModelFinder = 0;
_OffsetYfromModelFinder = 0;
_SuccessVisionResult = false;
Log.I(string.Format($"UseCellOffset Mode : {_SequenceSetting.UseCellOffset}; Can't Compensate ; - PatternOffsetX(mm) : {_PatternOffsetX}, PatternOffsetY(mm) : {_PatternOffsetY}, Distance(mm) : {distance}, Vision Offset Fail"));
}
}
}
else {
_OffsetXfromModelFinder = 0;
_OffsetYfromModelFinder = 0;
Log.I(string.Format($"UseCellOffset Mode : {_SequenceSetting.UseCellOffset}; OR Not Found ;"));
}
visionParam = new SeqVisionData_Param {
originalImageID = temp1,
overlayImageID = temp2,
IsSuccess = !Status.GetProcessingWeldingData().IsVisionNG,
VisionData_Foler = _SequenceSetting.VisionFolderName,
CellId = Status.GetProcessingWeldingData().CellId,
};
// 7. 필요시 해당 구문 테스트 필요
//_CameraAgent.StartLive(_CameraDisplay);
return SequenceAction.Ok(visionParam);
}
private bool IsHigherLimitScore(double limitScore, ePatternType patternType)
{
double Score = -1;
#region 기존 코드. 이 후 코드가 가독성이 좋아보여서 수정. 필요 시, 사용할 것.
//foreach (BaseResult baseResult in _PatternResult.Results) {
// if (_PatternInfo.PatternType == ePatternType.ImageModelFinder || _PatternInfo.PatternType == ePatternType.ShapeModelFinder) {
// var result = baseResult as ModelFinderResult;
// Score = result.Score;
// } else if (_PatternInfo.PatternType == ePatternType.PatternMatching) {
// var result = baseResult as PatternMatchingResult;
// Score = result.Score;
// }
// break;
//}
#endregion
Score = GetPatternScore(patternType);
Log.I(string.Format($"Extract Score : {Score}; Limit Score : {limitScore}"));
if (Score.Equals(double.NaN)) {
Log.I(string.Format($"Fail Extract Score : Pattern Find Logic Mismatch"));
return false;
}
return Score > limitScore ? true : false;
}
private double GetPatternScore(ePatternType patternType)
{
BaseResult baseResult = _PatternResult?.Results?.FirstOrDefault();
if (baseResult != null) {
switch (patternType) {
case ePatternType.ImageModelFinder:
case ePatternType.ShapeModelFinder:
return (baseResult as ModelFinderResult).Score;
case ePatternType.PatternMatching:
return (baseResult as PatternMatchingResult).Score;
default:
Log.E("Pattern Type is not proper !!! ");
return double.NaN;
}
}
return double.NaN;
}
private SequenceActionResult SaveImagesEvent(SequenceContext context, string id, SequenceActionResult param)
{
if (visionParam is SeqVisionData_Param result && result != null) {
var currentDate = DateTime.Now;
string SequenceSettingFilePath = result.VisionData_Foler;
var FinalFilePath = Path.Combine(SequenceSettingFilePath, currentDate.ToString("yyyy"), currentDate.ToString("MM"), currentDate.ToString("dd"));
if (!Directory.Exists(FinalFilePath)) {
Directory.CreateDirectory(FinalFilePath);
}
string SuccessStatus = result.IsSuccess ? "OK" : "NG";
string CurrentTimeStamp = DateTime.Now.ToString("HHmmss");
string rawFileName = $"{FinalFilePath}\\Raw\\Page{_PageNo.ToString()}\\{SuccessStatus}\\{CurrentTimeStamp}_{result.CellId}.png";
string resultFileName = $"{FinalFilePath}\\Result\\Page{_PageNo.ToString()}\\{SuccessStatus}\\{CurrentTimeStamp}_{result.CellId}.png";
if (result.originalImageID != MIL.M_NULL) {
ImageUtil.SaveFieldScanImageJPG(result.originalImageID, rawFileName, 10);
MilEnvironment.FreeImage(result.originalImageID);
result.originalImageID = MIL.M_NULL;
Log.D($"Update Q Raw Image Complete;{rawFileName}");
}
else {
Log.E($"Update Q Raw Image Failed;{rawFileName};rawImage Is Null..");
}
if (result.overlayImageID != MIL.M_NULL) {
ImageUtil.SaveFieldScanImageJPG(result.overlayImageID, resultFileName, 10);
MilEnvironment.FreeImage(result.overlayImageID);
result.overlayImageID = MIL.M_NULL;
Log.D($"Update Q Overlay Image Complete;{resultFileName}");
}
else {
Log.E($"Update Q Overlay Image Failed;{resultFileName};resultImage Is Null");
}
}
else
Log.E("SeqVisionData_Param is null >> Image Save Fail");
return SequenceAction.Ok();
}
private string CheckSaveWeldImage(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (_SequenceSetting.UseWeldResultImage) {
return SEQ_SAVE_WELD_IMAGE;
}
else {
return SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ;
}
}
private SequenceActionResult SaveWeldImage(SequenceContext context, string id, SequenceActionResult param)
{
// WeldResult이미지는 EventQ 저장 버그가 발생하였는데 원인 파악중.
double ScannerPosX = 0, ScannerPosY = 0;
// 용접 이후에 용접 전에 찍은 이미지와 동일한 위치로 용접을 진행해야 함
// 객체 기반의 중앙 기반
FindObjectsCenter(out ScannerPosX, out ScannerPosY);
Log.I(string.Format($"Move Scanner - ScannerPosX : {ScannerPosX},ScannerPosY : {ScannerPosY} "));
_Scanner.JumpAbs(ScannerPosX, ScannerPosY);
// 이동까지 기다려주어야 함.
Thread.Sleep(20); // EL Welder에서 10ms로 설정 시 이동중에 이미지가 촬영되어 대기시간 증가
_CameraDisplay.ClearMeasureGraphics();
_CameraDisplay.ClearFindGraphics();
_CameraDisplay.ClearTrainGraphics();
object image = null;
_CameraAgent.StopLive();
_CameraAgent.GrabSync(_CameraDisplay, ref image);
if (_PatternResult?.Status == eFoundStatus.FoundOK) {
_PatternAgent.Draw(_PatternInfo, _PatternResult, _CameraDisplay);
}
MIL_ID temp1;
MIL_ID temp2;
try {
// 성공하든 아니든 이미지를 남겨보자.
temp1 = MilEnvironment.CloneImage((MIL_ID)image);
temp2 = MilEnvironment.GetOverlayImage((IMilCameraDisplay)_CameraDisplay);
}
catch (Exception ex) {
temp1 = MIL.M_NULL;
temp2 = MIL.M_NULL;
Log.FuncE($"Vision Draw Error Occured !!!!!!;{ex.Message}");
}
visionParam = new SeqVisionData_Param {
originalImageID = temp1,
overlayImageID = temp2,
IsSuccess = !Status.GetProcessingWeldingData().IsVisionNG,
VisionData_Foler = _SequenceSetting.WeldResultFolderName,
CellId = Status.GetProcessingWeldingData().CellId,
};
return SequenceAction.Ok(visionParam);
}
private object SendEvent(SequenceContext context, string id, SequenceActionResult param)
{
var seqParam = param.Param as SeqVisionData_Param;
return SequenceAction.Ok(seqParam);
}
private SequenceActionResult JoinVisionCompensation(SequenceContext context, string id, SequenceActionResult param)
{
return SequenceAction.Ok();
}
#endregion
private string CheckVisionComplete(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (_SequenceSetting.UseImageAcquire) {
try {
int delayTimeMsec = _SequenceSetting.ImageAcquireDelayTime;
Log.I($"Welding Before Delay : {delayTimeMsec}msec");
Thread.Sleep(delayTimeMsec);
} catch (Exception) {
}
}
var visionComplete = !Status.GetProcessingWeldingData().IsVisionNG && _SuccessVisionResult && _SequenceSetting.UseCellOffset;
var WeldingSkip = !visionComplete && _SequenceSetting.UseNgCellSkip;
if (WeldingSkip) {
Log.I($"Vision Compension Complete[{!Status.GetProcessingWeldingData().IsVisionNG}] / Vision Result[{_SuccessVisionResult}] / UseCellOffset[{_SequenceSetting.UseCellOffset}]");
return SEQ_NG_RESULT_PROCESS;
}
// 성공했거나, Vision Offset을 사용 안하면 다음 스텝으로
return SEQ_GENERATE_WELDING_OBJECT;
}
private SequenceActionResult NgResultProcess(SequenceContext context, string id, SequenceActionResult param)
{
// 이미 NG 처리는 했고, PLC에서 로직상 BUSY는 토글되야 한다고 해서 넣어줌
_PLC.WriteBit(eBitOutput.PC_WELD_NG_JUDGE, Status.GetProcessingWeldingData().IsVisionNG || !_SuccessVisionResult);
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, true);
Thread.Sleep(100);
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, false);
return SequenceAction.Ok();
}
#region 5. 용접 진행
private SequenceActionResult GenerateWeldingObject(SequenceContext context, string id, SequenceActionResult param)
{
_SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[_PageNo - 1].Objects;
return SequenceAction.Ok();
}
private SequenceActionResult SetComponentTeachingrun_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, true);
Log.I(string.Format("Weld Busy On ! Set ComponentTeachingrun Start."));
_WeldStartTime = DateTime.Now;
Status.GetProcessingWeldingData().WeldingStartTime = _WeldStartTime;
var currentModelInfo = EquipmentSpecificModelUtil.GetCurrentRecipeEquipmentSpecificModel();
if (currentModelInfo.equipmentSpecificModel == null) {
MessageUtil.ShowWarningToast(string.Format("선택된 model 이 없습니다.", "Laser On"));
return null;
}
var intervalTimeBlowOn = 0;
var intervalTimeBlowOff = 0;
if (currentModelInfo.equipmentSpecificModel.UseBlow) {
intervalTimeBlowOn = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOn;
intervalTimeBlowOff = currentModelInfo.equipmentSpecificModel.IntervalTimeBlowOff;
}
Log.I(string.Format("Set ComponentTeachingrun Immediately!"));
// Object를 만들어
SeqTeachingRun_InitParam seqParam = new SeqTeachingRun_InitParam() {
IsUseExternalObjects = true,
ExternalObjects = _SelectedDrawObjectList,
//UseStream = _EquipmentInfo.UseStream,
UseStream = currentModelInfo.equipmentSpecificModel.UseStream,
UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow,
IntervalTimeBlowOn = intervalTimeBlowOn,
IntervalTimeBlowOff = intervalTimeBlowOff,
NeedSetLaserTiming = true,
CellCenterOffsetX = _OffsetXfromModelFinder,
CellCenterOffsetY = _OffsetYfromModelFinder
};
Log.I(string.Format("Set ComponentTeachingrun Complete!"));
return SequenceAction.Ok(seqParam);
}
private SequenceActionResult GetComponentTeachingrun_SeqResult(SequenceContext context, string id, SequenceActionResult param)
{
var resultOutParam = param.Param as SeqTeachingRun_OutParam;
Log.I(string.Format("Get ComponentTeachingrun Complete!"));
if (resultOutParam.Success == false) {
Log.I(string.Format("SeqComponentTeachingrun Complete Error;Success Failed."));
return null;
}
else Log.I(string.Format("SeqComponentTeachingrun Complete Success;Welding Complete!"));
int bitRet = _PLC.WriteBit(eBitOutput.PC_WELD_BUSY, false);
Log.I("Weld Busy OFF!");
_WeldEndTime = DateTime.Now;
_WeldSpanTime = _WeldEndTime.Subtract(_WeldStartTime);
Log.I($"[TactTime] Page({_PageNo}) Total Welding Time : {_WeldSpanTime.TotalMilliseconds}msec");
return SequenceAction.Ok();
}
private SequenceActionResult GetPlcWeldCompleteInterface_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
var resultOutParam = param.Param as SeqExecutePlcBitInterface_OutParam;
if (resultOutParam.Success == false) {
Log.I(string.Format("SeqCheckWeldingComplete Error;Success Failed."));
return null;
}
else Log.I(string.Format("SeqCheckWeldingComplete Success;Welding Complete!"));
_WeldComTime = DateTime.Now;
Log.I($"***** 공정 시간 WeldPage No : {_PageNo} WeldRequest ~ WeldComplete : {_WeldComTime.Subtract(_WeldReqTime).TotalMilliseconds}ms");
_CurrentWeldingStep++;
if (_CurrentWeldingStep >= _TotalWeldingStep) {
Log.I(string.Format("Need to Update the Cell Info;{0}/{1}", _CurrentWeldingStep, _TotalWeldingStep));
_IsWeldingRetry = false;
_CurrentWeldingStep = 0;
Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL);
UpdateProductCount(context);
UpdateProductInfoForView();
}
else {
Log.I(string.Format("Update Welding Step;{0}/{1}", _CurrentWeldingStep, _TotalWeldingStep));
_IsWeldingRetry = true;
}
return SequenceAction.Ok();
}
private SequenceActionResult SetPlcWeldCompleteInterface_SeqParams(SequenceContext context, string id, SequenceActionResult param)
{
if (_SequenceSetting.UseImageAcquire) {
try {
int delayTimeMsec = _SequenceSetting.ImageAcquireDelayTime;
Log.I($"Welding After Delay : {delayTimeMsec}msec");
Thread.Sleep(delayTimeMsec);
} catch (Exception) {
}
}
Log.I(string.Format("SeqCheckWeldingComplete Set Parameters"));
SeqExecutePlcBitInterface_InitParam seqParams = new SeqExecutePlcBitInterface_InitParam() {
GetAddress = (eCommonBitInput)eBitInput.PLC_WELD_COMPLETE_ACK,
SetAddress = (eCommonBitOutput)eBitOutput.PC_WELD_COMPLETE,
TimeoutSec = 60,
FirstTimeoutSec = 60,
IsSetMode = true
};
return SequenceAction.Ok(seqParams);
}
#endregion
#region 6. 데이터 쓰기 및 종료
private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param)
{
Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT);
DataTable table = new DataTable("CellHistory");
table.Columns.Add("time", typeof(string));
table.Columns.Add("Cell Id", typeof(string));
table.Columns.Add("Pre Compensation X", typeof(double));
table.Columns.Add("Pre Compensation Y", typeof(double));
table.Columns.Add("Post Compensation X", typeof(double));
table.Columns.Add("Post Compensation Y", typeof(double));
table.Columns.Add("(Pre - Post) X", typeof(double));
table.Columns.Add("(Pre - Post) Y", typeof(double));
table.Columns.Add("(Pre - Post) RMS", typeof(double));
table.Columns.Add("Score", typeof(double));
var SystemSetting = context.Settings[CommonSettingIds.SystemParameter] as SystemParameter;
var DrawingObjectCenterX = TargetObjectCenterPosition.X;
var DrawingObjectCenterY = TargetObjectCenterPosition.Y;
var FindObjectCenterX = _OffsetXfromModelFinder;
var FindObjectCenterY = _OffsetYfromModelFinder;
var CompensionGapX = DrawingObjectCenterX - FindObjectCenterX;
var CompensionGapY = DrawingObjectCenterY - FindObjectCenterY;
table.Rows.Add(DateTime.Now.ToString("HH:mm:ss"),
Status.GetProcessingWeldingData().CellId,
DrawingObjectCenterX, DrawingObjectCenterY,
FindObjectCenterX, FindObjectCenterY,
CompensionGapX, CompensionGapY,
Math.Sqrt(CompensionGapX * CompensionGapX + CompensionGapY * CompensionGapY),
GetPatternScore(_PatternInfo.PatternType));
string today = DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
string safeName = $"CellInfo_{today}.csv";
CSV_Manager.GetInstance().Save_DataTableToCsv(SystemSetting.CellHistoryFolderPath, safeName, table);
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true);
return SequenceAction.Ok();
}
private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param)
{
RestoreDeviceStatus();
context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult);
SeqAutoRunPrismaticScanner_OutParam result = new SeqAutoRunPrismaticScanner_OutParam() { Success = seqResult };
if (seqResult == false) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_ResultFailed);
}
Status.SetWorkingPosition(eWorkingPosition.DONE);
return SequenceAction.Ok(result);
}
#endregion
#endregion
#region 구동 함수
private bool InitSequenceVariable(SequenceContext context)
{
// TODO: [장비구현필요] Initialize Variable 추가
FovInfo _fovInfo = VisionRecipeManager.GetFovInfos().FirstOrDefault();
_PatternAgent = FovPatternAgentManager.GetAgent(_fovInfo.ToSubSysInfo());
_Patterns = _PatternAgent.GetMainPatterns(_fovInfo);
_PatternInfo = _Patterns[_InitParam.VisionRecipeNo];
_CameraDisplay = CameraDisplayManager.GetDisplay(_fovInfo, eDisplayType.Machine_A);
_CameraAgent = CameraAgentManager.GetCameraAgent(_fovInfo);
_FieldScanParams = FieldScanParams.GetInstance();
_RetryCount = 0;
var mainPage = CommonHelper.GetMainPage();
_ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching;
// PLC에게 받는 PageNo는 1부터 시작 !
_PageNo = 1;
_CurrentWeldingStep = 0;
_PreviousTotalWeldingStep = 0;
_TotalWeldingStep = 0;
_OffsetXfromModelFinder = 0;
_OffsetYfromModelFinder = 0;
_IsWeldingRetry = false;
_FirstUpdateStep = true;
return true;
}
private void InitDev(SequenceContext context)
{
//Device Init
_Scanner = context.GetLaserScanner();
_Laser = context.GetLaser();
_PLC = context.GetPLC() as PLCControllerPrismaticScanner;
_Light = context.GetLight();
}
private bool CheckDeviceReady(SequenceContext context)
{
List<string> deviceListToCheck = new List<string>() { CommonDeviceIds.Scanner, CommonDeviceIds.Laser, CommonDeviceIds.PLCDevice };
bool isAllDeviceActivated = true;
foreach (string item in deviceListToCheck) {
if (!context.Devices[item].IsActivated) {
isAllDeviceActivated = isAllDeviceActivated && false;
}
}
return isAllDeviceActivated;
}
private bool CheckCondition(SequenceContext context)
{
if (CheckCondition_Interlock()) {
if (InitializeDeviceStatus(context)) {
return true;
}
}
return false;
}
private bool CheckCondition_Interlock()
{
// PLC, Data, IO 등 Interlock checking
// 비전 오프셋을 쓰길 원한다면, 첫 번째 Object가 Arc 혹은 Dot이어야 함.
if (_SequenceSetting.UseImageAcquire) {
foreach (ICanvasLayer layer in _ScannerCanvasUserCtrl.Model.Layers) {
List<IDrawObject> sourcelist = layer.Objects;
_UnpackDrawObject?.Clear();
_UnpackDrawObject = new List<IDrawObject>();
UnpackRecipes(sourcelist);
for (int i = 0; i < _UnpackDrawObject.Count; i++) {
if (!(_UnpackDrawObject[i] is Arc || _UnpackDrawObject[i] is Dot || _UnpackDrawObject[i] is Spline)) {
Log.E(string.Format("Object of Page{0} is not Arc & Dot. If you use a Image Acquire, check Page{0} objects", layer.No, layer.No));
return false;
}
}
}
// 패턴 갯수 관련 인터락 추가
int patternCount = _Patterns.Count();
int layerCount = _ScannerCanvasUserCtrl.Model.Layers.Count();
if (_SequenceSetting.UseSubPatternOnFailure) {
if (patternCount < layerCount * 2) {
Log.E($"Can't Use SubPattern. Because of Sizes. Pattern Count : {patternCount}; Layer Count :{layerCount}");
return false;
}
}
else {
if (patternCount < layerCount) {
Log.E($"Can't Use Pattern. Because of Sizes. Pattern Count : {patternCount}; Layer Count :{layerCount}");
return false;
}
}
}
return true;
}
private bool InitializeDeviceStatus(SequenceContext context)
{
return true;
}
private void TranslateImageToField(double imgPosX, double imgPosY, out double realPosX, out double realPosY)
{
var imageWidthChild = _FieldScanParams.GetimageSizeWidthChild();
var imageHeightChild = _FieldScanParams.GetimageSizeHeightChild();
double moveOffsetX = 0;
double moveOffsetY = 0;
var cameraROIWidth = ((imageWidthChild * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0;
var cameraROIHeight = ((imageHeightChild * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0;
moveOffsetX = ((imgPosX * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0 - cameraROIWidth * 0.5;
moveOffsetY = cameraROIHeight * 0.5 - ((imgPosY * _SystemParameter.CameraPixelSize) / _SystemParameter.LensMagnification) / 1000.0;
double angle = 0;
realPosX = moveOffsetX * Math.Cos(angle) + moveOffsetY * Math.Sin(angle);
realPosY = -moveOffsetX * Math.Sin(angle) + moveOffsetY * Math.Cos(angle);
}
private void FindObjectsCenter(out double x, out double y)
{
x = 0; y = 0;
List<IDrawObject> sourcelist = _ScannerCanvasUserCtrl.Model.Layers[_PageNo - 1].Objects;
_UnpackDrawObject?.Clear();
_UnpackDrawObject = new List<IDrawObject>();
UnpackRecipes(sourcelist);
List<UnitPoint> centers = new List<UnitPoint>();
for (int i = 0; i < _UnpackDrawObject.Count; i++) {
if (_UnpackDrawObject[i] is Arc) {
Arc tempArc = _UnpackDrawObject[i] as Arc;
centers.Add(tempArc.Center);
}
else if (_UnpackDrawObject[i] is Dot) {
Dot tempDot = _UnpackDrawObject[i] as Dot;
centers.Add(tempDot.P1);
}
else if (_UnpackDrawObject[i] is Spline spline) {
centers.Add(spline.Center);
}
}
x = centers.Count > 0 ? centers.Average(center => center.X) : 0;
y = centers.Count > 0 ? centers.Average(center => center.Y) : 0;
}
private static void UnpackRecipes(List<IDrawObject> recipes)
{
// Recipe를 재귀적으로 탐색해서 List로 재구성하는 함수
if (recipes == null)
return;
for (int i = 0; i < recipes.Count; i++) {
if (recipes[i] is CoreUnit.Control.UserControl.Canvas.Group) {
CoreUnit.Control.UserControl.Canvas.Group subGroup = recipes[i] as CoreUnit.Control.UserControl.Canvas.Group;
List<IDrawObject> subrecipes = subGroup.SubObjects as List<IDrawObject>;
UnpackRecipes(subrecipes);
}
else {
_UnpackDrawObject.Add(recipes[i]);
}
}
}
private void ChangeRecipes(short pageNo)
{
_PageNo = pageNo;
}
private bool RestoreDeviceStatus()
{
return true;
}
private void InitializePlcBits()
{
_PLC.WriteBit(eBitOutput.PC_WELD_READY_ACK, false);
_PLC.WriteBit(eBitOutput.PC_WELD_BUSY, false);
_PLC.WriteBit(eBitOutput.PC_WELD_COMPLETE, false);
_PLC.WriteBit(eBitOutput.PC_PAGE_CHANGE_COMPLETE, false);
_PLC.WriteBit(eBitOutput.PC_WELD_NG_JUDGE, false);
var UploadDatas = Status.GetProcessingWeldingData().UploadProcessDatas;
foreach (var data in UploadDatas)
data.Clear();
_PLC.UploadReportDatas(UploadDatas);
}
#endregion
private void OnForceError(SequenceContext arg1, string arg2)
{
OnSeqActionStop();
Log.I("SequenceAction Ok : OnForceError");
}
private void OnStopped(SequenceContext obj)
{
OnSeqActionStop();
Log.I("SequenceAction Ok : OnStopped");
}
private void OnSeqActionStop()
{
// 자동 운전 끝날 떄에는 꺼주기
if (_Laser.GetLaserReady()) _Laser.LaserOff();
Status.SetStatus(eEqpStatus.Manual);
_PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false);
InitializePlcBits();
}
private void UpdateProductCount(SequenceContext context)
{
// SystemParameter를 저장하기 전에는 반드시 업데이트를 해주어야 한다.
// SystemParameter 값과 동기화
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
_TodayProduct = _SystemParameter.TodayProductCount;
_TotalProduct = _SystemParameter.TotalProductCount;
if (_TodayProduct < uint.MaxValue) {
_TodayProduct++;
}
if (_TotalProduct < uint.MaxValue) {
_TotalProduct++;
}
_SystemParameter.TodayProductCount = _TodayProduct;
_SystemParameter.TotalProductCount = _TotalProduct;
_WeldEndTime = DateTime.Now;
_SystemParameter.LastProductTime = _WeldEndTime;
//Systemparameter Save
context.Settings.Save(CommonSettingIds.SystemParameter, _SystemParameter, "SeqAutoRunPrismaticScanner_New");
}
private void UpdateProductInfoForView()
{
Status.GetProcessingWeldingData().TotalProduct = _TotalProduct;
Status.GetProcessingWeldingData().TodayProduct = _TodayProduct;
Status.GetProcessingWeldingData().WeldingStartTime = _WeldStartTime;
Status.GetProcessingWeldingData().WeldingEndTime = _WeldEndTime;
Status.GetProcessingWeldingData().WeldingSpanTimeMilliseconds = _WeldSpanTime.TotalMilliseconds.ToString();
Status.GetProcessingWeldingData().LotId = _LotId;
Status.GetProcessingWeldingData().CellId = _CellId;
}
private void UpdateAutoLightValue(int value)
{
lightValues[0] = value;
lightValues[1] = value;
lightValues[2] = value;
lightValues[3] = value;
}
}
public class SeqAutoRunPrismaticScanner_InitParam : InitParam
{
public int VisionRecipeNo { get; set; } = 0;
}
public class SeqAutoRunPrismaticScanner_OutParam : OutParam
{
}
public class SeqVisionData_Param
{
public MIL_ID originalImageID;
public MIL_ID overlayImageID;
public bool IsSuccess;
public string VisionData_Foler;
public string CellId;
}
}