diff --git a/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/.~lock.각형고속용접단동기_ACS_Buffer&Variable_Info_20260407.xlsx# b/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/.~lock.각형고속용접단동기_ACS_Buffer&Variable_Info_20260407.xlsx# deleted file mode 100644 index 3d933f0..0000000 --- a/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/.~lock.각형고속용접단동기_ACS_Buffer&Variable_Info_20260407.xlsx# +++ /dev/null @@ -1 +0,0 @@ -,DESKTOP-CN4O63O/User,DESKTOP-CN4O63O,09.04.2026 15:52,file:///C:/Users/User/AppData/Roaming/LibreOffice/4; \ No newline at end of file diff --git a/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/각형배터리 캔캡 MOF용접 제어컨셉_260408.pptx b/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/각형배터리 캔캡 MOF용접 제어컨셉_260408.pptx index afa6a70..c86620b 100644 Binary files a/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/각형배터리 캔캡 MOF용접 제어컨셉_260408.pptx and b/Doc/FromLGPRI/각형 고속등속용접 RND설비_MOF테스트관련프로그램개발컨셉및acs변수공유건/각형배터리 캔캡 MOF용접 제어컨셉_260408.pptx differ diff --git a/Machines/ILSOS_PrismaticScanner/EquipDef.cs b/Machines/ILSOS_PrismaticScanner/EquipDef.cs index 1d16b4e..e5c1a6c 100644 --- a/Machines/ILSOS_PrismaticScanner/EquipDef.cs +++ b/Machines/ILSOS_PrismaticScanner/EquipDef.cs @@ -40,6 +40,9 @@ namespace ILSOS_PrismaticScanner public const string SeqAlarmMonitor = "SeqAlarmMonitor"; public const string SeqStatusMonitor = "SeqStatusMonitor"; + // 26.04.09 NDY - ACS MOTF AutoRun - > SeqAutoRun은 CommonSequenceIds에 존재함. + public const string SeqAutoRunACS = "SeqAutoRunACS"; + } public class StreamMessageNames diff --git a/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs b/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs index 9c9ce87..4005908 100644 --- a/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs +++ b/Machines/ILSOS_PrismaticScanner/EquipmentSetup.cs @@ -64,6 +64,9 @@ namespace ILSOS_PrismaticScanner private SequenceBuilder _SeqAutoRunPrismaticScanner_Builder; private SequenceBuilder _SeqAutoRunComponentWeld_Builder; private SequenceBuilder _SeqAlarmMonitor_Builder; + #region 26.04.09 NDY - ACS MOTF AutoRun Sequence Builder + private SequenceBuilder _SeqAutoRunPrismaticScannerACS_Builder; + #endregion public override void BuildMainStatusView(MainStatusBuilder builder) @@ -265,28 +268,30 @@ namespace ILSOS_PrismaticScanner // Common Sequences register SequenceManager.RegisterSequence(AuroraEnv, register); base.RegisterSequences(register); - var equipSetting = AuroraEnv.SettingEntry[AuroraSettings.Equipment] as EquipmentInfo; register(SequenceIds.SeqStreamTest, SeqStreamTest.Builder()); - // Event Q Test 추가 var seqEventQTest = new SeqEventQTest(); register(SequenceIds.SeqEventQTest, seqEventQTest.Builder()); - var seqAutoRunPrismaticScanner = new SeqAutoRunPrismaticScanner(); var seqAutoRunComponentWeld = new SeqAutoRunComponentWeld(); var seqAlarmMonitor = new SeqAlarmMonitor(); - _SeqAutoRunPrismaticScanner_Builder = seqAutoRunPrismaticScanner.Builder(); _SeqAutoRunComponentWeld_Builder = seqAutoRunComponentWeld.Builder(); _SeqAlarmMonitor_Builder = seqAlarmMonitor.Builder(); - register(SequenceIds.SeqAutoRun, _SeqAutoRunPrismaticScanner_Builder); register(SequenceIds.SeqAutoRunComponentWeld, _SeqAutoRunComponentWeld_Builder); register(SequenceIds.SeqAlarmMonitor, _SeqAlarmMonitor_Builder); register(SequenceIds.SeqStatusMonitor, SeqStatusMonitor.Builder()); - // SequenceManager.RegisterSequence(AuroraEnv, register); + + #region 26.04.09 NDY - ACS MOTF AutoRun Sequence 등록 + // 기존 SeqAutoRun(SeqAutoRunPrismaticScanner)은 건드리지 않고 + // ACS MOTF 전용 시퀀스를 별도 Builder로 등록 + var seqAutoRunPrismaticScannerACS = new SeqAutoRunPrismaticScannerACS(); + _SeqAutoRunPrismaticScannerACS_Builder = seqAutoRunPrismaticScannerACS.Builder(); + register(SequenceIds.SeqAutoRunACS, _SeqAutoRunPrismaticScannerACS_Builder); + #endregion } public override void RegisterSettings(SettingRegister register) { @@ -384,13 +389,13 @@ namespace ILSOS_PrismaticScanner manager.RegisterMainMenu(calibration); // Monitoring - #region 26 04 06 주석처리 PLCBitMonitoring PLCWordMonitoring IOInterfaceView (o) PLCInterfaceView (원래부터 주석처리) - //var monitoring = new MainMenu(CommonViewIds.Monitoring, MenuIcon.GetMenuIcon(eMenuIcon.MONITORING), PermissionLevel.NONE, true); - ////manual.AddSubMenu(CommonViewIds.PLCInterfaceView, new UCPLCInterfaceView(AuroraEnv, CommonDeviceIds.PLCDevice)); - //monitoring.AddSubMenu(CommonViewIds.PLCBitMonitoring, new UCPLCBit() { DataContext = PLCStatic.GetInstanceBitVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.M) }); - //monitoring.AddSubMenu(CommonViewIds.PLCWordMonitoring, new UCPLCWord() { DataContext = PLCStatic.GetInstanceWordVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.D) }); - //monitoring.AddSubMenu(CommonViewIds.IOInterfaceView, new UCIOInterfaceView(AuroraEnv, CommonDeviceIds.IODevice)); - //manager.RegisterMainMenu(monitoring); + #region 26 04 06 주석처리 PLCBitMonitoring PLCWordMonitoring IOInterfaceView (o) PLCInterfaceView (원래부터 주석처리) - 04.10 잠시 추가 + var monitoring = new MainMenu(CommonViewIds.Monitoring, MenuIcon.GetMenuIcon(eMenuIcon.MONITORING), PermissionLevel.NONE, true); + //manual.AddSubMenu(CommonViewIds.PLCInterfaceView, new UCPLCInterfaceView(AuroraEnv, CommonDeviceIds.PLCDevice)); + monitoring.AddSubMenu(CommonViewIds.PLCBitMonitoring, new UCPLCBit() { DataContext = PLCStatic.GetInstanceBitVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.M) }); + monitoring.AddSubMenu(CommonViewIds.PLCWordMonitoring, new UCPLCWord() { DataContext = PLCStatic.GetInstanceWordVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.D) }); + monitoring.AddSubMenu(CommonViewIds.IOInterfaceView, new UCIOInterfaceView(AuroraEnv, CommonDeviceIds.IODevice)); + manager.RegisterMainMenu(monitoring); #endregion // Device diff --git a/Machines/ILSOS_PrismaticScanner/ILSOS_PrismaticScanner.csproj b/Machines/ILSOS_PrismaticScanner/ILSOS_PrismaticScanner.csproj index d332e46..42c2f67 100644 --- a/Machines/ILSOS_PrismaticScanner/ILSOS_PrismaticScanner.csproj +++ b/Machines/ILSOS_PrismaticScanner/ILSOS_PrismaticScanner.csproj @@ -110,6 +110,7 @@ + diff --git a/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScannerACS.cs b/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScannerACS.cs new file mode 100644 index 0000000..9854880 --- /dev/null +++ b/Machines/ILSOS_PrismaticScanner/Sequences/SeqAutoRunPrismaticScannerACS.cs @@ -0,0 +1,1076 @@ +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.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 +{ + /// + /// ACS 모션 + 갈바노 스캐너 MOTF 자동운전 시퀀스 + /// 기존 SeqAutoRunPrismaticScanner 기반으로 작성 + /// - 비전 보상 / 이미지 저장 제거 (MOTF 구조상 불필요) + /// - InitFly2D(엔코더 리셋) 노드 추가 + /// - Object 리스트를 List1 / List2 로 분리하여 각각 MOTF 용접 수행 + /// 작성일: 26.04.09 NDY + /// + class SeqAutoRunPrismaticScannerACS + { + static readonly Logger Log = new Logger("SeqAutoRunPrismaticScannerACS"); + + #region 시퀀스 노드 이름 + // 기존 SeqAutoRunPrismaticScanner 노드명과 동일하게 유지 + const string SEQ_ENTER = "SEQ_ENTER"; + const string SEQ_CHECK_INIT_CONDITION = "Check Init Condition"; + + 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_WELDDING_RETRY = "Check Welding Retry"; + + 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"; + + #region 26.04.09 NDY - MOTF 추가 노드 + // 기존 노드에 없던 ACS MOTF 전용 노드 + // 노드명 뒤에 "ACS" 또는 "1"/"2" 붙여서 기존과 구분 + + // [MOTF Step 1] Linear 원점에서 엔코더 리셋 + const string SEQ_RESET_ENCODER_ACS = "Reset Encoder ACS (InitFly2D)"; + + // [MOTF Step 3] 1번 Object 리스트 오프셋 이동 + const string SEQ_MOVE_OBJECT_LIST1_ACS = "Move Object List1 ACS (Scanner Center Offset)"; + + // [MOTF Step 5] 2번 Object 리스트 세팅 및 용접 + const string SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ2 = "Set Params Component Teachingrun Seq2"; + const string SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ2 = "Process (Component Teachingrun Seq2)"; + const string SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ2 = "Get Result Component Teachingrun Seq2"; + + // [MOTF Step 6] 2번 Object 리스트 오프셋 이동 + const string SEQ_MOVE_OBJECT_LIST2_ACS = "Move Object List2 ACS (Scanner Center Offset)"; + #endregion + + #region 26.04.10 NDY - 시뮬레이션 우회 전용 노드 + // PLC 서브시퀀스를 개발 환경에서 스킵하기 위한 노드 + // 실제 장비에서는 이 노드를 타지 않음 + const string SEQ_SKIP_PLC_WELD_COMPLETE_ACS = "Skip PLC WeldComplete ACS (Simulation)"; + #endregion + + #endregion + + static class ContextKey + { + // 기존과 동일한 패턴, 클래스명만 ACS로 변경 + public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqAutoRunPrismaticScannerACS"; + public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqAutoRunPrismaticScannerACS"; + } + + #region 변수 정의 + + #region 26.04.10 NDY - 시뮬레이션 1사이클 테스트용 + // true: 1사이클 후 루프 중단, false: 기존처럼 계속 루프 + private bool _IsOneCycleTest = true; + #endregion + + // 기존 SeqAutoRunPrismaticScanner 변수 그대로 유지 + + // 디바이스 정의 + private Scanner _Scanner; + private Laser _Laser; + private PLCControllerPrismaticScanner _PLC; + private IODevice _IO; + private LightDevice _Light; + + // 비전 정의 + // MOTF에서는 비전 보상을 사용하지 않으나, 향후 확장 가능성을 위해 선언 유지 + 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; + + // InitParam 타입만 ACS용으로 변경, 나머지 구조 동일 + private SeqAutoRunPrismaticScannerACS_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; + + 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); + + #region 26.04.09 NDY - ACS MOTF 추가 변수 + // ACS 모션 컨트롤러 참조 + // WeldingCommon/Devices/ACS/ScannerACS.cs 에 등록된 장치 + // Stage X 축(AxisX2), Y1/Y2/Z 축 제어 및 엔코더 값 읽기에 사용 + private ScannerACS _ScannerACS; + + // MOTF용 Object 리스트 분리 + // 이미지 기준: + // List1 - Page1 끝점 ~ Page2 시작점 (RM 용접, Virtual Line 포함) + // List2 - Page3 끝점 ~ Page4 시작점 (LM 용접, Virtual Line 포함) + // 기존 _SelectedDrawObjectList 명명 패턴 유지, 숫자로 구분 + private List _SelectedDrawObjectList1; + private List _SelectedDrawObjectList2; + + // 각 Object 리스트별 스캐너 오프셋 + // 스캐너 중심(50, 0) ~ Linear 원점(0, 0) 간 거리만큼 Object 좌표를 이동 + // → WaitForEncoderX가 RM(25,0) / LM(75,0) 시작점에서 정확히 트리거되도록 보장 + // TODO: 이정인 선임 ACS 매핑 파일 수령 후 실제 수치로 확정 필요 + private double _ObjectList1OffsetX = 0; + private double _ObjectList1OffsetY = 0; + private double _ObjectList2OffsetX = 0; + private double _ObjectList2OffsetY = 0; + #endregion + + #endregion + + public SequenceBuilder Builder() + { + var sb = new SequenceBuilder(1000, true); + + // 기존 노드 등록 (SeqAutoRunPrismaticScanner와 동일 구조) + sb.AddAction(SEQ_ENTER, Sequence_Enter); + sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); + + #region 26.04.10 NDY - PLC WeldRequest : Action → Decision 변경 + // 기존: sb.AddAction(SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, ...) + // 변경 이유: 시뮬레이션 환경에서 서브시퀀스(SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST)를 + // 타지 않도록 Decision 분기 처리가 필요 + // 실제 장비: SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST → SEQ_GET_PARAMS → SEQ_CHECK_WELDDING_RETRY + // 시뮬레이션: 서브시퀀스 스킵 → 바로 SEQ_CHECK_WELDDING_RETRY + sb.AddDecision(SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ, SetPlcPageChangeOrWeldRequest_SeqParams); + #endregion + + 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); + + sb.AddDecision(SEQ_CHECK_WELDDING_RETRY, CheckWeldingRetry); + sb.AddAction(SEQ_GENERATE_WELDING_OBJECT, GenerateWeldingObject); + + // List1 용접 (RM) + 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); + + // List2 용접 (LM) + sb.AddAction(SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ2, SetComponentTeachingrun_SeqParams2); + sb.AddSubsequence(SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ2, SequenceManager.GetSeqBuilder_SeqComponentTeachingRun()); + sb.AddAction(SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ2, GetComponentTeachingrun_SeqResult2); + + #region 26.04.10 NDY - PLC WeldComplete : Action → Decision 변경 + // 기존: sb.AddAction(SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, ...) + // 변경 이유: 시뮬레이션 환경에서 서브시퀀스(SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ)를 + // 타지 않도록 Decision 분기 처리가 필요 + // 실제 장비: SEQ_DO_PLC_WELD_COMPLETE → SEQ_GET_PARAMS → SEQ_SAVE_RESULT_DATA + // 시뮬레이션: SEQ_SKIP_PLC_WELD_COMPLETE_ACS → SEQ_SAVE_RESULT_DATA + sb.AddDecision(SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ, SetPlcWeldCompleteInterface_SeqParams); + sb.AddAction(SEQ_SKIP_PLC_WELD_COMPLETE_ACS, SkipPlcWeldComplete); + #endregion + + 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); + + #region 26.04.09 NDY - MOTF 추가 노드 등록 + sb.AddAction(SEQ_RESET_ENCODER_ACS, ResetEncoder); + sb.AddAction(SEQ_MOVE_OBJECT_LIST1_ACS, MoveObjectList1); + sb.AddAction(SEQ_MOVE_OBJECT_LIST2_ACS, MoveObjectList2); + #endregion + + // ── 메인 흐름 ────────────────────────────────────────────── + sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] + > sb[SEQ_RESET_ENCODER_ACS] + > sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]); + + #region 26.04.10 NDY - PLC WeldRequest 분기 흐름 + // 실제 장비: 서브시퀀스 진입 + sb.Flows(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] + > sb[SEQ_CHECK_WELDDING_RETRY]); + // 시뮬레이션: 서브시퀀스 스킵 → 바로 CheckWeldingRetry + sb.Flows(sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ] + > sb[SEQ_CHECK_WELDDING_RETRY]); + #endregion + + // CheckWeldingRetry 이후 공통 흐름 + sb.Flows(sb[SEQ_CHECK_WELDDING_RETRY] + > sb[SEQ_GENERATE_WELDING_OBJECT] + > sb[SEQ_MOVE_OBJECT_LIST1_ACS] + > sb[SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ] + > sb[SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ] + > sb[SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ] + > sb[SEQ_MOVE_OBJECT_LIST2_ACS] + > sb[SEQ_SET_PARAMS_COMPONENT_TEACHINGRUN_SEQ2] + > sb[SEQ_DO_ID_COMPONENT_TEACHINGRUN_SEQ2] + > sb[SEQ_GET_RESULT_COMPONENT_TEACHINGRUN_SEQ2] + > sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ]); + + // 재시도: Object 생성 스킵하고 List1 이동으로 + sb.Flows(sb[SEQ_CHECK_WELDDING_RETRY] > sb[SEQ_MOVE_OBJECT_LIST1_ACS]); + + #region 26.04.10 NDY - PLC WeldComplete 분기 흐름 + // 실제 장비: 서브시퀀스 진입 → 결과 수신 → SaveResultData + sb.Flows(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] + > sb[SEQ_SAVE_RESULT_DATA]); + // 시뮬레이션: 서브시퀀스 스킵 → SkipNode → SaveResultData + sb.Flows(sb[SEQ_SET_PARAMS_PLC_WELD_COMPLETE_INTERFACE_SEQ] + > sb[SEQ_SKIP_PLC_WELD_COMPLETE_ACS] + > sb[SEQ_SAVE_RESULT_DATA]); + #endregion + + // 결과 저장 후 PLC 요청 대기로 루프 + sb.Flows(sb[SEQ_SAVE_RESULT_DATA] + > sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ]); + + // CheckInitCondition 실패 시 종료 + sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final); + + // 26.04.10 1사이클만 체크하고 빠져나오는 시퀸스로 테스트용 추가 Builder()에 추가 -- + sb.Flows(sb[SEQ_SET_PARAMS_PLC_PAGE_CHANGE_OR_WELD_REQ_INTERFACE_SEQ] + > sb[SEQ_COMPLETE]); + + sb.OnForceError(OnForceError); + sb.OnStopped(OnStopped); + + //sb.Draw("SeqAutoRunPrismaticScannerACS.pdf"); + + return sb; + } + + private string CheckWeldingRetry(SequenceContext context, string id, ICollection branches, SequenceActionResult param) + { + // 기존 SeqAutoRunPrismaticScanner와 동일 로직 + if (_IsWeldingRetry) return SEQ_MOVE_OBJECT_LIST1_ACS; + else return SEQ_GENERATE_WELDING_OBJECT; + } + + #region 시퀀스 함수 + + #region 1. 디바이스 및 변수 상태 확인 + private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param) + { + Log.I("SeqAutoRunPrismaticScannerACS Start"); + + _InitParam = param.Param as SeqAutoRunPrismaticScannerACS_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; + } + + _PageNo = _SequenceSetting.LastPageNo; + Log.I($"Previous Page No is {_PageNo}"); + + 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); + + #region 26.04.10 NDY - 시뮬레이션 환경 우회 + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip Laser/PLC CheckInitCondition"); + return SEQ_RESET_ENCODER_ACS; + } + #endregion + + // 레이저 레디 확인 (기존과 동일) + 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(); + _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true); + _PLC.WriteWord(eWordOutput.PC_PAGE_NO, (short)_PageNo); + + Log.I("Complete CheckInitCondition."); + return SEQ_RESET_ENCODER_ACS; + } + #endregion + + #region 26.04.09 NDY - MOTF Step 1 : 엔코더 리셋 + /// + /// [MOTF Step 1] + /// 자동운전 시작 시 Linear 원점 위치에서 InitFly2D() 호출 + /// → 스캐너 내부 X 엔코더 값을 0으로 초기화 + /// → 이후 SeqUploadWeldingList 내 WaitForEncoderX가 + /// RM(25,0) / LM(75,0) 시작점에서 정확히 트리거되도록 보장 + /// + private SequenceActionResult ResetEncoder(SequenceContext context, string id, SequenceActionResult param) + { + Log.I("MOTF Step1: InitFly2D - Encoder Reset at Linear Origin"); + _Scanner.InitFly2D(); + return SequenceAction.Ok(); + } + #endregion + + #region 2. PLC 페이지 변경 / 용접 요청 대기 + + #region 26.04.10 NDY - Action → Decision 변경 (PLC WeldRequest) + /// + /// 기존 Action에서 Decision으로 변경 + /// - 시뮬레이션: SEQ_CHECK_WELDDING_RETRY 로 직접 분기 (서브시퀀스 완전 스킵) + /// - 실제 장비: SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST 서브시퀀스로 진입 + /// + private string SetPlcPageChangeOrWeldRequest_SeqParams(SequenceContext context, string id, ICollection branches, SequenceActionResult param) + { + if (ApplicationExt.IsSimulation) + { + #region 26.04.10 NDY - 1사이클 테스트 + if (_IsOneCycleTest && !_FirstUpdateStep) + { + // 첫 사이클 이후 루프 진입 시 종료 + Log.I("Simulation Mode - 1 Cycle Test Complete, Stop Loop"); + return SEQ_COMPLETE; + } + #endregion + + Log.I("Simulation Mode - Skip PLC WeldRequest, proceed to CheckWeldingRetry"); + _PageNo = 1; + _FirstUpdateStep = false; + _TotalWeldingStep = 1; + return SEQ_CHECK_WELDDING_RETRY; + } + + // 기존 SeqAutoRunPrismaticScanner와 동일 + 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); + return SEQ_WAIT_CHANGE_PAGE_OR_WELD_REQUEST; + } + #endregion + + private SequenceActionResult GetPlcPageChangeOrWeldRequest_SeqParams(SequenceContext context, string id, SequenceActionResult param) + { + #region 26.04.10 NDY - 시뮬레이션 환경 우회 + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip PLC WeldRequest Result"); + return SequenceAction.Ok(); + } + #endregion + + // 기존 SeqAutoRunPrismaticScanner와 동일 + var resultOutParam = param.Param as SeqWaitWeldOrChangePage_OutParam; + _WeldReqTime = resultOutParam._WeldReqTime; + if (resultOutParam.Success == false) + { + Log.E("Failed Page Change & Weld Request Interface"); + return null; + } + + _PreviousTotalWeldingStep = _TotalWeldingStep; + _PageNo = resultOutParam.PLCPageNo; + _TotalWeldingStep = Convert.ToInt16(_PLC.ReadWord(eWordInput.PLC_STEP_NO).data); + + _SequenceSetting.LastPageNo = _PageNo; + context.Settings.Save(CommonSettingIds.Sequence, _SequenceSetting, "SeqAutoRunPrismaticScannerACS"); + + 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); + + 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. 용접 Object 생성 및 List 분리 + /// + /// [MOTF Step 2] + /// 레시피 레이어에서 Object를 꺼내 List1 / List2 로 분리 + /// - _SelectedDrawObjectList : 기존과 동일하게 전체 Object 보관 + /// - _SelectedDrawObjectList1 : RM 용접용 (Page1 끝~Page2 시작, Virtual Line 포함) + /// - _SelectedDrawObjectList2 : LM 용접용 (Page3 끝~Page4 시작, Virtual Line 포함) + /// TODO: 실제 분리 기준(ScannerParamID 또는 레이어 구성)은 이정인 선임 확인 후 확정 + /// + private SequenceActionResult GenerateWeldingObject(SequenceContext context, string id, SequenceActionResult param) + { + // 기존 _SelectedDrawObjectList 유지 (전체 Object) + _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[_PageNo - 1].Objects; + + // TODO: 실제 분리 기준 확인 후 구현 + // 현재는 절반으로 단순 분리 (임시) + int half = _SelectedDrawObjectList.Count / 2; + _SelectedDrawObjectList1 = _SelectedDrawObjectList.Take(half).ToList(); + _SelectedDrawObjectList2 = _SelectedDrawObjectList.Skip(half).ToList(); + + Log.I($"GenerateWeldingObject: Total({_SelectedDrawObjectList.Count}), List1({_SelectedDrawObjectList1.Count}), List2({_SelectedDrawObjectList2.Count})"); + return SequenceAction.Ok(); + } + #endregion + + #region 26.04.09 NDY - MOTF Step 3 : List1 오프셋 이동 + /// + /// [MOTF Step 3] + /// 1번 Object 리스트를 스캐너 중심으로부터 원점위치까지 떨어진 양만큼 오프셋 이동 + /// - 스캐너 중심 = (50, 0), Linear 원점 = (0, 0) → 오프셋 X = -50mm + /// - 이 오프셋은 SeqUploadWeldingList 내 CellMeasureCenterOffset으로 전달됨 + /// - WaitForEncoderX가 RM 용접 시작점(25, 0)에서 트리거되도록 준비 + /// TODO: ScannerConfig 또는 SystemParameter에서 스캐너 중심 좌표 가져오도록 수정 + /// + private SequenceActionResult MoveObjectList1(SequenceContext context, string id, SequenceActionResult param) + { + _ObjectList1OffsetX = -_Scanner.Config.MarkingOntheFlyOffsetX; + _ObjectList1OffsetY = -_Scanner.Config.MarkingOntheFlyOffsetY; + + Log.I($"MOTF Step3: MoveObjectList1 Offset({_ObjectList1OffsetX}, {_ObjectList1OffsetY})"); + return SequenceAction.Ok(); + } + #endregion + + #region 4. List1 용접 (RM) + private SequenceActionResult SetComponentTeachingrun_SeqParams(SequenceContext context, string id, SequenceActionResult param) + { + // 기존 SeqAutoRunPrismaticScanner와 동일 구조 + _PLC.WriteBit(eBitOutput.PC_WELD_BUSY, true); + Log.I("Weld Busy On ! Set ComponentTeachingrun (List1) 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; + } + + SeqTeachingRun_InitParam seqParam = new SeqTeachingRun_InitParam() + { + IsUseExternalObjects = true, + ExternalObjects = _SelectedDrawObjectList1, // List1 사용 + UseStream = currentModelInfo.equipmentSpecificModel.UseStream, + UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow, + IntervalTimeBlowOn = intervalTimeBlowOn, + IntervalTimeBlowOff = intervalTimeBlowOff, + NeedSetLaserTiming = true, + // MOTF 오프셋 전달 (기존 CellCenterOffset 파라미터 재활용) + CellCenterOffsetX = _ObjectList1OffsetX, + CellCenterOffsetY = _ObjectList1OffsetY, + }; + + Log.I("Set ComponentTeachingrun (List1) Complete!"); + return SequenceAction.Ok(seqParam); + } + + private SequenceActionResult GetComponentTeachingrun_SeqResult(SequenceContext context, string id, SequenceActionResult param) + { + var resultOutParam = param.Param as SeqTeachingRun_OutParam; + if (resultOutParam.Success == false) + { + Log.E("ComponentTeachingrun (List1) Failed."); + return null; + } + Log.I("ComponentTeachingrun (List1) Complete!"); + return SequenceAction.Ok(); + } + #endregion + + #region 26.04.09 NDY - MOTF Step 6 : List2 오프셋 이동 + /// + /// [MOTF Step 6] + /// 2번 Object 리스트를 스캐너 중심으로부터 원점위치까지 떨어진 양만큼 오프셋 이동 + /// - LM 용접 시작점(75, 0)에서 WaitForEncoderX가 트리거되도록 준비 + /// TODO: List2 기준 오프셋 수치 확인 후 수정 + /// + private SequenceActionResult MoveObjectList2(SequenceContext context, string id, SequenceActionResult param) + { + _ObjectList2OffsetX = -_Scanner.Config.MarkingOntheFlyOffsetX; + _ObjectList2OffsetY = -_Scanner.Config.MarkingOntheFlyOffsetY; + + Log.I($"MOTF Step6: MoveObjectList2 Offset({_ObjectList2OffsetX}, {_ObjectList2OffsetY})"); + return SequenceAction.Ok(); + } + #endregion + + #region 26.04.09 NDY - List2 용접 (LM) + private SequenceActionResult SetComponentTeachingrun_SeqParams2(SequenceContext context, string id, SequenceActionResult param) + { + // List1과 동일 구조, ExternalObjects와 Offset만 List2 기준으로 변경 + 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; + } + + SeqTeachingRun_InitParam seqParam = new SeqTeachingRun_InitParam() + { + IsUseExternalObjects = true, + ExternalObjects = _SelectedDrawObjectList2, // List2 사용 + UseStream = currentModelInfo.equipmentSpecificModel.UseStream, + UseBlow = currentModelInfo.equipmentSpecificModel.UseBlow, + IntervalTimeBlowOn = intervalTimeBlowOn, + IntervalTimeBlowOff = intervalTimeBlowOff, + NeedSetLaserTiming = true, + CellCenterOffsetX = _ObjectList2OffsetX, + CellCenterOffsetY = _ObjectList2OffsetY, + }; + + Log.I("Set ComponentTeachingrun (List2) Complete!"); + return SequenceAction.Ok(seqParam); + } + + private SequenceActionResult GetComponentTeachingrun_SeqResult2(SequenceContext context, string id, SequenceActionResult param) + { + var resultOutParam = param.Param as SeqTeachingRun_OutParam; + if (resultOutParam.Success == false) + { + Log.E("ComponentTeachingrun (List2) Failed."); + return null; + } + + // List2 용접까지 완료되면 WELD_BUSY OFF + _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(); + } + #endregion + + #region 5. 용접 완료 PLC 인터페이스 + + #region 26.04.10 NDY - Action → Decision 변경 (PLC WeldComplete) + /// + /// 기존 Action에서 Decision으로 변경 + /// - 시뮬레이션: SEQ_SKIP_PLC_WELD_COMPLETE_ACS 로 분기 (서브시퀀스 완전 스킵) + /// - 실제 장비: SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ 서브시퀀스로 진입 + /// + private string SetPlcWeldCompleteInterface_SeqParams(SequenceContext context, string id, ICollection branches, SequenceActionResult param) + { + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip PLC WeldComplete Interface"); + return SEQ_SKIP_PLC_WELD_COMPLETE_ACS; + } + + // 기존 SeqAutoRunPrismaticScanner와 동일 + Log.I("SeqCheckWeldingComplete Set Parameters"); + return SEQ_DO_PLC_WELD_COMPLETE_INTERFACE_SEQ; + } + + /// + /// 시뮬레이션 환경에서 PLC WeldComplete 서브시퀀스 스킵 시 호출 + /// 실제 장비에서는 이 노드를 타지 않음 + /// + private SequenceActionResult SkipPlcWeldComplete(SequenceContext context, string id, SequenceActionResult param) + { + Log.I("Simulation Mode - PLC WeldComplete Skipped, Update ProductCount"); + _CurrentWeldingStep = 0; + _IsWeldingRetry = false; + Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL); + UpdateProductCount(context); + UpdateProductInfoForView(); + return SequenceAction.Ok(); + } + #endregion + + private SequenceActionResult GetPlcWeldCompleteInterface_SeqParams(SequenceContext context, string id, SequenceActionResult param) + { + #region 26.04.10 NDY - 시뮬레이션 환경 우회 + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip PLC WeldComplete Result"); + return SequenceAction.Ok(); + } + #endregion + + // 기존 SeqAutoRunPrismaticScanner와 동일 + var resultOutParam = param.Param as SeqExecutePlcBitInterface_OutParam; + if (resultOutParam.Success == false) + { + Log.E("SeqCheckWeldingComplete Error;Success Failed."); + return null; + } + Log.I("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(); + } + #endregion + + #region 6. 결과 저장 및 종료 + private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param) + { + Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT); + 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); + SeqAutoRunPrismaticScannerACS_OutParam result = new SeqAutoRunPrismaticScannerACS_OutParam() { Success = seqResult }; + + if (seqResult == false) + AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_ResultFailed); + + Status.SetWorkingPosition(eWorkingPosition.DONE); + Log.I($"SeqAutoRunPrismaticScannerACS Complete. Result: {seqResult}"); // ← 추가 확인용 + return SequenceAction.Ok(result); + } + #endregion + + #endregion + + #region 구동 함수 + + private bool InitSequenceVariable(SequenceContext context) + { + var mainPage = CommonHelper.GetMainPage(); + _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching; + + // 기존과 동일 + _PageNo = 1; + _CurrentWeldingStep = 0; + _PreviousTotalWeldingStep = 0; + _TotalWeldingStep = 0; + _OffsetXfromModelFinder = 0; + _OffsetYfromModelFinder = 0; + _IsWeldingRetry = false; + _FirstUpdateStep = true; + _RetryCount = 0; + + #region 26.04.09 NDY - ACS 추가 변수 초기화 + _SelectedDrawObjectList1 = new List(); + _SelectedDrawObjectList2 = new List(); + _ObjectList1OffsetX = 0; + _ObjectList1OffsetY = 0; + _ObjectList2OffsetX = 0; + _ObjectList2OffsetY = 0; + #endregion + + return true; + } + + private void InitDev(SequenceContext context) + { + // 기존과 동일 + _Scanner = context.GetLaserScanner(); + _Laser = context.GetLaser(); + _PLC = context.GetPLC() as PLCControllerPrismaticScanner; + _Light = context.GetLight(); + + #region 26.04.09 NDY - ACS 장치 초기화 + // ScannerACS: WeldingCommon/Devices/ACS/ScannerACS.cs 에 등록된 장치 + // Stage X(AxisX2), Y1/Y2/Z 축 제어 및 엔코더 읽기 담당 + try + { + _ScannerACS = context.Devices[CommonDeviceIds.ACS] as ScannerACS; + } + catch (Exception ex) + { + Log.E($"ScannerACS Device Init Failed: {ex.Message}"); + } + #endregion + } + + private bool CheckDeviceReady(SequenceContext context) + { + #region 26.04.10 NDY - 시뮬레이션 환경 우회 + // 시뮬레이션 환경에서는 장치 활성화 체크 스킵 + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip Device Ready Check"); + return true; + } + #endregion + + Log.I("CheckDeviceReady Start"); + + // 기존과 동일 + List deviceListToCheck = new List() { + CommonDeviceIds.Scanner, + CommonDeviceIds.Laser, + CommonDeviceIds.PLCDevice + }; + + bool isAllDeviceActivated = true; + foreach (string item in deviceListToCheck) + { + bool isActivated = context.Devices[item].IsActivated; + Log.I($"Device: {item}, IsActivated: {isActivated}"); + if (!isActivated) + isAllDeviceActivated = false; + } + + #region 26.04.09 NDY - ScannerACS 장치 활성화 확인 + if (_ScannerACS == null || !_ScannerACS.IsActivated) + { + Log.E("ScannerACS is not activated."); + isAllDeviceActivated = false; + } + #endregion + + Log.I($"CheckDeviceReady Result: {isAllDeviceActivated}"); + return isAllDeviceActivated; + } + + private bool CheckCondition(SequenceContext context) + { + if (CheckCondition_Interlock()) + { + if (InitializeDeviceStatus(context)) + { + return true; + } + } + return false; + } + + private bool CheckCondition_Interlock() + { + // TODO: 필요한 인터락 조건 추가 + return true; + } + + private bool InitializeDeviceStatus(SequenceContext context) + { + return true; + } + + private void InitializePlcBits() + { + // 기존 SeqAutoRunPrismaticScanner와 동일 + _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); + } + + private bool RestoreDeviceStatus() + { + #region 26.04.10 NDY - 시뮬레이션 환경 우회 + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip RestoreDeviceStatus"); + Status.SetStatus(eEqpStatus.Manual); + return true; + } + #endregion + + if (_Laser.GetLaserReady()) _Laser.LaserOff(); + Status.SetStatus(eEqpStatus.Manual); + _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false); + InitializePlcBits(); + return true; + } + + private void UpdateProductCount(SequenceContext context) + { + // 기존 SeqAutoRunPrismaticScanner와 동일 + _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; + + context.Settings.Save(CommonSettingIds.SystemParameter, _SystemParameter, "SeqAutoRunPrismaticScannerACS"); + } + + private void UpdateProductInfoForView() + { + // 기존 SeqAutoRunPrismaticScanner와 동일 + 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 static void UnpackRecipes(List recipes) + { + // 기존 SeqAutoRunPrismaticScanner와 동일 + 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 OnForceError(SequenceContext arg1, string arg2) + { + OnSeqActionStop(); + Log.E("SeqAutoRunPrismaticScannerACS OnForceError"); + } + + private void OnStopped(SequenceContext obj) + { + OnSeqActionStop(); + Log.E("SeqAutoRunPrismaticScannerACS OnStopped"); + } + + private void OnSeqActionStop() + { + #region 26.04.10 NDY - 시뮬레이션 환경 우회 + if (ApplicationExt.IsSimulation) + { + Log.I("Simulation Mode - Skip OnSeqActionStop"); + Status.SetStatus(eEqpStatus.Manual); + return; + } + #endregion + + // 기존 SeqAutoRunPrismaticScanner와 동일 + if (_Laser.GetLaserReady()) _Laser.LaserOff(); + Status.SetStatus(eEqpStatus.Manual); + _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false); + InitializePlcBits(); + } + + #endregion + } + + public class SeqAutoRunPrismaticScannerACS_InitParam : InitParam + { + // 기존 SeqAutoRunPrismaticScanner_InitParam 패턴 유지 + // 추후 MOTF 관련 파라미터 추가 가능 + } + + public class SeqAutoRunPrismaticScannerACS_OutParam : OutParam + { + } +} \ No newline at end of file diff --git a/Machines/ILSOS_PrismaticScanner/Sequences/SequenceManager.cs b/Machines/ILSOS_PrismaticScanner/Sequences/SequenceManager.cs index c80f470..704db3b 100644 --- a/Machines/ILSOS_PrismaticScanner/Sequences/SequenceManager.cs +++ b/Machines/ILSOS_PrismaticScanner/Sequences/SequenceManager.cs @@ -38,6 +38,12 @@ namespace ILSOS_PrismaticScanner.Sequences SeqTeachingRun _SeqTeachingRun; SeqComponentTeachingRun _SeqComponentTeachingRun; + #region 26.04.09 NDY - ACS MOTF AutoRun Sequence + // 기존 SeqAutoRunPrismaticScanner와 별개로 추가 + // ACS 모션(Stage X 등) + 갈바노 스캐너 MOTF 자동운전 전용 + SeqAutoRunPrismaticScannerACS _SeqAutoRunPrismaticScannerACS; + #endregion + private static Dictionary mapSequenceBuilders = new Dictionary(); static SequenceManager() @@ -56,12 +62,17 @@ namespace ILSOS_PrismaticScanner.Sequences private void ChangeRunState(ISequenceEntry sequenceEntry, string sequenceId, bool isRunning, object param = null) { - if (isRunning) { - if (sequenceEntry[sequenceId].RunState != SequenceRunState.Running) { + if (isRunning) + { + if (sequenceEntry[sequenceId].RunState != SequenceRunState.Running) + { sequenceEntry[sequenceId].Start(param); } - } else { - if (sequenceEntry[sequenceId].RunState == SequenceRunState.Running) { + } + else + { + if (sequenceEntry[sequenceId].RunState == SequenceRunState.Running) + { sequenceEntry[sequenceId].Stop(); } } @@ -84,66 +95,87 @@ namespace ILSOS_PrismaticScanner.Sequences //register(CommonSequenceIds.DeviceMonitor, DeviceMonitoring.Builder()); base.Register(aurora, register); - if (_SeqMarkGrid == null) { + if (_SeqMarkGrid == null) + { _SeqMarkGrid = new SeqMarkGrid(); } register(CommonSequenceIds.SeqMarkGrid, _SeqMarkGrid.Builder()); - if (_SeqScannerCalibration == null) { + if (_SeqScannerCalibration == null) + { _SeqScannerCalibration = new SeqScannerCalibration(); } register(CommonSequenceIds.SeqScannerCalibration, _SeqScannerCalibration.Builder()); - if (_SeqReceiveCellInfo == null) { + if (_SeqReceiveCellInfo == null) + { _SeqReceiveCellInfo = new SeqReceiveCellInfo(); } register(CommonSequenceIds.SeqReceiveCellInfo, _SeqReceiveCellInfo.Builder()); - if (_SeqReportCellResult == null) { + if (_SeqReportCellResult == null) + { _SeqReportCellResult = new SeqReportCellResult(); } register(CommonSequenceIds.SeqReportCellResult, _SeqReportCellResult.Builder()); - if (_SeqScoutFlyingVision == null) { + if (_SeqScoutFlyingVision == null) + { _SeqScoutFlyingVision = new SeqScoutFlyingVision(); } register(CommonSequenceIds.SeqScoutFlyingVision, _SeqScoutFlyingVision.Builder()); - if (_SeqScoutNormal == null) { + if (_SeqScoutNormal == null) + { _SeqScoutNormal = new SeqScoutNormal(); } register(CommonSequenceIds.SeqScoutNormal, _SeqScoutNormal.Builder()); - - if (_SeqExecutePlcBitInterface == null) { + if (_SeqExecutePlcBitInterface == null) + { _SeqExecutePlcBitInterface = new SeqExecutePlcBitInterface(); } register(CommonSequenceIds.SeqExecutePlcBitInterface, _SeqExecutePlcBitInterface.Builder()); - if (_SeqExecutePlcBitInterfaceForStatusChange == null) { + if (_SeqExecutePlcBitInterfaceForStatusChange == null) + { _SeqExecutePlcBitInterfaceForStatusChange = new SeqExecutePlcBitInterface(); } register(CommonSequenceIds.SeqExecutePlcBitInterfaceForStatusChange, _SeqExecutePlcBitInterfaceForStatusChange.Builder()); - if (_SeqCheckWeldingComplete == null) { + if (_SeqCheckWeldingComplete == null) + { _SeqCheckWeldingComplete = new SeqCheckWeldingComplete(); } register(CommonSequenceIds.SeqCheckWeldingComplete, _SeqCheckWeldingComplete.Builder()); - if (_SeqTeachingRun == null) { + if (_SeqTeachingRun == null) + { _SeqTeachingRun = new SeqTeachingRun(); } register(CommonSequenceIds.SeqTeachingRun, _SeqTeachingRun.Builder()); - if (_SeqComponentTeachingRun == null) { + if (_SeqComponentTeachingRun == null) + { _SeqComponentTeachingRun = new SeqComponentTeachingRun(); } register(CommonSequenceIds.SeqComponentTeachingRun, _SeqComponentTeachingRun.Builder()); - if (_SeqWaitWeldOrChangePage == null) { + if (_SeqWaitWeldOrChangePage == null) + { _SeqWaitWeldOrChangePage = new SeqWaitWeldOrChangePage(); } register(CommonSequenceIds.SeqWaitWeldOrChangePage, _SeqWaitWeldOrChangePage.Builder()); + + #region 26.04.09 NDY - ACS MOTF AutoRun Sequence 등록 + // 기존 SeqAutoRun(SeqAutoRunPrismaticScanner)은 건드리지 않고 + // ACS MOTF 전용 시퀀스를 별도 ID(SeqAutoRunACS)로 등록 + if (_SeqAutoRunPrismaticScannerACS == null) + { + _SeqAutoRunPrismaticScannerACS = new SeqAutoRunPrismaticScannerACS(); + } + register(SequenceIds.SeqAutoRunACS, _SeqAutoRunPrismaticScannerACS.Builder()); + #endregion } // MainProcess Sequence @@ -161,9 +193,13 @@ namespace ILSOS_PrismaticScanner.Sequences public static SequenceBuilder GetSeqBuilder_SeqTeachingRun() { return Instance._SeqTeachingRun.Builder(); } public static SequenceBuilder GetSeqBuilder_SeqComponentTeachingRun() { return Instance._SeqComponentTeachingRun.Builder(); } + #region 26.04.09 NDY - ACS MOTF AutoRun Sequence GetSeqBuilder + public static SequenceBuilder GetSeqBuilder_SeqAutoRunPrismaticScannerACS() { return Instance._SeqAutoRunPrismaticScannerACS.Builder(); } + #endregion + public virtual void Terminate() { } } -} +} \ No newline at end of file diff --git a/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRun.xaml b/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRun.xaml index 7ccb906..28746e8 100644 --- a/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRun.xaml +++ b/Machines/ILSOS_PrismaticScanner/Views/Main/UCMainAutoRun.xaml @@ -53,13 +53,35 @@ - + + + + + + -