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 { /// /// 각형 스캐너 타입 자동 운전을 위한 시퀸스 /// 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 _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 _SelectedDrawObjectList; private static List _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 lightValues = new List { 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 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(CommonSettingIds.SystemParameter); _SequenceSetting = context.GetSetting(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 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 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 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 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 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 deviceListToCheck = new List() { 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 sourcelist = layer.Objects; _UnpackDrawObject?.Clear(); _UnpackDrawObject = new List(); 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 sourcelist = _ScannerCanvasUserCtrl.Model.Layers[_PageNo - 1].Objects; _UnpackDrawObject?.Clear(); _UnpackDrawObject = new List(); UnpackRecipes(sourcelist); List centers = new List(); 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 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 subrecipes = subGroup.SubObjects as List; 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(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; } }