From 525339a262cd6196915666b582f2a942d822d057 Mon Sep 17 00:00:00 2001 From: NohDoyeon241104 Date: Wed, 8 Apr 2026 17:09:44 +0900 Subject: [PATCH] =?UTF-8?q?feat=20:=20Scanner=20=EC=BA=98=EB=A6=AC?= =?UTF-8?q?=EB=B8=8C=EB=A0=88=EC=9D=B4=EC=85=98=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=9D=98=20Run=20Mark=20Grid=20=EB=B2=84=ED=8A=BC=20=ED=81=B4?= =?UTF-8?q?=EB=A6=AD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=8B=9C=ED=80=B8?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20=EB=85=B8=EB=93=9C=202=EA=B0=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20/RunScanner?= =?UTF-8?q?=20Calib=EC=9D=80=20=EB=B9=84=EC=A0=84=EC=9D=B4=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C=ED=99=98=EA=B2=BDpc=EC=97=90=20=EC=97=86=EC=96=B4?= =?UTF-8?q?=EC=84=9C=20=ED=99=95=EC=9D=B8=20=EB=B6=88=EA=B0=80=20->=20Move?= =?UTF-8?q?StageToOrigin=20->=20StageX=20->0=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20->=20EncoderReset=20[=EC=8A=A4=ED=82=B5]?= =?UTF-8?q?=20-MOTF=EB=B9=84=ED=99=9C=EC=84=B1=ED=99=94=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=9D=BC=20=EC=8A=A4=ED=82=B5=20->=20UploadWeldingLis?= =?UTF-8?q?t=20-=20=EB=9D=BC=EC=9D=B8=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20->=20=EC=A0=84=EC=B2=B4=20=EC=8B=9C?= =?UTF-8?q?=ED=80=B8=EC=8A=A4=20->=20=ED=86=A0=EC=8A=A4=ED=8A=B8=EB=A9=94?= =?UTF-8?q?=EC=8B=9C=EC=A7=80=20=EC=84=B1=EA=B3=B5=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EA=B2=83=EA=B9=8C=EC=A7=80=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20=3D=20UseMarkingOnTheFly=20=3D=20false=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=B4=20EncoderRest=EC=9D=80=20=EC=8A=A4=ED=82=B5=EB=90=9C?= =?UTF-8?q?=EB=8B=A4.=20[=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD]=20-SeqCompo?= =?UTF-8?q?nentTeachingRun.cs=20=20-=20SeqTeachingRun.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Manual/SeqComponentTeachingRun.cs | 121 +++++++++++++++++- .../Sequences/Manual/SeqTeachingRun.cs | 97 ++++++++++++++ 2 files changed, 212 insertions(+), 6 deletions(-) diff --git a/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs b/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs index f5095db..1278ae7 100644 --- a/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs +++ b/Machines/ILSOS_PrismaticScanner/Sequences/Manual/SeqComponentTeachingRun.cs @@ -1,7 +1,9 @@ using Aurora; using Aurora.Api; +using Aurora.Api.Devices; using Aurora.Api.Services; using Aurora.View; +using AuroraACS; using AuroraIO; using AuroraLaser; using AuroraLaser.Ekspla; @@ -68,6 +70,12 @@ namespace ILSOS_PrismaticScanner.Sequences const string SEQ_CHECK_END_LIST = "[SeqComponentTeachingRun] Check End List"; const string SEQ_END_WELDING = "[SeqComponentTeachingRun] End All Welding"; + #region 260408 NDY 노드 추가 + // Builder()에 노드 추가 + const string SEQ_MOVE_STAGE_ORIGIN = "[SeqComponentTeachingRun] Move Stage To Origin"; + const string SEQ_ENCODER_RESET = "[SeqComponentTeachingRun] Encoder Reset"; + #endregion + const int timeoutSeconds = 10; static class ContextKey { @@ -83,9 +91,12 @@ namespace ILSOS_PrismaticScanner.Sequences // 필드 선언 추가 (다른 필드들 옆에) // 변경 전 //private ScannerACS _ACS; + // 변경 후 private IScannerACS _ACS; // 26.04.06 NDY - MOTF용 ACS Stage X 제어 + private static ACSMotor _AxisX; // 26.04.08 NDY - MOTF용 Stage X 축 + private Laser _Laser; private ILaser _LaserDevice; private LaserTruFiber _LaserTruFiber; @@ -127,6 +138,12 @@ namespace ILSOS_PrismaticScanner.Sequences sb.AddAction(SEQ_ENTER, Sequence_Enter); sb.AddAction(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기 sb.AddAction(SEQ_SET_BLOW_ON, SetBlowOn); + #region 26.04.08 추가된 노드 NDY + // 26.04.08 필드 좌표 0,0으로 스테이지 이동 시퀸스 + EncoderReset 호출 + sb.AddAction(SEQ_MOVE_STAGE_ORIGIN, MoveStageToOrigin); + sb.AddAction(SEQ_ENCODER_RESET, EncoderReset); + #endregion + sb.AddAction(SEQ_START_WELDING, StartWelding); sb.AddDecision(SEQ_CHECK_END_LIST, CheckEndList); sb.AddAction(SEQ_UPDATE_COMPONENT_WELDING_PARAMETERS, UpdateComponentWeldingParameters); @@ -140,10 +157,19 @@ namespace ILSOS_PrismaticScanner.Sequences sb.AddAction(SEQ_COMPLETE, Complete); - + // 기존 흐름에서 SET_BLOW_ON → START_WELDING 사이에 끼워넣기 + #region 기존 흐름 26.04.08 + //sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_SET_BLOW_ON] + // > sb[SEQ_START_WELDING] > sb[SEQ_CHECK_END_LIST]); + #endregion + #region 2개 노드 추가한 흐름 26.04.08 sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_SET_BLOW_ON] + > sb[SEQ_MOVE_STAGE_ORIGIN] // ← 추가 + > sb[SEQ_ENCODER_RESET] // ← 추가 > sb[SEQ_START_WELDING] > sb[SEQ_CHECK_END_LIST]); + #endregion + // 아래 두 흐름은 기존 그대로 유지 sb.Flows(sb[SEQ_CHECK_END_LIST] > sb[SEQ_UPDATE_COMPONENT_WELDING_PARAMETERS] > 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_CHECK_END_LIST]); @@ -158,9 +184,70 @@ namespace ILSOS_PrismaticScanner.Sequences return sb; } + #region 26.04.08 NDY + private SequenceActionResult EncoderReset(SequenceContext context, string id, SequenceActionResult param) + { + if (_ACS == null) + { + Log.W("[EncoderReset] ACS null - 스킵"); + return SequenceAction.Ok(); + } + 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 SequenceActionResult MoveStageToOrigin(SequenceContext context, string id, SequenceActionResult param) + { + if (_AxisX == null) + { + Log.W("[MoveStageToOrigin] AxisX2 null - 스킵"); + return SequenceAction.Ok(); + } + + Log.I("[MoveStageToOrigin] Stage X → 0 이동 시작"); + + // SeqTeachingPosMoveStageA.MoveXYStage 패턴 + _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(); + + var res = context.RetryUntilSuccess( + () => _AxisX.IsReachedTargetPosition(0), + 30 * 1000, 20, true); + + if (res == E2.TIMEOUT) { Log.E("[MoveStageToOrigin] TIMEOUT"); return null; } + if (res == E2.CANCELLED) { Log.E("[MoveStageToOrigin] CANCELLED"); return null; } + + Log.I("[MoveStageToOrigin] Stage X = 0 도달 완료"); + return SequenceAction.Ok(); + + } + #endregion 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; @@ -209,6 +296,10 @@ namespace ILSOS_PrismaticScanner.Sequences // 변경 후 try { _ACS = context.Devices[DeviceIds.ACS] as IScannerACS; } catch { _ACS = null; Log.W("ACS 디바이스 취득 실패 (null 처리)"); } + + //26.04.08 NDY 추가 _AxisX 가져오기 + try { _AxisX = context.Devices[CommonDeviceIds.AxisX2] as ACSMotor; } + catch { _AxisX = null; Log.W("AxisX2 취득 실패"); } } private bool CheckDeviceReady(SequenceContext context) { @@ -401,12 +492,30 @@ namespace ILSOS_PrismaticScanner.Sequences SlotNo = 1; // 결과 Data 를 업데이트 함. var power = Status.GetProcessingWeldingData().UploadProcessDatas.Where(data => data.DataName == $"Power_{SlotNo}_{_SequenceSetting.LastPageNo}").FirstOrDefault(); - power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; - var velocity = Status.GetProcessingWeldingData().UploadProcessDatas.Where(data => data.DataName == $"Velocity_{SlotNo}_{_SequenceSetting.LastPageNo}").FirstOrDefault(); - velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; - // FDC 데이터 상신 - _plc.UploadReportDatas(Status.GetProcessingWeldingData().UploadProcessDatas); + #region 26.04.08 NDY 캘리브레이션 노드 추가 테스트를 위해서 잠시 예외처리 코드 추가 + if (power != null) + power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 + + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; + #endregion + // 26.04.08 NDY 주석처리 - power null 예외 방지 (위 null 체크로 대체) + //power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; + + + var velocity = Status.GetProcessingWeldingData().UploadProcessDatas.Where(data => data.DataName == $"Velocity_{SlotNo}_{_SequenceSetting.LastPageNo}").FirstOrDefault(); + #region 26.04.08 NDY velocity null에 대한 예외 처리 + if (velocity != null) + velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; + #endregion + // 26.04.08 NDY 주석처리 - velocity null 예외 방지 (위 null 체크로 대체) + //velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; + + + // 26.04.08 NDY - FDC 데이터 상신 -기존 코드 주석처리 + //_plc.UploadReportDatas(Status.GetProcessingWeldingData().UploadProcessDatas); + // 26.04.08 NDY - FDC 상신도 데이터 있을 때만 - 테스트 목적 예외 처리 추가 + if (Status.GetProcessingWeldingData().UploadProcessDatas.Count > 0) + _plc.UploadReportDatas(Status.GetProcessingWeldingData().UploadProcessDatas); _CenterBeamPower = (int)ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1; _RingBeamPower = (int)ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; diff --git a/WeldingComponents/WeldingCommon/Sequences/Manual/SeqTeachingRun.cs b/WeldingComponents/WeldingCommon/Sequences/Manual/SeqTeachingRun.cs index d6de800..5955440 100644 --- a/WeldingComponents/WeldingCommon/Sequences/Manual/SeqTeachingRun.cs +++ b/WeldingComponents/WeldingCommon/Sequences/Manual/SeqTeachingRun.cs @@ -1,9 +1,13 @@ using Aurora.Api; +using Aurora.Api.Devices; using Aurora.Api.Services; +// 26.04.08 NDY 추가 - 비전 있는 오토 티칭 버튼은 MIL없어서 리스트만으로 테스트 시퀸스 확인하고자 추가 예정 +using AuroraACS; using AuroraIO; using AuroraLaserScanner; using AuroraLBAD; using AuroraLBAD.Services; +using AuroraMotionBoard.Ajin; using AuroraOCT; using CoreUnit.Control.UserControl.Canvas; using CoreUnit.Scanner.UserControl.ScannerCanvas; @@ -52,6 +56,12 @@ namespace WeldingCommon.Sequences const string SEQ_WRITE_COMPLETION_PLC_BIT = "[SeqTeachingRun] Write Completion PLC Bit"; const string SEQ_COMPLETE = "[SeqTeachingRun] SEQ_COMPLETE"; // 완료 + #region 26.04.08 NDY 상수 추가 + const string SEQ_MOVE_STAGE_ORIGIN = "[SeqTeachingRun] Move Stage To Origin"; + const string SEQ_ENCODER_RESET = "[SeqTeachingRun] Encoder Reset"; + + #endregion + static class ContextKey { public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqTeachingRun"; @@ -64,6 +74,7 @@ namespace WeldingCommon.Sequences private OCT _OCT; private IODevice _IO; private StreamLBADClient _StreamLBADClient; + private ACSMotor _AxisX; // ← 26.04.08 NDY 필드 추가 private SystemParameter _SystemParameter; private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언 @@ -73,6 +84,9 @@ namespace WeldingCommon.Sequences private static TimeSpan _WeldingSpanTime; //TeacingRun 시퀀스에서 용접 소요시간 + + + public SequenceBuilder Builder() { var sb = new SequenceBuilder(1000, true); @@ -81,6 +95,12 @@ namespace WeldingCommon.Sequences sb.AddAction(SEQ_ENTER, Sequence_Enter); sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기 + #region 26.04.08 NDY 노드 추가 + sb.AddAction(SEQ_MOVE_STAGE_ORIGIN, MoveStageToOrigin); + sb.AddAction(SEQ_ENCODER_RESET, EncoderReset); + #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); @@ -95,12 +115,23 @@ namespace WeldingCommon.Sequences sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write sb.AddAction(SEQ_COMPLETE, Complete); + #region 기존 흐름 26.04.08 주석처리 + //sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] + // > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ] + // > sb[SEQ_SET_BLOW_ON] > sb[SEQ_STREAM_LBAD_BEFORE_WELDLINES] > sb[SEQ_EXECUTE_WELDLINES] + // > sb[SEQ_SET_BLOW_OFF] > sb[SEQ_STREAM_LBAD_AFTER_WELDLINES] + // > sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_COMPLETE]); + #endregion + #region 2개 노드 추가 흐름 26.04.08 - Test 중 sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] + > sb[SEQ_MOVE_STAGE_ORIGIN] // ← 추가 + > sb[SEQ_ENCODER_RESET] // ← 추가 > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_SET_BLOW_ON] > sb[SEQ_STREAM_LBAD_BEFORE_WELDLINES] > sb[SEQ_EXECUTE_WELDLINES] > sb[SEQ_SET_BLOW_OFF] > sb[SEQ_STREAM_LBAD_AFTER_WELDLINES] > sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_COMPLETE]); + #endregion sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final); @@ -112,6 +143,64 @@ namespace WeldingCommon.Sequences return sb; } + #region 26.04.08 NDY 메서드 추가 + private SequenceActionResult EncoderReset(SequenceContext context, string id, SequenceActionResult param) + { + if (_AxisX == null) + { + Log.W("[EncoderReset] AxisX2 null - 스킵"); + return SequenceAction.Ok(); + } + + if (!_Scanner.Config.UseMarkingOnTheFly) + { + Log.W("[EncoderReset] MOTF 비활성화 - 스킵"); + return SequenceAction.Ok(); + } + + Log.I("[EncoderReset] 시작"); + + _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(); + } + + private SequenceActionResult MoveStageToOrigin(SequenceContext context, string id, SequenceActionResult param) + { + if (_AxisX == null) + { + Log.W("[MoveStageToOrigin] AxisX2 null - 스킵"); + return SequenceAction.Ok(); + } + + Log.I("[MoveStageToOrigin] Stage X → 0 이동 시작"); + + _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(); + + var res = context.RetryUntilSuccess( + () => _AxisX.IsReachedTargetPosition(0), + 30 * 1000, 20, true); + + if (res == E2.TIMEOUT) { Log.E("[MoveStageToOrigin] TIMEOUT"); return null; } + if (res == E2.CANCELLED) { Log.E("[MoveStageToOrigin] CANCELLED"); return null; } + + Log.I("[MoveStageToOrigin] Stage X = 0 도달 완료"); + return SequenceAction.Ok(); + } + #endregion private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) { @@ -180,6 +269,14 @@ namespace WeldingCommon.Sequences // return SequenceAction.Error(); //} + + #region 26.04.08 NDY 수정 예외처리를 위해서 + + // ↓ 추가 + try { _AxisX = context.Devices[CommonDeviceIds.AxisX2] as ACSMotor; } + catch { _AxisX = null; Log.W("[SeqTeachingRun] AxisX2 취득 실패"); } + #endregion + } private bool CheckDeviceReady(SequenceContext context)