using Aurora; using Aurora.Api; using Aurora.Api.Devices; using Aurora.Api.Manager; using Aurora.View; using AuroraACS; 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.Devices.ACS; 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 { /// /// 각형 스캐너 타입 자동 운전을 위한 시퀸스 /// public class SeqAutoRunPrismaticScanner { static readonly Logger Log = new Logger("SeqAutoRunPrismaticScanner"); const string SEQ_ENTER = "SEQ_ENTER"; // inputParam 으로 받자. const string SEQ_CHECK_INIT_CONDITION = "Check Init Condition"; // Check Init Condition const string SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ = "Set Sequence Param (UploadWeldingList Seq)"; const string SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ = "Process (UploadWeldingList Seq)"; const string SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ = "Get Sequence Result (UploadWeldingList Seq)"; const string SEQ_COMPLETE = "SEQ_COMPLETE"; // 완료 const string SEQ_START_WELDING = "Start All Welding"; const string SEQ_DO_COMPONENT_WELDING = "Do Component Welding"; const string SEQ_ACS_MOTION_SET = "Set ACS Motion Parameter"; const string SEQ_ACS_MOTION_START = "ACS Motion Start"; const string SEQ_CHECK_MOTION_END = "Check ACS Motion Complete"; #region 260408 NDY 노드 추가 // Builder()에 노드 추가 const string SEQ_STANDBY_MOTION = "Move Stage To Origin"; const string SEQ_STANDBY_SCANNER = "Encoder Reset"; #endregion const int timeoutSeconds = 10; 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"; } private SeqTeachingRun_InitParam _InitParam; private static Scanner _Scanner; // 필드 선언 추가 (다른 필드들 옆에) // 변경 전 //private ScannerACS _ACS; // 변경 후 private IScannerACS _ACS; // 26.04.06 NDY - MOTF용 ACS Stage X 제어 private static ACSMotor _AxisX, _AxisZ; // 26.04.08 NDY - MOTF용 Stage X 축 private SystemParameter _SystemParameter; private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언 private static SequenceSetting _SequenceSetting; private eListNo _WeldingScannerListNo; private List _SelectedObjectList; private List _TargetObjects; private static DateTime _SequenceCompleteTime; // 시퀸스 시간 private static TimeSpan _MeasurementSpanTime; //측정 시간 private static TimeSpan _ScannerSaveLogTime; private static DateTime _partialWeldingStartTime; public SequenceBuilder Builder() { var sb = new SequenceBuilder(1000, true); sb.AddAction(SEQ_ENTER, Sequence_Enter); sb.AddAction(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기 #region 26.04.08 추가된 노드 NDY // 26.04.08 필드 좌표 0,0으로 스테이지 이동 시퀸스 + EncoderReset 호출 sb.AddAction(SEQ_STANDBY_MOTION, SetStandbyMotion); sb.AddAction(SEQ_STANDBY_SCANNER, SetStandbyScanner); #endregion sb.AddAction(SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams); sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, CommonSequenceManager.GetSeqBuilder_SeqUploadWeldingList()); sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult); sb.AddAction(SEQ_DO_COMPONENT_WELDING, DoComponentWelding); sb.AddAction(SEQ_ACS_MOTION_SET, SetMotion); sb.AddAction(SEQ_ACS_MOTION_START, StartMotion); sb.AddAction(SEQ_CHECK_MOTION_END, EndWelding); sb.AddAction(SEQ_COMPLETE, Complete); sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_STANDBY_MOTION] > sb[SEQ_STANDBY_SCANNER] > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_COMPONENT_WELDING] > sb[SEQ_ACS_MOTION_SET] > sb[SEQ_ACS_MOTION_START] > sb[SEQ_CHECK_MOTION_END] > sb[SEQ_COMPLETE] > sb.Final); sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); //sb.Draw("SeqAutoRunPrismaticScanner.pdf"); return sb; } private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) { Log.I("===== SeqTeachingRun Sequence_Enter 진입 ====="); // 임시 추가 Log.I("SeqTeachingRun Start"); _InitParam = param.Param as SeqTeachingRun_InitParam; _SystemParameter = context.Settings[CommonSettingIds.SystemParameter] as SystemParameter; _SequenceSetting = context.Settings[CommonSettingIds.Sequence] as SequenceSetting; _Scanner = context.Devices[DeviceIds.Scanner] as Scanner; if (_Scanner == null) { MessageUtil.ShowErrorToast("Scanner not Ready", Log); return null; } _ACS = context.Devices[DeviceIds.ACS] as ScannerACS; if (_ACS == null) { MessageUtil.ShowErrorToast("ACS not Ready", Log); return null; } _AxisX = context.Devices[CommonDeviceIds.AxisX2] as ACSMotor; if (_AxisX == null) { MessageUtil.ShowErrorToast("AxisX not Ready", Log); return null; } _AxisZ = context.Devices[CommonDeviceIds.AxisZ] as ACSMotor; if (_AxisZ == null) { MessageUtil.ShowErrorToast("AxisZ not Ready", Log); return null; } //Check Device if (!CheckDeviceReady(context)) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_DeviceNotReady); Log.E("The devices are not active."); return null; } return SequenceAction.Ok(); } private bool CheckDeviceReady(SequenceContext context) { List deviceListToCheck = new List() { DeviceIds.ACS, CommonDeviceIds.Scanner, CommonDeviceIds.AxisX2, CommonDeviceIds.AxisZ }; //List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.IODevice,DeviceIds.ACS }; 변수 맵 수신 후 ACS 주석 해제 위에 주석 처리 bool isAllDeviceActivated = true; foreach (string item in deviceListToCheck) { if (!context.Devices[item].IsActivated) { isAllDeviceActivated = isAllDeviceActivated && false; } } return isAllDeviceActivated; } private SequenceActionResult CheckInitCondition(SequenceContext context, string id, SequenceActionResult param) { bool boolian = AlarmManager.HasAlarmLevelHigh(); if (boolian) { Log.E($"Clear Heavy Alarms For Welding!"); MessageUtil.ShowWarningToast(string.Format("Clear Heavy Alarms For Welding!")); return null; } _ScannerSaveLogTime = TimeSpan.Zero; var startTime = DateTime.Now; context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime); Log.D("ComponentTeachingRun Sequence CheckInitCondition"); return SequenceAction.Ok(); } #region 26.04.08 NDY private SequenceActionResult SetStandbyMotion(SequenceContext context, string id, SequenceActionResult param) { if (_AxisX == null) { Log.W("[MoveStageToOrigin] AxisX2 null - 스킵"); return SequenceAction.Ok(); } Log.I("[MoveStageToOrigin] Stage X → 0 이동 시작"); // (스캐너의 0,0 && 용접 대상의 시작 위치) >> X축을 이동 _AxisX.SetTargetPos(0, TargetPosType.ABS); _AxisX.SetMoveVelocityImm(AuroraACS.Speed.M); _AxisX.AccelerationImm(AuroraACS.Speed.M); _AxisX.DecelerationImm(AuroraACS.Speed.M); _AxisX.JerkImm(AuroraACS.Speed.M); _AxisX.Move(); // Laser FocusPosition Move _AxisZ.SetTargetPos(0, TargetPosType.ABS); _AxisZ.SetMoveVelocityImm(AuroraACS.Speed.M); _AxisZ.AccelerationImm(AuroraACS.Speed.M); _AxisZ.DecelerationImm(AuroraACS.Speed.M); _AxisZ.JerkImm(AuroraACS.Speed.M); _AxisZ.Move(); var res = context.RetryUntilSuccess( () => _AxisX.IsReachedTargetPosition(0) && _AxisZ.IsReachedTargetPosition(0), 30 * 1000, 20, true); if (res == E2.TIMEOUT) { Log.E("[MoveStageToOrigin] TIMEOUT"); return null; } else if (res == E2.CANCELLED) { Log.E("[MoveStageToOrigin] CANCELLED"); return null; } Log.I("[MoveStageToOrigin] Stage X = 0 도달 완료"); return SequenceAction.Ok(); } private SequenceActionResult SetStandbyScanner(SequenceContext context, string id, SequenceActionResult param) { // Scanner Default Setting var mainPage = CommonHelper.GetMainPage(); _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching; _WeldingScannerListNo = eListNo.LIST1; _SelectedObjectList = FindObjects(); //Vision Correction File 선택 _Scanner.SelectCorrectionTable(2); if (!_Scanner.Config.UseMarkingOnTheFly) { Log.W("[EncoderReset] MOTF 비활성화 - 스킵"); return SequenceAction.Ok(); } Log.I("[EncoderReset] 시작"); // SeqUploadWeldingList.MarkingOnTheFlyOn 패턴 _Scanner.SetFlyX(_Scanner.Config.MarkingOnTheFlyEncoderResolutionX); Log.I($"[EncoderReset] SetFlyX: {_Scanner.Config.MarkingOnTheFlyEncoderResolutionX}"); _Scanner.WaitForEncoderX(_Scanner.Config.MarkingStartEncoderPositionX); Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.MarkingStartEncoderPositionX}"); _Scanner.StoreEncoder(0); _Scanner.SaveAndRestartTimer(); Log.I("[EncoderReset] 완료 - Stage(0) = Scanner(0) 동기화"); return SequenceAction.Ok(); } //SeqTeachingPosMoveStageA패턴 참고 - 기존 GUN 프로젝트 시퀸스 private List FindObjects() { if (_InitParam.IsUseExternalObjects) { return _InitParam.ExternalObjects; } else if (_InitParam.IsObjectSelectAll) { return _ScannerCanvasUserCtrl.Model.ActiveLayer.Objects; } else { return _ScannerCanvasUserCtrl.Canvas.Model.SelectedObjects; } } #endregion private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param) { Status.SetWorkingPosition(eWorkingPosition.UPLOADING_WELD_LIST); // TeachingRun에는 용접 시퀀스만 정의해서 Auto Run에서도 사용하므로 Offset 넣어줘야 함 SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() { NeedSetLaserTiming = _InitParam.NeedSetLaserTiming, IncludeListStartEnd = true, CellMeasureCenterOffsetX = _SystemParameter.CellPosMeasureCenterOffsetX + _InitParam.CellCenterOffsetX, CellMeasureCenterOffsetY = _SystemParameter.CellPosMeasureCenterOffsetY + _InitParam.CellCenterOffsetY, SelectedDrawObjectList = _SelectedObjectList, WeldingScannerListNo = _WeldingScannerListNo, }; Log.D(string.Format($"[ComponentTeachingRun] Set UploadWelindList Seq Param")); return SequenceAction.Ok(seqParams); } private SequenceActionResult GetUploadWeldingList_SeqResult(SequenceContext context, string id, SequenceActionResult param) { Log.D(string.Format($"[ComponentTeachingRun] Get UploadWelindList Seq Param")); var resultOutParam = param.Param as SeqUploadWeldingList_OutParam; if (resultOutParam.Success == false) { Log.E(string.Format($"[ComponentTeachingRun] Failed UploadWeldingList Sequence")); return null; } return SequenceAction.Ok(); } private SequenceActionResult DoComponentWelding(SequenceContext context, string id, SequenceActionResult param) { Log.I("ExecuteList Start"); Log.I($"Welding List Space : {_Scanner.GetListSpace()}"); DateTime weldingTime = DateTime.Now; _Scanner.ExecuteList(_WeldingScannerListNo); // ── 26.04.06 NDY 추가: ACS Stage X 이동 ────────────── // MOTF: Scanner List 실행과 동시에 Stage X 이동 시작 // Stage X 엔코더가 트리거 → WaitForEncoderX 조건 도달 시 갈바노 마킹 // TODO: 변수 맵 수신 후 _ACS.MOTFStart()로 교체 // _ACS?.MOTFStart(); // ──────────────────────────────────────────────────── // ── 26.04.06 NDY 추가: ACS 완료 대기 ───────────────── // TODO: 변수 맵 수신 후 활성화 // while (!_ACS.CheckMOTFComp()) { Thread.Sleep(10); } // ──────────────────────────────────────────────────── if (_Scanner.Config.UseRecordSignal) { DateTime startTime = DateTime.Now; _Scanner.StackRecord(); _ScannerSaveLogTime += DateTime.Now.Subtract(startTime); } // excel 파일 작성 SaveScannerRecordFileForamt(); Log.D(string.Format($"[ComponentTeachingRun] SaveScannerRecordFileForamt Time : {_ScannerSaveLogTime.TotalMilliseconds}")); var PartialTactTime = DateTime.Now.Subtract(_partialWeldingStartTime); Log.D(string.Format($"[TactTime] Complete Partial Component Weld : {PartialTactTime.TotalMilliseconds}")); _ScannerCanvasUserCtrl.TimeLabelUpdate(_MeasurementSpanTime.TotalMilliseconds, PartialTactTime.TotalMilliseconds); return SequenceAction.Ok(); } private SequenceActionResult SetMotion(SequenceContext context, string id, SequenceActionResult param) { // 메뉴얼 화면에서 셋팅이 필요해보임 _ACS.SetMOTFParams(0, 0, 0, 0, 0); return SequenceAction.Ok(); } private SequenceActionResult StartMotion(SequenceContext context, string id, SequenceActionResult param) { _ACS.MOTFStart(); // 모션 동작시키고 바로 확인하면 그냥 빠져나올 것 같음 var res = context.Wait(1000, 100); if (res == E2.TIMEOUT) ; else if (res == E2.CANCELLED) { Log.E("[MOTF] Motion CANCELLED"); return null; } res = context.RetryUntilSuccess(() => _ACS.CheckMOTFComp(), 1000 * 20, 100, true); if (res == E2.TIMEOUT) { Log.E("[MOTF] Motion TIMEOUT"); return null; } else if (res == E2.CANCELLED) { Log.E("[MOTF] Motion CANCELLED"); return null; } return SequenceAction.Ok(); } private SequenceActionResult EndWelding(SequenceContext context, string id, SequenceActionResult param) { _Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut); Log.I("ExecuteList Complete"); _MeasurementSpanTime = DateTime.Now.Subtract(_SequenceCompleteTime); Log.D(string.Format($"[TactTime] Complete All Component Weld : {_MeasurementSpanTime.TotalMilliseconds}")); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); var mesurementTime = DateTime.Now; //용접 완료 후 CorrectionFile Vision으로 원복 _Scanner.SelectCorrectionTable(1); _MeasurementSpanTime = DateTime.Now.Subtract(mesurementTime); Log.D(string.Format($"[ComponentTeachingRun] Change SelectCorrectionTable Time : {_MeasurementSpanTime.TotalMilliseconds}")); return SequenceAction.Ok(); } private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param) { context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult); SeqTeachingRun_OutParam result = new SeqTeachingRun_OutParam() { Success = seqResult }; if (seqResult == false) { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_ResultFailed); } Log.D("ComponentTeachingRun Sequence Complete"); return SequenceAction.Ok(result); } private void SaveScannerRecordFileForamt() { if (_Scanner.Config.UseRecordSignal) { DateTime startTime = DateTime.Now; _Scanner.SaveStackRecord("Normal"); _ScannerSaveLogTime += DateTime.Now.Subtract(startTime); } } private void OnForceError(SequenceContext context, string arg2) { // Scanner Log 저장 SaveScannerRecordFileForamt(); //List 실행 종료 _Scanner.StopExecution(); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND); //Scanner 원점 복귀 // 복귀 시 Scanner Busy 상태일 수 있으므로 진행중인 작업이 끝날 때까지 대기 Thread.Sleep(10); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); Thread.Sleep(10); Log.I("SequenceAction Ok : OnForceError"); } private void OnStopped(SequenceContext context) { // Scanner Log 저장 SaveScannerRecordFileForamt(); //List 실행 종료 _Scanner.StopExecution(); //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND); //Scanner 원점 복귀 // 복귀 시 Scanner Busy 상태일 수 있으므로 진행중인 작업이 끝날 때까지 대기 Thread.Sleep(10); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); Thread.Sleep(10); Log.I("SequenceAction Ok : OnStopped"); } } // TODO: UseStream 으로 TeachingRun 을 할 필요가 있을까?? 확인 필요 public class SeqAutoRunPrismaticScanner_InitParam : InitParam { public bool IsObjectSelectAll { get; set; } // All == true, Selected Object == false public bool UseStream { get; set; } public bool UseBlow { get; set; } public int IntervalTimeBlowOn { get; set; } public int IntervalTimeBlowOff { get; set; } public bool NeedSetLaserTiming { get; set; } // For SubSequence, SeqUploadWeldingList } public class SeqAutoRunPrismaticScanner_OutParam : OutParam { } }