From e61e99f9ab8a87a71d095e20f9b99b406af8f988 Mon Sep 17 00:00:00 2001 From: to3796zw Date: Sun, 21 Jun 2026 20:15:43 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20bit15=20=EB=B3=B4=EA=B0=95=C2=B7?= =?UTF-8?q?=EC=96=91=EA=B7=B9=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=C2=B7=EB=A1=9C=EA=B7=B8=20=EA=B0=9C=ED=8E=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - D70002(PC_RECIPE_NO): 동일 레시피여도 매 핸드셰이크 에코 유지 - 정지 시 에코워드(Page/Recipe No) 0 초기화 제거(보존) - bit15 Manual 전환 시 레시피 활성화+로드, bit15 ON 중 AUTO_REQ 자동시작 무시 - [AutoRun Resume] PC bit15(0x0F) 1초 펄스 OFF - 양극(P) 미사용: PC_AUTO_STATUS_P/양극 용접 핸드셰이크 제거, 용접 응답 음극 통일 - 공유 eCommon READY(0x0F) 제거(PTOP enum은 유지) - SeqAutoRunPTOP 로그 설명형 개편(노드 Start/End 제거) - 빌드 0오류. Recipe/Page 수정분은 현장 미적용(추후 검증 예정) Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Sequences/SeqAutoRunPTOP.cs | 185 ++++++------------ .../Views/Main/UCMainAutoRunViewModel.cs | 46 ++++- .../Devices/PLC/PLCController.Address.cs | 6 +- docs/작업일지/2026_06_21.md | 55 ++++++ 4 files changed, 149 insertions(+), 143 deletions(-) diff --git a/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs b/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs index 554dc58..f3f7d5b 100644 --- a/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs +++ b/Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs @@ -184,7 +184,7 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences private bool InitSequenceVariable(SequenceContext context) { - Log.I($"[InitSequenceVariable] 노드 Start"); + Log.I($"[InitSequenceVariable] 캔버스 핸들 확보 + 용접대상 오브젝트 선택 시작"); var mainPage = CommonHelper.GetMainPage(); _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlTeaching; @@ -204,7 +204,7 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences //Status.GetProcessingWeldingData().WeldingScannerListNo = _WeldingScannerListNo; //Status.GetProcessingWeldingData().SelectedDrawObjectList = _SelectedObjectList; - Log.I($"[InitSequenceVariable] 노드 End"); + Log.I($"[InitSequenceVariable] 변수 초기화 완료 (선택 오브젝트 {_SelectedObjectList.Count()}개)"); return true; } @@ -250,8 +250,7 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences // 장비 체크 InitDev(context); - _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true); // 음극 - _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS_P, true); //양극 + _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true); // 음극 (양극 P 미사용) #region [TEST_BYPASS] Alarm Check - DeFvice Ready /* if (!CheckDeviceReady(context)) @@ -321,11 +320,13 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences return SequenceAction.Ok(); } - // 이미 활성 레시피면 재활성화·워드 재기록 생략 (매 페이지마다 PC_RECIPE_NO 워드 변경 금지) + // 이미 활성 레시피면 재활성화·Model reload는 생략하되, PC_RECIPE_NO 에코는 반드시 유지. + // (정지 시 OnSeqActionStop이 D70002=0으로 지우므로, 동일 레시피 재진입에서 에코를 건너뛰면 PLC가 0/이전값을 봄) var preActivated = _RecipeManager.ActivatedRecipe(); if (preActivated != null && preActivated.Id == recipe.Id) { - Log.I($"[CheckRecipeNoComp] 이미 활성 레시피({recipe.Id}) → 재활성화 생략 → CheckPageNoComp 이동"); + _PLC.WriteWord(eWordOutput.PC_RECIPE_NO, plcRecipeNo); + Log.I($"[CheckRecipeNoComp] 이미 활성 레시피({recipe.Id}) → 재활성화 생략, PC_RECIPE_NO 에코({plcRecipeNo}) 유지 → CheckPageNoComp 이동"); return SequenceAction.Ok(); } @@ -515,9 +516,8 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences } private bool LaserReadyResponse() { - Log.I($"[LaserReadyResponse] 노드 Start"); + Log.I($"[LaserReadyResponse] 레이저 Ready 응답 (현재 no-op)"); //if (ApplicationExt.IsSimulation) - Log.I($"[LaserReadyResponse] 노드 End"); return true; } private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param) @@ -648,38 +648,13 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences Log.I($"[---> WaitWeldingStart] 용접 준비 신호 송신 - PageNo: [{plcPageNo}] CurrentPage: {_CurrentPageNo + 1})"); Status.SetWorkingPosition(eWorkingPosition.WAITING_WELD_START); - if (plcPageNo == 1) - { - // 2. 준비 완료(Ack) 및 가동 중(Busy) 비트 ON - _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, true); - _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, true); - Thread.Sleep(200); - _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, false); - Thread.Sleep(5); - - Log.I($"[WaitWeldingStart] WriteBit Anode Welding Ready ACK TRUE"); - Log.I($"[WaitWeldingStart] WriteBit Anode Welding Busy TRUE"); - Log.I($"[WaitWeldingStart] WriteBit Anode Welding Comp False"); - //Thread.Sleep(10); - } - else if (plcPageNo == 2) - { - // 2. 준비 완료(Ack) 및 가동 중(Busy) 비트 ON - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_READY_REQ, true); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_BUSY, true); - Thread.Sleep(200); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_COMP_ACK, false); - Thread.Sleep(5); - - Log.I($"[WaitWeldingStart] WriteBit Cathode Welding Ready REQ TRUE"); - Log.I($"[WaitWeldingStart] WriteBit Cathode Welding Busy TRUE"); - Log.I($"[WaitWeldingStart] WriteBit Cathode Welding Comp False"); - //Thread.Sleep(10); - } - else // PAGE NUM = 0 - { - Log.I($"[WaitWeldingStart] Page num 1,2 etc.."); - } + // 양극(P) 미사용 → 모든 페이지 음극 용접 비트로 응답 (PLC 트리거가 음극 PLC_WELD_TRIGGER_REQ라 정합) + _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, true); + _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, true); + Thread.Sleep(200); + _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, false); + Thread.Sleep(5); + Log.I($"[WaitWeldingStart] Welding READY_ACK=ON / BUSY=ON / COMPLETE=OFF 송신 (PageNo:{plcPageNo})"); // 디버그 끝내고 삭제 //MessageUtil.ShowToast("WaitWeldingStart -> WriteBit True"); @@ -789,38 +764,16 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences Log.D(string.Format($"[ExecuteWelding] Change SelectCorrectionTable Time : {_WeldingSpanTime.TotalMilliseconds}")); - if (plcPageNo == 1) - { - // 용접끝 - _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false); - Log.I("[PLCInterface] PageNo[1] READY_ACK false"); - Thread.Sleep(5); - // 용접중 신호로 변경 - _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false); - Log.I("[PLCInterface] PageNo[1] BUSY false"); - Thread.Sleep(5); - _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, true); - Log.I("[PLCInterface] PageNo[1] COMPLETE true"); - Thread.Sleep(5); - } - else if (plcPageNo == 2) - { - // 용접끝 - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_READY_REQ, false); - Log.I("[PLCInterface] PageNo[2] READY_REQ false"); - Thread.Sleep(5); - // 용접중 신호로 변경 - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_BUSY, false); - Log.I("[PLCInterface] PageNo[2] BUSY false"); - Thread.Sleep(5); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_COMP_ACK, true); - Log.I("[PLCInterface] PageNo[2] COMP_ACK true"); - Thread.Sleep(5); - } - else - { - - } + // 양극(P) 미사용 → 모든 페이지 음극 완료 비트로 응답 + _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false); + Log.I($"[ExecuteWelding] 용접완료 송신 - READY_ACK=false (PageNo:{plcPageNo})"); + Thread.Sleep(5); + _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false); + Log.I($"[ExecuteWelding] 용접완료 송신 - BUSY=false (PageNo:{plcPageNo})"); + Thread.Sleep(5); + _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, true); + Log.I($"[ExecuteWelding] 용접완료 송신 - COMPLETE=true (PageNo:{plcPageNo})"); + Thread.Sleep(5); Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL); #endregion @@ -867,17 +820,17 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences // 실제 동작 _Scanner에서 private void SaveScannerRecordFileForamt() { - Log.I($"[SaveScannerRecordFileForamt] 노드 Start"); + Log.I($"[SaveScannerRecordFileForamt] 스캐너 파형 기록 저장 시작 (SaveRecord)"); // ── 용접 완료 후 파형 데이터 저장 ──────────────────────────── _Scanner.SaveRecord("AutoRunTDC"); - Log.I("[SaveResultData] SaveRecord Complete"); + Log.I("[SaveScannerRecordFileForamt] SaveRecord 완료"); _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND); _Scanner.SelectCorrectionTable(1); - Log.D("[SaveResultData] JumpAbs + SelectCorrectionTable Complete"); + Log.D("[SaveScannerRecordFileForamt] JumpAbs(0,0) + CorrectionTable(1) 복귀 완료"); - //Thread.Sleep(1000); // 추후 제거 - Log.I($"[SaveScannerRecordFileForamt] 노드 End"); + //Thread.Sleep(1000); // 추후 제거 + Log.I($"[SaveScannerRecordFileForamt] 스캐너 파형 기록 저장 완료"); } // 이곳에서 bit 초기화 진행함. 260512 private SequenceActionResult WriteCompletionPLCBit(SequenceContext context, string id, SequenceActionResult param) @@ -934,7 +887,7 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences private bool RestoreDeviceStatus() { - Log.I($"[RestoreDeviceStatus] 노드 Start"); + Log.I($"[RestoreDeviceStatus] PLC 출력 비트 리셋 시작 (Page/Weld 음극)"); ////PLC All BIT OFF _PLC.WriteBit((int)eCommonBitOutput.PC_PAGE_CHANGE_COMPLETE, false); // 페이지 false @@ -943,29 +896,26 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences _PLC.WriteBit((int)eCommonBitOutput.PC_WELD_COMPLETE, false); // 용접 완료 false Log.I(string.Format("[PLCInterface]PC Anod Bit Off")); - _PLC.WriteBit((int)eCommonBitOutput.PC_WELD_P_READY_REQ, false); // 용접 준비 false - _PLC.WriteBit((int)eCommonBitOutput.PC_WELD_P_BUSY, false); // 동작 중 false - _PLC.WriteBit((int)eCommonBitOutput.PC_WELD_P_COMP_ACK, false); // 용접 완료 false - Log.I(string.Format("[PLCInterface]PC Cathode Bit Off")); + // 양극(P) 미사용 — 양극 용접 비트 리셋 제거 - //Thread.Sleep(1000); // 추후 제거 - Log.I($"[RestoreDeviceStatus] 노드 End"); + //Thread.Sleep(1000); // 추후 제거 + Log.I($"[RestoreDeviceStatus] PLC 출력 비트 리셋 완료"); return true; } // 장비 3개 가져오기 완료 private void InitDev(SequenceContext context) { - Log.I($"[InitDev] 노드 Start"); + Log.I($"[InitDev] 디바이스 핸들 확보 시작 (Scanner/PLC/IO)"); _Scanner = context.GetLaserScanner(); _PLC = context.GetPLC() as PLCControllerPTOP; _IO = context.GetIOUnit(); - Log.I($"[InitDev] 노드 End"); + Log.I($"[InitDev] 디바이스 핸들 확보 완료"); } private bool CheckDeviceReady(SequenceContext context) { - Log.I($"[CheckDeviceReady] 노드 Start"); + Log.I($"[CheckDeviceReady] 디바이스 활성 상태 확인 시작 (Scanner/Laser/PLC/IO)"); List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.Laser, CommonDeviceIds.PLCDevice, CommonDeviceIds.IODevice }; bool isAllDeviceActivated = true; @@ -978,38 +928,37 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences } - Log.I($"[CheckDeviceReady] 노드 End"); + Log.I($"[CheckDeviceReady] 디바이스 활성 상태 확인 완료 (AllActive={isAllDeviceActivated})"); return isAllDeviceActivated; } private bool CheckCondition(SequenceContext context) { - Log.I($"[CheckCondition] 노드 Start"); - // 체크 신호 plc + Log.I($"[CheckCondition] 시작 조건 확인 (인터락 + 디바이스 초기상태)"); + // 체크 신호 plc if (CheckCondition_Interlock()) { if (InitializeDeviceStatus(context)) { - Log.I($"[CheckCondition] 노드 End"); + Log.I($"[CheckCondition] 조건 충족 → 시작 가능"); return true; } } - Log.I($"[CheckCondition] 노드 End"); + Log.I($"[CheckCondition] 조건 불충족 → 시작 불가"); return false; } private bool CheckCondition_Interlock() { - Log.I($"[CheckCondition_Interlock] 노드 Start"); // PLC, Data, IO 등 Interlock checking - Log.I($"[CheckCondition_Interlock] 노드 End"); + Log.I($"[CheckCondition_Interlock] 인터락 확인 (현재 항상 통과)"); return true; } private bool InitializeDeviceStatus(SequenceContext context) { - Log.I($"[InitializeDeviceStatus] 노드 Start"); + Log.I($"[InitializeDeviceStatus] 디바이스 초기상태 설정 시작 (AutoRun 비트 + LBAD Ready)"); // PLC Interface AutoStatus Start InitAutoRunBit(); @@ -1043,15 +992,14 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences _IO._iOUnit.WriteDoBit(Convert.ToInt32(eADOAddress.PCLBADReadyStatus), true); Log.I(string.Format($"[IOInterface]SET PCLBADReadyStatus true")); - Log.I($"[InitializeDeviceStatus] 노드 End"); + Log.I($"[InitializeDeviceStatus] 디바이스 초기상태 설정 완료"); return true; } private void InitAutoRunBit() { - Log.I($"[InitAutoRunBit] 노드 Start"); - Log.I(string.Format($"[PLCInterface] Start AutoRun PLC Bit Initialize")); + Log.I($"[InitAutoRunBit] AutoRun 시작 PLC 비트 초기화 시작 (음극)"); _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false); Log.I(string.Format($"[PLCInterface]SET (-)PC_WELD_TRIGGER_ACK False")); _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false); @@ -1061,35 +1009,24 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS, true); Log.I(string.Format($"[PLCInterface]SET (-)PC_AUTO_STATUS True")); - Log.I(string.Format($"[PLCInterface] Start AutoRun PLC Bit Initialize")); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_READY_REQ, false); - Log.I(string.Format($"[PLCInterface]SET (+)PC_WELD_TRIGGER_ACK False")); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_BUSY, false); - Log.I(string.Format($"[PLCInterface]SET (+)PC_WELD_BUSY False")); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_COMP_ACK, false); - Log.I(string.Format($"[PLCInterface]SET (+)PC_WELD_COMPLETE False")); - _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS_P, true); - Log.I(string.Format($"[PLCInterface]SET (+)PC_AUTO_STATUS True")); - - Log.I($"[InitAutoRunBit] 노드 End"); + // 양극(P) 미사용 — 양극 초기화 비트 제거 + Log.I($"[InitAutoRunBit] AutoRun 시작 PLC 비트 초기화 완료"); } private void OnForceError(SequenceContext arg1, string arg2) { - Log.I($"[OnForceError] 노드 Start"); + Log.I($"[OnForceError] ForceError 발생 → 장비 정지 처리 시작"); OnSeqActionStop(); - Log.I("SequenceAction Ok : OnForceError"); - Log.I($"[OnForceError] 노드 End"); + Log.I($"[OnForceError] ForceError 정지 처리 완료"); } private void OnStopped(SequenceContext obj) { - Log.I($"[OnStopped] 노드 Start"); + Log.I($"[OnStopped] 시퀀스 Stop 수신 → 장비 정지 처리 시작"); OnSeqActionStop(); - Log.I("SequenceAction Ok : OnStopped"); - Log.I($"[OnStopped] 노드 End"); + Log.I($"[OnStopped] Stop 정지 처리 완료"); } private void OnSeqActionStop() @@ -1100,27 +1037,15 @@ namespace ILSOS_PrismaticRD_PTOP.Sequences _PLC.WriteBit((int)eBitOutput.PC_WELD_TRIGGER_ACK, false); _PLC.WriteBit((int)eBitOutput.PC_WELD_BUSY, false); _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE, false); - Log.I("[OnSeqActionStop] PLC ANODE(-) Output All false"); - - - - _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS_P, false); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_READY_REQ, false); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_COMP_ACK, false); - _PLC.WriteBit((int)eBitOutput.PC_WELD_P_BUSY, false); - Log.I("[OnSeqActionStop] PLC Cathod(+) Output All false"); + Log.I("[OnSeqActionStop] PLC 음극 Output All false (양극 P 미사용 — 양극 비트 제거)"); _PLC.WriteBit((int)eBitOutput.PC_PAGE_CHANGE_COMPLETE, false); Log.I("[OnSeqActionStop] PLC IO Output PAGECHANGE Init"); // 260512 워드 _PLC.WriteDWord((int)eWordOutput.PC_ERROR_CODE, 0); - // 260621: Resume 정지면 에코 워드(Page/Recipe No) 보존 — PLC가 정합 확인에 쓰므로 0으로 안 지움 - if (!ResumeMode) - { - _PLC.WriteDWord((int)eWordOutput.PC_PAGE_NO, 0); - _PLC.WriteDWord((int)eWordOutput.PC_RECIPE_NO, 0); // 260618 레시피 No init - } + // 260621: 에코 워드(PC_PAGE_NO/PC_RECIPE_NO)는 정지 시 0으로 지우지 않음 — PLC가 정합 확인에 쓰는 + // 마지막 핸드셰이크 값을 보존(다음 사이클에 어차피 재에코). 기존 0 초기화 2줄 제거. Log.I("[OnSeqActionStop] PLC IO Output Word Init"); diff --git a/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs b/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs index 9aebcf6..36250ac 100644 --- a/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs +++ b/Machines/ILSOS_PrismaticRD_PTOP/Views/Main/UCMainAutoRunViewModel.cs @@ -233,10 +233,24 @@ namespace ILSOS_PrismaticRD_PTOP.Views string msg = $"Recipe No : {recipeNo}\nPage No : {pageNo}\n\n용접 위치 확인해주세요."; Aurora.Api.Control.MessageDialog.Show(Application.Current.MainWindow, "용접 위치 확인", msg); - // [확인] 후 Auto → Manual (레시피는 이미 활성 → 재활성/롤백 안 함). 작업자가 가변 용접 직접 진행. + // [확인] 후 Auto → Manual. 작업자가 가변 용접 직접 진행. Status.SetStatus(eEqpStatus.Manual); _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS, false); - _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS_P, false); + + // PLC 지정 레시피를 활성화 + 티칭/캔버스 로드 → Manual에서 해당 Recipe/Page로 이동·수동용접 가능. + // (미등록/0이면 현재 활성 레시피로 폴백. OnMonitoring=UI 스레드라 OnActiveReloaded 직접 호출 OK) + var recipe = _RecipeManager.Recipes().FirstOrDefault(x => x.Id == recipeNo) + ?? _RecipeManager.ActivatedRecipe(); + if (recipe != null) + { + _RecipeManager.ActivateRecipe(recipe.Id, "Bit15 Manual Weld"); + RecipeHelper.OnActiveReloaded(); + Log.I($"[Bit15] Recipe 활성화+로드 (Id:{recipe.Id}) → Manual 이동 가능"); + } + else + { + Log.W($"[Bit15] 활성화할 Recipe 없음 (PLC RecipeNo:{recipeNo}, 활성 레시피도 null)"); + } Log.I($"[Bit15] ModeChange → 정지(보존)·Manual 전환 (Recipe:{recipeNo}, Page:{pageNo})"); } @@ -252,11 +266,19 @@ namespace ILSOS_PrismaticRD_PTOP.Views MessageUtil.ShowWarningToast("Resume 상태가 아닙니다."); return; } - // PC→PLC bit15(Weld_01 ReadyACK) ON — resume run 신호 (한 사이클 완료 후 시퀀스가 OFF/해제) + // PC→PLC bit15(Weld_01 ReadyACK) ON — resume run 신호. 1초 펄스 후 자동 OFF(아래 타이머). _Plc.WriteBit(eBitOutput.PC_WELD_READY_ACK, true); + // 1초 후 PC bit15 OFF (one-shot DispatcherTimer) + var bit15OffTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) }; + bit15OffTimer.Tick += (s, ev) => + { + bit15OffTimer.Stop(); + _Plc.WriteBit(eBitOutput.PC_WELD_READY_ACK, false); + Log.I("[Bit15] PC_WELD_READY_ACK OFF (1초 펄스)"); + }; + bit15OffTimer.Start(); // Auto 복귀 (ResumeMode=true → CheckInitCondition에서 핸드셰이크 스킵하고 업로드→용접) _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS, true); - _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS_P, true); Status.GetProcessingWeldingData().AutoRunStartTime = DateTime.Now; RunSequenceAutoRun(); Log.I("[Bit15] AutoRun Resume → PC_WELD_READY_ACK ON + Auto 이어하기(핸드셰이크 스킵)"); @@ -265,6 +287,15 @@ namespace ILSOS_PrismaticRD_PTOP.Views // PLC 비트에 의한 AutoRun 실행의 경우, PLC 인터페이스 이후 AutoRun 필요 public override async void OnAutoRunStartRequest(object obj = null) { + // PLC bit15(MODECHANGE)가 ON인 동안에는 PLC_AUTO_REQ 자동시작 신호를 무시. + // (bit15 Manual 전환 중 AUTO_REQ rising edge가 AutoRun을 띄워 Manual을 덮어쓰는 현상 차단. + // 이어하기는 전용 [AutoRun Resume] 버튼(OnAutoRunResume) 경로라 이 가드와 무관.) + if (_Plc != null && !_Plc.IsSimul && _Plc.ReadBit(eBitInput.PLC_WELD_READY_REQ).data) + { + Log.I("[Bit15] bit15 ON 중 → AUTO_REQ 자동시작 무시 (Manual 유지)"); + return; + } + if (!ApproveAutorunByUserOrAuto()) { return; } SeqAutoRunPTOP.ResumeMode = false; // 일반 시작은 항상 핸드셰이크 포함 (resume 아님) @@ -275,15 +306,13 @@ namespace ILSOS_PrismaticRD_PTOP.Views if (!IsStart) { _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS, false); - _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS_P, false); //양극 return; } #endregion - _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS, true); // 음극 - _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS_P, true); //양극 + _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS, true); // 음극 (양극 P 미사용) - // PLC에선 Auto Req 받으면 바로 살려주도록 + // PLC에선 Auto Req 받으면 바로 살려주도록 Status.GetProcessingWeldingData().AutoRunStartTime = DateTime.Now; RunSequenceAutoRun(); @@ -408,7 +437,6 @@ namespace ILSOS_PrismaticRD_PTOP.Views Status.SetStatus(eEqpStatus.Manual); _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS, false); - _Plc.WriteBit(eBitOutput.PC_AUTO_STATUS_P, false); //Recipe를 기존에 선택된 Recipe로 Rollback 해 줌 RecipeHelper.RollbackRecipe(); diff --git a/WeldingComponents/WeldingCommon/Devices/PLC/PLCController.Address.cs b/WeldingComponents/WeldingCommon/Devices/PLC/PLCController.Address.cs index 1d22fdb..082dd2b 100644 --- a/WeldingComponents/WeldingCommon/Devices/PLC/PLCController.Address.cs +++ b/WeldingComponents/WeldingCommon/Devices/PLC/PLCController.Address.cs @@ -230,8 +230,7 @@ namespace WeldingCommon.Devices PLC_PAGE_CHANGE_REQ = 0x08, PLC_RMS_USED = 0x09, - // 0x0A ~ 0x0E - PLC_WELD_READY_REQ = 0x0F, // 260621: Weld_01 Ready (PTOP 동기화) + // 0x0A ~ 0x0F PLC_WELD_TRIGGER_REQ = 0x10, PLC_WELD_WAIT = 0x11, PLC_WELD_COMPLETE_ACK = 0x12, @@ -267,8 +266,7 @@ namespace WeldingCommon.Devices PC_EMO_STATUS = 0x07, PC_PAGE_CHANGE_COMPLETE = 0x08, PC_RECIPE_CHANGE_COMPLETE = 0x09, // 260618: PTOP enum과 동기화 (RestoreDeviceStatus가 common enum 사용) - // 0x0A ~ 0x0E - PC_WELD_READY_ACK = 0x0F, // 260621: Weld_01 ReadyACK (PTOP 동기화) + // 0x0A ~ 0x0F PC_WELD_TRIGGER_ACK = 0x10, PC_WELD_BUSY = 0x11, PC_WELD_COMPLETE = 0x12, diff --git a/docs/작업일지/2026_06_21.md b/docs/작업일지/2026_06_21.md index 1de5707..5e3eaa2 100644 --- a/docs/작업일지/2026_06_21.md +++ b/docs/작업일지/2026_06_21.md @@ -59,3 +59,58 @@ - 21~24 페이지 수 미확정(코드는 `Layers.Count()` 가변 대응). - 알람 타이밍: `device.Activate()`가 연결완료까지 동기인지/개시만인지 디바이스별 확인. - 제품 1개 = Recipe×Page 사이클 예상 시나리오: `docs/분석/2026/06/21/121750_제품1개_recipe_page_사이클_예상시나리오.md`. + +## 8. 현장 피드백 반영 SW 수정 (5건, 빌드 0오류·미커밋) + +> 모두 MSBuild Debug|x64 EXITCODE=0 확인. 현장 런타임 검증 대기. + +1. **D70002(PC_RECIPE_NO) 에코 누락 수정** — `SeqAutoRunPTOP.cs` `CheckRecipeNoComp` + - 증상: Page CHG Request 수신·레시피는 그대로인데 `PC_RECIPE_NO`(D70002)가 안 바뀜. + - 원인: "이미 활성 레시피" 분기가 에코 없이 조기 return → 정지 시 0으로 지워진 값이 그대로 남음(PageNo는 매번 에코라 비대칭). + - 조치: 동일 레시피여도 `WriteWord(PC_RECIPE_NO, plcRecipeNo)` 에코 유지(재활성/reload는 계속 생략). + +2. **bit15 Manual 전환 시 레시피 활성화+로드 추가** — `UCMainAutoRunViewModel.cs` `ShowRecipePageConfirmAndSwitchManual` + - 증상: bit15 팝업 후 Manual로 가도 레시피 비활성화되어 해당 Recipe/Page로 이동·수동용접 불가. + - 원인: "레시피 이미 활성" 가정으로 ActivateRecipe/OnActiveReloaded 생략 → 티칭/캔버스(SelectedRecipe)가 PLC 레시피로 로드 안 됨. + - 조치: `ActivateRecipe(recipe.Id)` + `RecipeHelper.OnActiveReloaded()` 추가(미등록/0이면 현재 활성 레시피 폴백). + +3. **bit15 ON 중 AUTO_REQ 자동시작 무시** — `UCMainAutoRunViewModel.cs` `OnAutoRunStartRequest` + - 증상: bit15 살아있을 때 Recipe/Page 팝업과 "자동운전 시작?" 팝업이 동시에 뜨고, Manual이 아닌 Auto로 복귀. + - 원인: AutoRun 팝업은 `PLC_AUTO_REQ`(0x02) rising edge로 트리거(bit15와 별개·무가드), TimerMessageBox default=true(5초 자동확인)가 Manual을 덮어씀. + - 조치: `OnAutoRunStartRequest` 진입부에 `PLC_WELD_READY_REQ`(0x0F=bit15) ON이면 즉시 return. (Resume 버튼 경로는 별개라 무관) + +4. **정지 시 에코 워드 0 초기화 제거** — `SeqAutoRunPTOP.cs` `OnSeqActionStop` + - `if(!ResumeMode){ PC_PAGE_NO=0; PC_RECIPE_NO=0; }` 블록 제거 → 모든 정지에서 D70001·D70002 보존(다음 사이클 재에코). `PC_ERROR_CODE=0`은 유지. + +5. **[AutoRun Resume] PC bit15 1초 펄스 OFF** — `UCMainAutoRunViewModel.cs` `OnAutoRunResume` + - `PC_WELD_READY_ACK`(PC쪽 bit15) ON 직후 one-shot DispatcherTimer로 1초 뒤 자동 false. + - 참고: 시퀀스 `CheckCycleStop`의 동일 OFF는 이제 중복(무해) — 정리는 별도 검토. + +### 검증 메모 (bit15 흐름) +- bit15 감지는 `OnMonitoring`이 **실 PLC(비-Simul)** 에서만 동작(Simul 가드) → 벤치 검증 불가, 실 PLC 필요. +- 코드는 입력 **0x0F(`PLC_WELD_READY_REQ`)** 를 봄 → PLC의 "15번 bit" 실주소가 0x0F(M70515)인지 디바이스맵 대조 필요(안 먹히면 1순위 원인). +- 확인 로그: `[Bit15] ModeChange…` / `[Bit15] Recipe 활성화+로드…` / `[Bit15] bit15 ON 중 → AUTO_REQ 자동시작 무시` / `[Bit15] PC_WELD_READY_ACK OFF (1초 펄스)`. + +## 9. 양극(P) 미사용 정리 + eCommon READY 제거 + 로그 개편 (빌드 0오류·미커밋) + +### 9-1. 공유 enum eCommon READY(0x0F) 제거 — `WeldingCommon/Devices/PLC/PLCController.Address.cs` +- `eCommonBitInput.PLC_WELD_READY_REQ` / `eCommonBitOutput.PC_WELD_READY_ACK` (0x0F) **제거**(`// 0x0A~0x0F` 예약범위로). 공유 enum의 READY는 미사용 항목(실제 bit15는 PTOP enum 0x0F 사용)이라 제거 안전. `PC_WELD_TRIGGER_ACK`(0x10)는 유지. +- **PTOP enum의 READY(0x0F)는 유지** → bit15/Resume 코드 영향 없음. + +### 9-2. 양극(P) PC→PLC 출력 전체 제거 (현장: 현재 장비 P 미사용) +- 증상: `Out of Range: 0x0065 (WriteBit)` — 0x65=`PC_AUTO_STATUS_P`(양극 Auto, M70601)가 출력맵에 없는데 코드가 씀. +- **PC_AUTO_STATUS_P(0x65) 쓰기 8곳 제거** — `SeqAutoRunPTOP`(Sequence_Enter/InitAutoRunBit/OnSeqActionStop) + `UCMainAutoRunViewModel`(bit15핸들러/OnAutoRunResume/OnAutoRunStartRequest×2/OnStopSequenceAutoRun). +- **양극 용접 핸드셰이크(PC_WELD_P_* 0x74~76) 제거** — `WaitWeldingStart`·`ExecuteWelding`의 `plcPageNo==2` 양극 분기 삭제 → **모든 페이지 음극 비트(0x10~12)로 통일**(용접 트리거가 음극 `PLC_WELD_TRIGGER_REQ`라 정합). `RestoreDeviceStatus`·`InitAutoRunBit` 양극 리셋도 제거. +- 검증: 두 파일에 `PC_AUTO_STATUS_P`/`PC_WELD_P_` 0건. +- ⚠️ PLC 확인: 이제 page2 용접 완료도 음극 COMPLETE(0x12)로 송신 → PLC가 전 페이지를 음극 완료로 받는지 정합 확인 필요. (보조 `SeqAutoRunPrismatic`의 양극 비트는 미변경.) + +### 9-3. SeqAutoRunPTOP 로그 개편 (로그만 수정, 로직 무변경) +- 제네릭 `[X] 노드 Start/End` 전부 제거 → 각 노드 동작을 설명하는 로그로 교체(InitSequenceVariable/LaserReadyResponse/SaveScannerRecordFileForamt/RestoreDeviceStatus/InitDev/CheckDeviceReady/CheckCondition/CheckCondition_Interlock/InitializeDeviceStatus/InitAutoRunBit/OnForceError/OnStopped). 잘못된 프리픽스(`SaveScannerRecordFileForamt` 내 `[SaveResultData]`) 정정. +- 핵심 동작 노드는 기존 촘촘한 로그 유지 → 로그만으로 시퀀스 어느 구간인지 추적 가능. + +## 10. 현장 진행 상황 / 이어서 할 일 + +- **확인 완료(현장)**: 앞서 수정분으로 해당 구간 **정상 통과** 확인, **Auto↔Manual 모드 전환** 동작 확인. +- **보류**: Recipe No / Page No 관련 **이번 세션 수정 코드는 아직 현장 미적용** → 추후 현장에 적용 후 검증 예정. +- **이어서 할 일(다음)**: 수정 코드(D70002 에코, 정지 시 에코워드 보존, 양극 제거/음극 통일, bit15 흐름, 로그 개편) 현장 반영 → Recipe/Page 핸드셰이크 + 용접 완료신호(음극) PLC 정합 재확인부터 이어감. +- 상태: 이번 세션 변경 4개 파일 + 작업일지 **미커밋**.