From 7dd4648587a3a3f88810d0ce2259edd92551eb7f Mon Sep 17 00:00:00 2001 From: RnD_Welder Date: Fri, 17 Apr 2026 17:07:21 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20=ED=98=84=EC=9E=A5=20=EC=B5=9C?= =?UTF-8?q?=EC=8B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sequences/SeqAutoRunPrismaticScanner.cs | 447 ++++++++++-------- .../Views/Main/UCMainAutoRunViewModel.cs | 19 +- 2 files changed, 255 insertions(+), 211 deletions(-) diff --git a/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScanner.cs b/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScanner.cs index 45b264b..4d9cd36 100644 --- a/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScanner.cs +++ b/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScanner.cs @@ -18,6 +18,7 @@ using CoreUnit.Scanner.UserControl.ScannerCanvas; using DB; using DB.Recipe; using Hermes; +using Hermes.Functional; using Hermes.Functional.Option; using ILSOS_PrismaticScanner.Device; using ILSOS_PrismaticScanner.Models; @@ -39,6 +40,7 @@ using System.Globalization; using System.IO; using System.IO.Pipes; using System.Linq; +using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Runtime.Remoting.Contexts; using System.Text; @@ -72,31 +74,27 @@ using static WeldingCommon.Sequences.Main.SeqExecutePlcBitInterface_InitParam; namespace ILSOS_PrismaticScanner.Sequences { /// - /// 각형 스캐너 타입 자동 운전을 위한 시퀸스 + /// Auto run sequence for prismatic scanner type /// 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_ENTER = "SEQ_ENTER"; + const string SEQ_CHECK_INIT_CONDITION = "Check Init Condition"; const string SEQ_SET_BEFORE_UPLOAD = "Set Before Upload"; 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_CHECK_AFTER_UPLOAD = "Check After Upload"; - - const string SEQ_COMPLETE = "SEQ_COMPLETE"; // 완료 - + 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()에 노드 추가 + #region 260408 NDY nodes added const string SEQ_STANDBY_MOTION = "Move Stage To Origin"; const string SEQ_STANDBY_SCANNER = "Encoder Reset"; #endregion @@ -108,51 +106,37 @@ namespace ILSOS_PrismaticScanner.Sequences public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqAutoRunPrismaticScanner"; } + // 기존 private SeqTeachingRun_InitParam _InitParam; - + // 변경 + //private SeqAutoRunPrismaticScanner_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 IScannerACS _ACS; + private static ACSMotor _AxisX, _AxisZ; private SystemParameter _SystemParameter; - private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언 - + 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 DateTime _SequenceCompleteTime; + private static TimeSpan _MeasurementSpanTime; private static TimeSpan _ScannerSaveLogTime; private static DateTime _partialWeldingStartTime; - private int _PageNum; - private double lastX, lastY; // 저장용 값 - private double _LastX, _LastY; // 진짜로 UploadWeldingList에 들어갈 값 + private double lastX, lastY; + private double _LastX, _LastY; 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 필드 좌표 x 0 y 22.8(자재 기준)으로 스테이지 이동 시퀸스 + sb.AddAction(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); + #region 26.04.08 NDY nodes added sb.AddAction(SEQ_STANDBY_MOTION, SetStandbyMotion); - // 26 04 17 스테이지 x =0 에서 스캐너 엔코더 0 상태에서 기록된 듯 싶습니다. - sb.AddAction(SEQ_STANDBY_SCANNER, SetStandbyScanner); + sb.AddAction(SEQ_STANDBY_SCANNER, SetStandbyScanner); #endregion - sb.AddAction(SEQ_SET_BEFORE_UPLOAD, SetBeforeUpload); sb.AddAction(SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams); sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, CommonSequenceManager.GetSeqBuilder_SeqUploadWeldingList()); @@ -164,7 +148,6 @@ namespace ILSOS_PrismaticScanner.Sequences sb.AddAction(SEQ_CHECK_MOTION_END, EndWelding); sb.AddAction(SEQ_COMPLETE, Complete); - sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] @@ -188,16 +171,14 @@ namespace ILSOS_PrismaticScanner.Sequences sb.OnStopped(OnStopped); sb.OnForceError(OnForceError); - //sb.Draw("SeqAutoRunPrismaticScanner.pdf"); - return sb; } - + // ==================== SEQ_ENTER ==================== private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) { - Log.I("===== SeqTeachingRun Sequence_Enter 진입 ====="); // 임시 추가 + Log.I("===== [SEQ_ENTER] Sequence entry ====="); Log.I("SeqTeachingRun Start"); _InitParam = param.Param as SeqTeachingRun_InitParam; @@ -205,50 +186,50 @@ namespace ILSOS_PrismaticScanner.Sequences _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; } + if (_Scanner == null) { Log.E("[SEQ_ENTER] Scanner is 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; } + if (_ACS == null) { Log.E("[SEQ_ENTER] ACS is 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; } + if (_AxisX == null) { Log.E("[SEQ_ENTER] AxisX2 is 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; } + if (_AxisZ == null) { Log.E("[SEQ_ENTER] AxisZ is 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."); + Log.E("[SEQ_ENTER] Device not ready -> exit"); return null; } + Log.I("[SEQ_ENTER] Complete -> CheckInitCondition"); 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; - } + bool activated = context.Devices[item].IsActivated; + Log.D($"[CheckDeviceReady] {item} -> IsActivated={activated}"); + if (!activated) isAllDeviceActivated = false; } - return isAllDeviceActivated; } + // ==================== SEQ_CHECK_INIT_CONDITION ==================== private SequenceActionResult CheckInitCondition(SequenceContext context, string id, SequenceActionResult param) { - bool boolian = AlarmManager.HasAlarmLevelHigh(); - if (boolian) + bool hasAlarm = AlarmManager.HasAlarmLevelHigh(); + Log.D($"[CheckInitCondition] HasAlarmLevelHigh={hasAlarm}"); + if (hasAlarm) { - Log.E($"Clear Heavy Alarms For Welding!"); - MessageUtil.ShowWarningToast(string.Format("Clear Heavy Alarms For Welding!")); + Log.E("[CheckInitCondition] High alarm exists -> exit"); + MessageUtil.ShowWarningToast("Clear Heavy Alarms For Welding!"); return null; } @@ -257,159 +238,191 @@ namespace ILSOS_PrismaticScanner.Sequences var startTime = DateTime.Now; context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime); + Log.I($"[CheckInitCondition] Initial _PageNum={_PageNum}, StartTime={startTime:HH:mm:ss.fff}"); Log.D("ComponentTeachingRun Sequence CheckInitCondition"); return SequenceAction.Ok(); } + // ==================== SEQ_STANDBY_MOTION ==================== #region 26.04.08 NDY private SequenceActionResult SetStandbyMotion(SequenceContext context, string id, SequenceActionResult param) { if (_AxisX == null) { - Log.W("[MoveStageToOrigin] AxisX2 null - 스킵"); + Log.W("[MoveStageToOrigin] AxisX2 null - skip"); return SequenceAction.Ok(); } - Log.I("[MoveStageToOrigin] Stage X → 0 이동 시작"); + Log.I("[MoveStageToOrigin] Stage X -> 310 move start"); - // (스캐너의 0,0 && 용접 대상의 시작 위치) >> X축을 이동 - _AxisX.SetTargetPos(0, TargetPosType.ABS); + // 26.04.17 NDY - start from 310 + _AxisX.SetTargetPos(310, TargetPosType.ABS); _AxisX.SetMoveVelocityImm(AuroraACS.Speed.M); _AxisX.AccelerationImm(AuroraACS.Speed.M); _AxisX.DecelerationImm(AuroraACS.Speed.M); _AxisX.JerkImm(AuroraACS.Speed.M); _AxisX.Move(); + Log.D("[MoveStageToOrigin] AxisX.Move() called (target=310)"); - // Laser FocusPosition Move 22.8 <- 세팅 Z 축 자재 위치 + // Laser FocusPosition Move 22.8 <- Z axis material position _AxisZ.SetTargetPos(22.8, TargetPosType.ABS); _AxisZ.SetMoveVelocityImm(AuroraACS.Speed.M); _AxisZ.AccelerationImm(AuroraACS.Speed.M); _AxisZ.DecelerationImm(AuroraACS.Speed.M); _AxisZ.JerkImm(AuroraACS.Speed.M); _AxisZ.Move(); + Log.D("[MoveStageToOrigin] AxisZ.Move() called (target=22.8mm)"); //var res = context.RetryUntilSuccess( // () => _AxisX.IsReachedTargetPosition(0) && _AxisZ.IsReachedTargetPosition(0), // 30 * 1000, 20, true); + //var res = context.RetryUntilSuccess( + // () => _AxisX.IsReachedTargetPosition(0), + // 30 * 1000, 20, true); var res = context.RetryUntilSuccess( - () => _AxisX.IsReachedTargetPosition(0), // Z축 조건 제거 - 30 * 1000, 20, true); + () => _AxisX.IsReachedTargetPosition(310), + 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 도달 완료"); + Log.I("[MoveStageToOrigin] Stage X = 310 reached"); return SequenceAction.Ok(); - } + + // ==================== SEQ_STANDBY_SCANNER ==================== private SequenceActionResult SetStandbyScanner(SequenceContext context, string id, SequenceActionResult param) { - // Scanner Default Setting var mainPage = CommonHelper.GetMainPage(); _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching; _WeldingScannerListNo = eListNo.LIST1; - //_SelectedObjectList = FindObjects(); // 중복 주석 처리 260417 - //Vision Correction File 선택 + // _SelectedObjectList = FindObjects(); // removed duplicate 260417 _Scanner.SelectCorrectionTable(2); + Log.D("[EncoderReset] SelectCorrectionTable(2) done"); if (!_Scanner.Config.UseMarkingOnTheFly) { - Log.W("[EncoderReset] MOTF 비활성화 - 스킵"); + Log.W("[EncoderReset] MOTF disabled - skip"); return SequenceAction.Ok(); } - Log.I("[EncoderReset] 시작"); - // SeqUploadWeldingList.MarkingOnTheFlyOn 패턴 + Log.I("[EncoderReset] Start"); _Scanner.SetStartList(_WeldingScannerListNo); + Log.D($"[EncoderReset] SetStartList({_WeldingScannerListNo}) done"); + _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.WaitForEncoderX(_Scanner.Config.MarkingStartEncoderPositionX); + //Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.MarkingStartEncoderPositionX}"); - // 스캐너 기준 엔코더 리셋이 스테이지 x = 0에서 이뤄졌음. 260417 + // 26.04.17 NDY - encoder reset at Stage X = 310 _Scanner.StoreEncoder(0); _Scanner.SaveAndRestartTimer(); - - Log.I("[EncoderReset] 완료 - Stage(0) = Scanner 엔코더(0) 동기화"); + Log.I("[EncoderReset] Complete - Stage(310) = Scanner Encoder(0) synchronized"); 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 - // 260417 NDY 먼저 업로드 되는 것 - 페이지 2번 4번 ?? 1번 4번은 어떻게 되는지 궁금합니다. + + // ==================== SEQ_SET_BEFORE_UPLOAD ==================== private SequenceActionResult SetBeforeUpload(SequenceContext context, string id, SequenceActionResult param) { + Log.I($"===== [SetBeforeUpload] Entry PageNum={_PageNum} ====="); + var mainPage = CommonHelper.GetMainPage(); _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching; - _SelectedObjectList = _ScannerCanvasUserCtrl.Model.Layers[_PageNum-1].Objects; + + int layerIdx = _PageNum - 1; + Log.D($"[SetBeforeUpload] LayerIndex={layerIdx}, TotalLayers={_ScannerCanvasUserCtrl.Model.Layers.Count()}"); + + _SelectedObjectList = _ScannerCanvasUserCtrl.Model.Layers[_PageNum - 1].Objects; + Log.D($"[SetBeforeUpload] PageNum={_PageNum} ObjectCount={_SelectedObjectList?.Count ?? 0}"); + _LastX = lastX; _LastY = lastY; - if (_PageNum == 2 || _PageNum == 4) { + Log.D($"[SetBeforeUpload] Prev LastX={_LastX:F4}, LastY={_LastY:F4}"); + + // ScannerConfig dump + Log.I("---- [SetBeforeUpload] ScannerConfig values ----"); + Log.I($" MarkingStartX_1 = {_Scanner.Config.MarkingStartX_1} (forward start)"); + Log.I($" MarkingStartX_2 = {_Scanner.Config.MarkingStartX_2} (reverse start)"); + Log.I($" XDiff_Page1and2 = {_Scanner.Config.XDiff_Page1and2} (360, Page1/2 boundary)"); + Log.I($" XDiff_Page3and4 = {_Scanner.Config.XDiff_Page3and4} (590, Page3/4 boundary)"); + Log.I($" EncoderResolutionX = {_Scanner.Config.MarkingOnTheFlyEncoderResolutionX}"); + Log.I("------------------------------------------------"); + + if (_PageNum == 2 || _PageNum == 4) + { if (_PageNum == 2) - { // NDY 26 04 17 0 아니고 2 인 것 같습니다. MarkingStartX_1 = 현재 310 (출발위치) + { + // 기존 코드 유지 (if _PageNum==0 -> if _PageNum==2 수정) + Log.I($"[SetBeforeUpload] PageNum=2 -> WaitForEncoderX({_Scanner.Config.MarkingStartX_1:F4})"); + Log.D($" MarkingStartX_1={_Scanner.Config.MarkingStartX_1} / XDiff_Page1and2={_Scanner.Config.XDiff_Page1and2}"); _Scanner.WaitForEncoderX(_Scanner.Config.MarkingStartX_1); Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.MarkingStartX_1}"); - // NDY 26 04 17아래가 반영해야 할 용접 시작위치라고 생각하는 구간 엔코더 리셋이 사실 용접 시작인 360 에서 이뤄져야하는 것 같은데 위 변수 값 맞는지 확인 - //_Scanner.WaitForEncoderX(_Scanner.Config.XDiff_Page1and2); // XDiff_Page1and2 = 360 + // 26.04.17 NDY - if need 360 boundary, replace with below + //_Scanner.WaitForEncoderX(_Scanner.Config.XDiff_Page1and2); //Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.XDiff_Page1and2}"); - - // 페이지 1,2 가 360에서 가공되어야 할 것 } - else // pageNum 4 - { // MarkingStartX_2 = 현재 640 (출발 위치) 돌아오는 , 해당 리셋 위치가 원래는 용접 위치인 590 인 Diff에서 가져와야 하는 건 아닌지 의문 + else // PageNum == 4 + { + // 기존 코드 유지 + Log.I($"[SetBeforeUpload] PageNum=4 -> WaitForEncoderX({_Scanner.Config.MarkingStartX_2:F4})"); + Log.D($" MarkingStartX_2={_Scanner.Config.MarkingStartX_2} / XDiff_Page3and4={_Scanner.Config.XDiff_Page3and4}"); _Scanner.WaitForEncoderX(_Scanner.Config.MarkingStartX_2); Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.MarkingStartX_2}"); - // NDY 26 04 17아래가 반영해야 할 용접 시작위치라고 생각하는 구간 - //_Scanner.WaitForEncoderX(_Scanner.Config.XDiff_Page3and4); // XDiff_Page3and4 = 590 + // 26.04.17 NDY - if need 590 boundary, replace with below + //_Scanner.WaitForEncoderX(_Scanner.Config.XDiff_Page3and4); //Log.I($"[EncoderReset] WaitForEncoderX: {_Scanner.Config.XDiff_Page3and4}"); - - // 페이지 3,4 가 590에서 가공되어야 할 것 - } - foreach (IDrawObject obj in _SelectedObjectList) { - if (obj is Line) { + + int lineCount = 0; + foreach (IDrawObject obj in _SelectedObjectList) + { + if (obj is Line) + { lastX = obj.PF2.X; lastY = obj.PF2.Y; + lineCount++; } } + Log.D($"[SetBeforeUpload] Line objects={lineCount}, lastX={lastX:F4}, lastY={lastY:F4}"); + } + else + { + Log.D($"[SetBeforeUpload] PageNum={_PageNum} -> WaitForEncoderX skip"); } - - Log.D(string.Format($"[SetBeforeUpload] Set Before UploadWeldingList")); + Log.I($"[SetBeforeUpload] Complete -> SetUploadWeldingList_SeqParams (PageNum={_PageNum})"); + Log.D($"[SetBeforeUpload] Set Before UploadWeldingList"); return SequenceAction.Ok(); } + + // ==================== SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ ==================== private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param) { Status.SetWorkingPosition(eWorkingPosition.UPLOADING_WELD_LIST); bool _isUseInitParam = _InitParam == null ? false : true; - // TeachingRun에는 용접 시퀀스만 정의해서 Auto Run에서도 사용하므로 Offset 넣어줘야 함 SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() { NeedSetLaserTiming = _isUseInitParam ? _InitParam.NeedSetLaserTiming : true, IncludeListStartEnd = false, - CellMeasureCenterOffsetX = _SystemParameter.CellPosMeasureCenterOffsetX + (_isUseInitParam ? _InitParam.CellCenterOffsetX :0), + CellMeasureCenterOffsetX = _SystemParameter.CellPosMeasureCenterOffsetX + (_isUseInitParam ? _InitParam.CellCenterOffsetX : 0), CellMeasureCenterOffsetY = _SystemParameter.CellPosMeasureCenterOffsetY + (_isUseInitParam ? _InitParam.CellCenterOffsetY : 0), SelectedDrawObjectList = _SelectedObjectList, WeldingScannerListNo = _WeldingScannerListNo, @@ -418,55 +431,64 @@ namespace ILSOS_PrismaticScanner.Sequences LastY = _LastY }; - Log.D(string.Format($"[ComponentTeachingRun] Set UploadWelindList Seq Param")); + Log.D($"[ComponentTeachingRun] Set UploadWeldingList Seq Param"); return SequenceAction.Ok(seqParams); } + // ==================== SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ ==================== private SequenceActionResult GetUploadWeldingList_SeqResult(SequenceContext context, string id, SequenceActionResult param) { - Log.D(string.Format($"[ComponentTeachingRun] Get UploadWelindList Seq Param")); + Log.D($"[ComponentTeachingRun] Get UploadWeldingList Seq Param"); var resultOutParam = param.Param as SeqUploadWeldingList_OutParam; if (resultOutParam.Success == false) { - Log.E(string.Format($"[ComponentTeachingRun] Failed UploadWeldingList Sequence")); + Log.E($"[ComponentTeachingRun] Failed UploadWeldingList Sequence"); return null; } return SequenceAction.Ok(); } + // ==================== SEQ_CHECK_AFTER_UPLOAD ==================== private string CheckAfterUpload(SequenceContext context, string id, ICollection branches, SequenceActionResult param) { _PageNum += 1; if (_PageNum == 5) _PageNum = 1; - if (_PageNum == 2) return SEQ_DO_COMPONENT_WELDING; - else return SEQ_SET_BEFORE_UPLOAD; + Log.I($"[CheckAfterUpload] PageNum after increment={_PageNum}"); + + if (_PageNum == 2) + { + Log.I("[CheckAfterUpload] PageNum==2 -> branch to DoComponentWelding"); + return SEQ_DO_COMPONENT_WELDING; + } + else + { + Log.I($"[CheckAfterUpload] PageNum={_PageNum} -> branch to SetBeforeUpload (loop)"); + return SEQ_SET_BEFORE_UPLOAD; + } } + // ==================== SEQ_DO_COMPONENT_WELDING ==================== private SequenceActionResult DoComponentWelding(SequenceContext context, string id, SequenceActionResult param) { + // Close list buffer before execute - subsequence has no SetStartList/SetEndList + //Log.I($"[DoComponentWelding] ListSpace (before SetEndList)={_Scanner.GetListSpace()}"); + //_Scanner.SetEndList(); + Log.I($"[DoComponentWelding] ListSpace (after SetEndList)={_Scanner.GetListSpace()}"); + Log.I("ExecuteList Start"); Log.I($"Welding List Space : {_Scanner.GetListSpace()}"); DateTime weldingTime = DateTime.Now; _Scanner.ExecuteList(_WeldingScannerListNo); + Log.I($"[DoComponentWelding] ExecuteList called at {weldingTime:HH:mm:ss.fff}"); - - // ── 26.04.06 NDY 추가: ACS Stage X 이동 ────────────── - // MOTF: Scanner List 실행과 동시에 Stage X 이동 시작 - // Stage X 엔코더가 트리거 → WaitForEncoderX 조건 도달 시 갈바노 마킹 - // TODO: 변수 맵 수신 후 _ACS.MOTFStart()로 교체 + // 26.04.06 NDY - ACS Stage X move (simultaneous with scanner list) + // MOTF: Stage X encoder triggers WaitForEncoderX -> galvano marking // _ACS?.MOTFStart(); - // ──────────────────────────────────────────────────── - - - - // ── 26.04.06 NDY 추가: ACS 완료 대기 ───────────────── - // TODO: 변수 맵 수신 후 활성화 + // 26.04.06 NDY - ACS completion wait // while (!_ACS.CheckMOTFComp()) { Thread.Sleep(10); } - // ──────────────────────────────────────────────────── - if (_Scanner.Config.UseRecordSignal) { @@ -475,32 +497,30 @@ namespace ILSOS_PrismaticScanner.Sequences _ScannerSaveLogTime += DateTime.Now.Subtract(startTime); } - // excel 파일 작성 SaveScannerRecordFileForamt(); - Log.D(string.Format($"[ComponentTeachingRun] SaveScannerRecordFileForamt Time : {_ScannerSaveLogTime.TotalMilliseconds}")); + Log.D($"[ComponentTeachingRun] SaveScannerRecordFileForamt Time : {_ScannerSaveLogTime.TotalMilliseconds}"); var PartialTactTime = DateTime.Now.Subtract(_partialWeldingStartTime); - Log.D(string.Format($"[TactTime] Complete Partial Component Weld : {PartialTactTime.TotalMilliseconds}")); + Log.D($"[TactTime] Complete Partial Component Weld : {PartialTactTime.TotalMilliseconds}"); _ScannerCanvasUserCtrl.TimeLabelUpdate(_MeasurementSpanTime.TotalMilliseconds, PartialTactTime.TotalMilliseconds); return SequenceAction.Ok(); } + // ==================== SEQ_ACS_MOTION_SET ==================== private SequenceActionResult SetMotion(SequenceContext context, string id, SequenceActionResult param) { - #region 디바이스 세팅 값만 가져오기 var _scannerConfig = _Scanner.Config; - #endregion - Log.I("[SetMotion] 정방향 파라미터 세팅 시작"); - Log.I($"[SetMotion] PosX={_scannerConfig.MarkingStartX_1}, NegX={_scannerConfig.MarkingStartX_2}, Vel={_scannerConfig.MOTFStageSpeed}"); - // 메뉴얼 화면에서 셋팅이 필요해보임 - 출발, 도착, 스피드,rPCset_MOF_WEL_ACC,iPCset_MOF_WEL_MOTION_INTERVAL - // iMOTFComplete 리셋 - _ACS.ResetMOTFComp(); // 0 으로 바꿔주는 거 다시 추가해줘야함. - - Log.I("[SetMotion] ResetMOTFComp 완료"); - // 정방향 파라미터 세팅 (310 → 640) + Log.I("[SetMotion] Forward param setting start"); + Log.I($"[SetMotion] PosX={_scannerConfig.MarkingStartX_1}, NegX={_scannerConfig.MarkingStartX_2}, Vel={_scannerConfig.MOTFStageSpeed}"); + + // iMOTFComplete reset + _ACS.ResetMOTFComp(); + Log.I("[SetMotion] ResetMOTFComp done"); + + // Forward direction param setting (310 -> 640) bool result = _ACS.SetMOTFParams( posX: _scannerConfig.MarkingStartX_1, negX: _scannerConfig.MarkingStartX_2, @@ -508,129 +528,153 @@ namespace ILSOS_PrismaticScanner.Sequences acc: 5000, interval: 100 ); + if (!result) { Log.E("[SetMotion] SetMOTFParams failed"); return null; } + Log.I("[SetMotion] SetMOTFParams done"); + + // 26.04.17 NDY - below line overwrites with MarkSpeed/0/0, commented out + // _ACS.SetMOTFParams(_scannerConfig.MarkingStartX_1, _scannerConfig.MarkingStartX_2, _scannerConfig.MarkSpeed, 0, 0); - if (!result) { Log.E("[SetMotion] SetMOTFParams 실패"); return null; } - Log.I("[SetMotion] SetMOTFParams 완료"); - _ACS.SetMOTFParams(_scannerConfig.MarkingStartX_1, _scannerConfig.MarkingStartX_2, _scannerConfig.MarkSpeed, 0, 0); return SequenceAction.Ok(); - } + // ==================== SEQ_ACS_MOTION_START ==================== private SequenceActionResult StartMotion(SequenceContext context, string id, SequenceActionResult param) { - #region + #region old code (commented out) //_ACS.MOTFStart(); - //// 모션 동작시키고 바로 확인하면 그냥 빠져나올 것 같음 //var res = context.Wait(1000, 100); - //if (res == E2.TIMEOUT) {;} + //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(); - #endregion var config = _Scanner.Config; - // ── 정방향 실행 (310 → 640) ── - Log.I("[StartMotion] 정방향 MOTFStart 호출"); + // ── Forward execution (310 -> 640, params already set in SetMotion) ── + Log.I("[StartMotion] Forward MOTFStart called"); _ACS.MOTFStart(); - // 출발 후 50mm 이동 시간만큼 대기 - // 50mm / velocity(mm/s) * 1000 = ms - int waitMs = (int)((50.0 / config.MOTFStageSpeed) * 1000) + 200; // 여유 200ms - Log.I($"[StartMotion] 정방향 50mm 이동 대기 {waitMs}ms"); + int waitMs = (int)((50.0 / config.MOTFStageSpeed) * 1000) + 200; + Log.I($"[StartMotion] Forward 50mm wait {waitMs}ms"); var res = context.Wait(waitMs, 10); - if (res == E2.CANCELLED) { Log.E("[StartMotion] 정방향 50mm 대기 CANCELLED"); return null; } + if (res == E2.CANCELLED) { Log.E("[StartMotion] Forward 50mm wait CANCELLED"); return null; } - // 정방향 완료 대기 - Log.I("[StartMotion] 정방향 CheckMOTFComp 대기 시작"); + Log.I("[StartMotion] Forward CheckMOTFComp wait start"); res = context.RetryUntilSuccess(() => _ACS.CheckMOTFComp(), 1000 * 30, 100, true); - if (res == E2.TIMEOUT) { Log.E("[StartMotion] 정방향 TIMEOUT"); return null; } - else if (res == E2.CANCELLED) { Log.E("[StartMotion] 정방향 CANCELLED"); return null; } - Log.I("[StartMotion] 정방향 완료"); + if (res == E2.TIMEOUT) { Log.E("[StartMotion] Forward TIMEOUT"); return null; } + else if (res == E2.CANCELLED) { Log.E("[StartMotion] Forward CANCELLED"); return null; } + Log.I("[StartMotion] Forward complete"); - // ── 역방향 세팅 (640 → 310) ── - Log.I("[StartMotion] 역방향 파라미터 세팅 시작"); + // ── Reverse setting (640 -> 310) after forward complete ── + Log.I("[StartMotion] Reverse param setting start"); _ACS.ResetMOTFComp(); - Log.I("[StartMotion] 역방향 ResetMOTFComp 완료"); + Log.I("[StartMotion] Reverse ResetMOTFComp done"); bool result = _ACS.SetMOTFParams( - posX: config.MarkingStartX_2, // 640 출발 - negX: config.MarkingStartX_1, // 310 도착 + posX: config.MarkingStartX_2, // 640 start + negX: config.MarkingStartX_1, // 310 end velocity: config.MOTFStageSpeed, acc: 5000, interval: 100 ); - if (!result) { Log.E("[StartMotion] 역방향 SetMOTFParams 실패"); return null; } - Log.I($"[StartMotion] 역방향 SetMOTFParams 완료 | PosX={config.MarkingStartX_2}, NegX={config.MarkingStartX_1}"); + if (!result) { Log.E("[StartMotion] Reverse SetMOTFParams failed"); return null; } + Log.I($"[StartMotion] Reverse SetMOTFParams done | PosX={config.MarkingStartX_2}, NegX={config.MarkingStartX_1}"); - // ── 역방향 실행 (640 → 310) ── - Log.I("[StartMotion] 역방향 MOTFStart 호출"); + // ── Reverse execution ── + Log.I("[StartMotion] Reverse MOTFStart called"); _ACS.MOTFStart(); - // 출발 후 50mm 이동 대기 - Log.I($"[StartMotion] 역방향 50mm 이동 대기 {waitMs}ms"); + Log.I($"[StartMotion] Reverse 50mm wait {waitMs}ms"); res = context.Wait(waitMs, 10); - if (res == E2.CANCELLED) { Log.E("[StartMotion] 역방향 50mm 대기 CANCELLED"); return null; } + if (res == E2.CANCELLED) { Log.E("[StartMotion] Reverse 50mm wait CANCELLED"); return null; } - // 역방향 완료 대기 - Log.I("[StartMotion] 역방향 CheckMOTFComp 대기 시작"); + Log.I("[StartMotion] Reverse CheckMOTFComp wait start"); res = context.RetryUntilSuccess(() => _ACS.CheckMOTFComp(), 1000 * 30, 100, true); - if (res == E2.TIMEOUT) { Log.E("[StartMotion] 역방향 TIMEOUT"); return null; } - else if (res == E2.CANCELLED) { Log.E("[StartMotion] 역방향 CANCELLED"); return null; } - Log.I("[StartMotion] 역방향 완료 → EndWelding 진입"); + if (res == E2.TIMEOUT) { Log.E("[StartMotion] Reverse TIMEOUT"); return null; } + else if (res == E2.CANCELLED) { Log.E("[StartMotion] Reverse CANCELLED"); return null; } + Log.I("[StartMotion] Reverse complete -> EndWelding"); + + #region Stage X return to 0 after welding complete + // 26.04.16 NDY - Stage X return to origin(0) after welding + _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(); + Log.D("[StartMotion] AxisX.Move() to 0 called"); + + res = context.RetryUntilSuccess( + () => _AxisX.IsReachedTargetPosition(0), + 30 * 1000, 20, true); + + if (res == E2.TIMEOUT) { Log.E("[EndWelding] Stage X return TIMEOUT"); } + else if (res == E2.CANCELLED) { Log.E("[EndWelding] Stage X return CANCELLED"); } + else { Log.I("[EndWelding] Stage X = 0 return complete"); } + #endregion + + // 26.04.16 NDY - sequence result set + context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true); + Log.I("[StartMotion] KEY_SEQUENCE_RESULT = true set"); return SequenceAction.Ok(); - - } + + // ==================== SEQ_CHECK_MOTION_END ==================== private SequenceActionResult EndWelding(SequenceContext context, string id, SequenceActionResult param) { - + Log.I("[EndWelding] Entry"); _Scanner.WaitForListTaskCompleted(_SystemParameter.WeldLinesTaskTimeOut); Log.I("ExecuteList Complete"); _MeasurementSpanTime = DateTime.Now.Subtract(_SequenceCompleteTime); - Log.D(string.Format($"[TactTime] Complete All Component Weld : {_MeasurementSpanTime.TotalMilliseconds}")); + Log.D($"[TactTime] Complete All Component Weld : {_MeasurementSpanTime.TotalMilliseconds}"); - - //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨 + // Analog output - COMMAND mode = direct (not list) _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 5, eFunctionType.COMMAND); _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 5, eFunctionType.COMMAND); + Log.D("[EndWelding] WriteDA(DA1=5, DA2=5) done"); + _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); + Log.D("[EndWelding] JumpAbs(0,0) done"); + _Scanner.Fly_Return(_Scanner.Config.MarkingOntheFlyParkingPositionX, _Scanner.Config.MarkingOntheFlyParkingPositionY); - _Scanner.SetEndList(); + Log.D($"[EndWelding] Fly_Return({_Scanner.Config.MarkingOntheFlyParkingPositionX}, {_Scanner.Config.MarkingOntheFlyParkingPositionY}) done"); + + // _Scanner.SetEndList(); // moved to DoComponentWelding var mesurementTime = DateTime.Now; - //용접 완료 후 CorrectionFile Vision으로 원복 + // Restore CorrectionFile to Vision after welding _Scanner.SelectCorrectionTable(1); + Log.D("[EndWelding] SelectCorrectionTable(1) restored"); _MeasurementSpanTime = DateTime.Now.Subtract(mesurementTime); - Log.D(string.Format($"[ComponentTeachingRun] Change SelectCorrectionTable Time : {_MeasurementSpanTime.TotalMilliseconds}")); + Log.D($"[ComponentTeachingRun] Change SelectCorrectionTable Time : {_MeasurementSpanTime.TotalMilliseconds}"); return SequenceAction.Ok(); } + // ==================== SEQ_COMPLETE ==================== private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param) { + Log.D("[Complete] Start"); context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult); SeqTeachingRun_OutParam result = new SeqTeachingRun_OutParam() { Success = seqResult }; + Log.I($"[Complete] seqResult = {seqResult}"); if (seqResult == false) - { AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_TeachingRun_ResultFailed); - } Log.D("ComponentTeachingRun Sequence Complete"); return SequenceAction.Ok(result); } + // ==================== Helper methods ==================== private void SaveScannerRecordFileForamt() { if (_Scanner.Config.UseRecordSignal) @@ -643,18 +687,14 @@ namespace ILSOS_PrismaticScanner.Sequences private void OnForceError(SequenceContext context, string arg2) { - // Scanner Log 저장 + Log.E("===== [OnForceError] Force error occurred ====="); SaveScannerRecordFileForamt(); - - //List 실행 종료 _Scanner.StopExecution(); + Log.D("[OnForceError] StopExecution done"); - //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); @@ -664,44 +704,33 @@ namespace ILSOS_PrismaticScanner.Sequences private void OnStopped(SequenceContext context) { - // Scanner Log 저장 + Log.W("===== [OnStopped] Sequence stopped ====="); SaveScannerRecordFileForamt(); - - //List 실행 종료 _Scanner.StopExecution(); + Log.D("[OnStopped] StopExecution done"); - //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 IsObjectSelectAll { get; set; } public bool UseStream { get; set; } - public bool UseBlow { get; set; } public int IntervalTimeBlowOn { get; set; } public int IntervalTimeBlowOff { get; set; } - - public bool NeedSetLaserTiming { get; set; } // For SubSequence, SeqUploadWeldingList - + public bool NeedSetLaserTiming { get; set; } } public class SeqAutoRunPrismaticScanner_OutParam : OutParam { - } -} +} \ No newline at end of file diff --git a/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRunViewModel.cs b/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRunViewModel.cs index 7901615..8a851d1 100644 --- a/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRunViewModel.cs +++ b/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRunViewModel.cs @@ -219,8 +219,23 @@ namespace ILSOS_PrismaticScanner.Views // 자동으로 Activated Recipe로 변경 OnUpdateCurrentActivatedRecipe(null); - var param = new SeqAutoRunPrismaticScanner_InitParam() { - //VisionRecipeNo = 0 // 해당 InitParam에는 없는 선언 + //var param = new SeqAutoRunPrismaticScanner_InitParam() { + // //VisionRecipeNo = 0 // 해당 InitParam에는 없는 선언 + //}; + + // 26 04 17 수정 + //var param = new SeqAutoRunPrismaticScanner_InitParam() + //{ + // NeedSetLaserTiming = true, + // IsObjectSelectAll = true, // 전체 오브젝트 선택 + //}; + + var param = new SeqTeachingRun_InitParam() + { + IsObjectSelectAll = true, + CellCenterOffsetX = 0, + CellCenterOffsetY = 0, + }; Status.SetStatus(eEqpStatus.Auto);