196 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 작업일지 — 2026-06-20
## 09:41 — Setting 정리: Axis Position Interlock 삭제 + Head Theta Interlock 영어화
### 변경 내용 (2파일)
- `WeldingComponents/WeldingCommon/Settings/SequenceSetting.cs`
- `[Category("Axis Position Interlock")]` 블록 **전체 삭제** (주석 + 속성 4개)
- 삭제 속성: `VerticalSector` / `VerticalTolerance` / `HorizontalSector` / `HorizontalTolerance`
- Serena `find_referencing_symbols` + grep 확인 결과 정의 파일 외 **참조처 없음** → 삭제 안전
- `Machines/ILSOS_PrismaticRD_PTOP/Settings/LaserInterlockSetting.cs`
- Head Theta Interlock 항목 `DisplayName` 5개 한글→영어화 (주석은 유지)
- `인터락 사용`→`Use Interlock` / `수평 각도(deg)`→`Horizontal Angle (deg)` / `수직 각도(deg)`→`Vertical Angle (deg)` / `허용 오차(±deg)`→`Tolerance (±deg)` / `PLC 스케일(deg×?)`→`PLC Scale (deg×?)`
- `[Category("Head Theta Interlock")]`는 기존부터 영어라 변경 없음
### 검증
- 서브에이전트로 MSBuild 빌드(Debug|x64) — **오류 0개**, 경고 655개(전부 기존·무관)
- 빌드 로그에 삭제 속성/변경 DisplayName 관련 오류·잔존 참조 없음
- 빌드 산출물: **WeldingCommon.dll**, **ILSOS_PrismaticRD_PTOP**
## 11:xx — AjinIO.IsConnected() 반전 로직 정정 + OnMonitoring 가드 정리
### 문제 (분석)
- 증상: `OnMonitoring()`의 `_IODevice.IsConnected()` 가드가 **시뮬에선 정상, 실제 Ajin 연결 시 동작 안 함**
- 원인: `AuroraDevices/AuroraIO/Vendor/AJINEXTEK/AjinIO.cs`의 `IsConnected()`가 **반대로 반환**
- `AxlGetBoardStatus(0)`이 `AXT_RT_SUCCESS`(정상)면 `false`, 에러면 `true` 반환 (파일 내 다른 호출부 관례와 정반대)
- 시뮬: 보드 없음 → 에러 → 우연히 `true` → 폴링 동작("정상"으로 보임)
- 실연결: 정상(SUCCESS) → `false` → 207줄 가드가 `return`으로 폴링 통째 스킵 → `IsGuidBeam`/`IsLaserReady` 미갱신
### 변경 내용 (2파일, 사용자 직접 수정)
- `AuroraDevices/AuroraIO/Vendor/AJINEXTEK/AjinIO.cs`
- `IsConnected()` → `return err == (int)AXT_FUNC_RESULT.AXT_RT_SUCCESS;` 로 정정 (SUCCESS=연결)
- `WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs`
- `OnMonitoring()` 정리: 미연결 시 `IsGuidBeam/IsLaserReady=false` 후 `return` 가드를 앞으로 이동
- 연결 시에만 `ReadDO(17)`·`ReadDI(11)&&ReadDI(15)` 수행 (LaserReady 한 줄로 정리)
### 비고
- `AjinIO.cs`는 공용(AuroraIO.dll) — Ajin 보드 쓰는 타 머신에도 영향(반전 정정이 올바른 방향)
- 사용자 요청으로 **빌드 검증 생략**
## 13:26 — U-Frame 가접 추가 → Recipe 신규 시 SeqAutoRunPTOP 작업구간 분석 (소스 미수정)
### 배경
- 어제 U-Frame 용접 시 **밀착 불량**으로 품질 미달 → 결론: **가접(tack weld) 추가** 후 본접.
- 현재 샘플 1개에 **테이프**로 임시 고정 검증 중. SW상 결국 **Recipe 1개 추가** 예상.
- 가접 정의(현장 확인): 본접 전, 부품이 떨어지지 않도록 **Dot/짧은 Line을 미리 출사해 pre-fix**하는 작업.
### 분석 결론 (소스 수정 없이 분석만)
- `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs` 노드별 검토
- **레시피 선택(CheckRecipeNo/Comp)**: 수정 불필요 — `.Id` 1:1 조회, 하드코딩 없음. 레시피 등록 + PLC 번호 송신만 하면 됨
- **페이지 처리(CheckPageNoComp)**: 수정 불필요 — 레이어 수 기반 가변 검증
- **리스트 업로드(SetUploadWeldingList)**: 조건부 — 가접이 같은 리스트 추가 오브젝트면 자동 포함
- **★ 용접 실행(ExecuteWelding, 라인 720)**: 핵심 분기점
- Case A: 가접 Dot/짧은 Line을 본접보다 **앞 순서 오브젝트**로 추가 → 한 리스트 연속 발진, 코드 수정 불필요(동일 파라미터·연속 Gate일 때)
- **Case B(이번 케이스 유력)**: 가접을 **별도 선행 패스**(다른 파라미터/별도 리스트)로 분리 → ExecuteWelding 수정, 필요 시 Builder Flow에 가접 전용 노드 추가
- 한 줄 요약: **레시피 추가 자체는 데이터 구동이라 소스 수정 거의 불필요. 가접을 "별도 동작"으로 돌릴 때만 ExecuteWelding이 작업 구간.**
### 결정 필요
- 가접 파라미터(파워/속도/체류시간)가 본접과 다른가? → 다르면 Case B
- 신규 레시피 번호(PLC_RECIPE_NO) 체계/매핑 (PLC측 확정 대기와 연계)
### 산출물
- 분석 문서: `docs/분석/2026/06/20/132647_uframe_가접_recipe_추가_seqautorun_분석.md`
## 13:37 — 레시피 전환 시 Scout Field가 "이전 레시피 필드"를 가져오는 버그 분석 (개선 필요, 소스 미수정)
### 증상 (현장 확인)
- 어제(06-19) PageIndex 주입 수정(`4a655ec`) 적용 후, 오늘 확인하니 **레시피 전환 시 새 레시피가 이전 레시피의 필드 영역을 그대로 가져옴.**
- 페이지 인덱스 정합성은 맞았으나 **레시피 단위 정합성**이 깨짐.
### 루트 원인
- `RecipeFieldScan`은 **싱글톤**이고 필드 데이터를 페이지명(`selectedIndex{N}`)으로만 키잉 — **레시피 식별자 없음.**
- **★ `MainPage.ReloadRecipe()`에서 필드 재로드(`SaveAndLoadFieldScanFromLasaDb()`)가 주석 처리됨** → 레시피를 바꿔도 싱글톤 `fieldScanDatas`가 이전 레시피 값 그대로 유지(stale).
- 그 결과 `SeqScoutNormal.LoadScoutField(pageIndex)`가 "올바른 페이지 × 이전 레시피 데이터"를 읽음.
- 어제 수정은 **페이지축 stale**을 고친 것이고, 오늘 버그는 **레시피축 stale**(별개 축).
### 개선안
- **단순 주석 해제는 위험** ⚠️ — `SaveAndLoadFieldScanFromLasaDb()`는 저장→로드 순서라, ReloadRecipe에서 `WorkRecipeFile`이 이미 새 경로로 바뀐 뒤 호출하면 **이전 레시피 stale 데이터를 새 레시피 FieldHelper.json에 덮어써 손상**시킴(주석 처리된 이유로 추정).
- **권장: "로드 전용" 메서드 추가**(`SaveFieldHelper` 없이 `LoadFieldHelper`만) → `ReloadRecipe`에서 `UpdateWeldingLinesRecipe()`로 Layers 채워진 뒤 호출하여 새 레시피의 FieldHelper.json만 로드.
- 검토: FieldHelper.json 부재 시 NRE, ChangeScoutImage 동반 필요 여부, PLC Recipe Change(`RMSHandlerForPTOP`) 경로 적용, 전환 시 저장 금지(저장은 필드변경/스카우트/레시피저장 때만).
- (선택) 근본: 싱글톤에 레시피 식별자 포함 또는 전환 시 명시적 Clear.
### 산출물
- 분석 문서: `docs/분석/2026/06/20/133714_recipe전환시_scout필드_이전레시피_잔존_분석.md`
## 13:50~14:xx — 레시피축 stale 수정 적용 + 서브에이전트 교차검증 + 정정
### 1차 수정 (불완전)
- `MainPage.cs`에 로드 전용 `LoadFieldScanFromLasaDb()` 추가 + `ReloadRecipe`에서 호출. 빌드 0 오류.
### 서브에이전트 교차검증으로 발견한 핵심 결함 (직접 코드 재확인 완료)
- **숨은 저장 경로**: `ReloadRecipe → UpdateWeldingLinesRecipe()(MainPage.cs:411) → UpdateLayersInfo() → toolStripComboBoxPage.SelectedIndex 설정(:364) → SelectedIndexChanged 핸들러(:1411) → SaveAndLoadFieldScanFromLasaDb()(:1418)`.
- 이 시점 싱글톤=이전 레시피(stale), WorkRecipeFile=새 경로 → **SaveFieldHelper가 stale을 새 레시피 FieldHelper.json에 덮어써 디스크 손상.** 1차 수정의 로드는 손상된 파일을 다시 읽어 무력화.
- 이 손상은 **수정 전부터 잠재**한 기존 거동 → 기존 레시피 전환 반복 시 일부 FieldHelper.json 오염 가능성(현장 점검 권장).
## 야간 작업 — PLC Page Change bit 단일 트리거 통합 (Recipe→Page)
### 배경/목표
- 기존: PLC가 Page Change bit(`PLC_PAGE_CHANGE_REQ` 0x08)와 Recipe Change bit(`PLC_RECIPE_CHANGE_REQ` 0x09)를 각각 줘서 Recipe/Page를 따로 처리.
- 변경: **Page Change bit(0x08) 한 bit만 트리거.** ON되면 Recipe No·Page No를 함께 읽어 **Recipe 먼저 전환 → Page 전환**. 0x09는 트리거로 미사용(enum 정의는 보존).
### 변경 파일: `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPTOP.cs` (단일 파일)
- 흐름 재배선: `CheckInitCondition → CheckPageNo(0x08 대기) → CheckRecipeNoComp(레시피 먼저) → CheckPageNoComp(페이지)`. `CheckRecipeNo` Decision(0x09 peek) 노드/메서드/`SEQ_CHECK_RECIPENO` 상수 제거. 루프백·`CheckInitCondition`·`CheckCycleStop` 반환을 `SEQ_CHECK_PAGENO`로 변경.
- `CheckRecipeNoComp`: `PLC_RECIPE_NO` **1회 읽기**(3회 검증 제거→tact 절감). 0/음수/미등록/현재활성과 동일 → 전환·워드 재기록 생략(시퀀스 정지 없음). 실제 전환 시에만 `ActivateRecipe`+Id폴링 + **`Dispatcher.Invoke(RecipeHelper.OnActiveReloaded)` 동기 Model 갱신** + `PC_RECIPE_NO` 에코.
- **B-1(race 차단)**: `CheckPageNoComp`의 `Model.Layers.Count()` 검증과 `SetUploadWeldingList_SeqParams`의 레이어 선택을 `Dispatcher.Invoke`로 UI 스레드 직렬화.
- `PC_RECIPE_CHANGE_COMPLETE` write 3곳(CheckRecipeNoComp/RestoreDeviceStatus/OnSeqActionStop) 모두 제거(미사용 확정). `PC_RECIPE_NO` 에코는 유지.
### 핵심 근거 (Serena 추적)
- `ActivateRecipe`→`SYS_RECIPE_ACTIVATED`(UI dispatcher)→`EVENT_CHANGE_RECIPE_INFO`(UI dispatcher)→`RecipeHelper.OnActiveReloaded`→`MainPage.ReloadRecipe`→`UpdateLayersInfo`에서 `Model.Layers` 채움. 시퀀스(worker)는 `ActivatedRecipe().Id`만 폴링 → Layers 갱신과 **비동기 race**. 노드 순서만 바꾸면 race 악화 → B(동기 reload)+B-1(읽기 UI 직렬화)로 해소.
### 검증
- Serena LSP 진단 0건.
- 서브에이전트 시나리오 점검 ①무효 recipe+무효 page ②recipe=현재값 ③recipe 0/음수 ④없는 번호 ⑤PC 에코/표시: `short` 음수 recipe는 `==0`이 아니라 "미등록" 경로로 안전 차단 확인. 시2·3 PASS.
- 결정: **G1(무효/0/음수 recipe = 현 레시피 유지)이 정상**, **G2 `PC_RECIPE_CHANGE_COMPLETE` 미사용**.
- **VS Rebuild(MSBuild Debug|x64) EXITCODE=0, CS/MSB 에러 0건**(경고는 기존 코드, 이번 변경 무관).
- 인코딩 UTF-8 BOM+CRLF, 한글 정상.
### 후속(참고)
- page 무효 정지 시 알람/`PC_ERROR_CODE` 기록은 여전히 주석 처리 상태(별도 과제).
### 최종 수정 (적용·빌드 검증 완료)
- `MainPage.cs` 한 파일, 기존 소스 보존:
1. 가드 플래그 `private bool _suppressPageFieldSync = false;` 추가
2. 콤보박스 핸들러: `if (!_suppressPageFieldSync) SaveAndLoadFieldScanFromLasaDb();`
3. `ReloadRecipe`: `UpdateWeldingLinesRecipe()`를 try/finally로 감싸 전환 동안 가드 ON → 저장 스킵, 이후 `LoadFieldScanFromLasaDb()`로 새 레시피 필드만 로드
- 효과: (a) 전환 중 stale 저장 차단(손상 방지), (b) 새 레시피 필드 정상 로드 → 레시피축 stale 해소
- 평상시 사용자 페이지 전환은 가드 false라 무영향. WinForms 단일 UI 스레드 동기 발화라 경쟁 없음.
- **MSBuild Debug|x64 솔루션 빌드: 오류 0개** (경고 212개 전부 기존·무관)
### 잔여(선택)
- 인덱스 불변(0→0) 케이스 ChangeScoutImage 미동반 시 이미지/데이터 불일치 소지(Low~Med)
- 근본: 싱글톤에 레시피 식별자 도입 또는 전환 시 명시적 Clear
- 현장: 레시피 전환 → Scout Field 새 영역 반영 런타임 확인, 기존 오염된 FieldHelper.json 점검
## 14:xx — 현재 상태 정리 (레시피축 stale 수정 건)
- **수정 적용 완료**: `WeldingComponents/LASA/GUI/Page/MainPage.cs` (가드 플래그 `_suppressPageFieldSync` + 콤보박스 핸들러 가드 + ReloadRecipe try/finally + 로드 전용 `LoadFieldScanFromLasaDb`)
- **빌드 검증 완료**: MSBuild Debug|x64 솔루션, 오류 0개
- **미커밋 상태**: 커밋 전, 현장 런타임 확인 대기 중
- 다음 액션: ① 현장에서 레시피 전환 시 Scout Field 새 영역 반영 확인 ② 기존 전환으로 오염됐을 수 있는 FieldHelper.json 점검 ③ 확인 후 커밋
- 산출물: 분석 `docs/분석/2026/06/20/133714_recipe전환시_scout필드_이전레시피_잔존_분석.md` (4-B 교차검증 섹션 포함)
## PLC PC I/O Check 완료
- PLC ↔ PC I/O Check 완료
## 15:xx — TeachingRun H알람 인터락 임시 우회 원복 (현장 PLC 연결 완료)
- `Machines/ILSOS_PrismaticRD_PTOP/Sequences/Manual/SeqComponentTeachingRun.cs`의 `CheckCondition_Interlock()`에서 현장 PLC 미연결로 임시 주석 처리했던 **High(중대) 알람 인터락 블록(`AlarmManager.HasAlarmLevelHigh()`)을 원복**.
- 현장 PLC 연결 완료 + 현장 소스도 원복 완료 → SW도 원복.
- 아래 Door/EMO 블록(`#warning to KSJ > 지워도 무방해보임`)은 별개 건이라 그대로 둠.
- 커밋 `9330fb2`, `field/ptop-1st` 브랜치 푸시 완료.
- 2026_06_19 작업일지 TODO **"알람 인터락 블록 원복 (현장 PLC 정상화 후)"** 항목 해소.
## 19:xx — SeqAutoRunPTOP Recipe/Page "동시 수신" 핸드셰이크 재구성 분석 (소스 미수정, 확정)
### 배경 (현장 확인)
- PLC/IO 체크 완료.
- 현장 PLC가 **Recipe 변경 신호와 Page 변경 신호를 따로 안 주고 동시에** 줌 → 현재 SW의 비대칭 4노드 구조와 안 맞음.
- 가접(tack weld)도 곧 추가 예정(어제 분석과 연계).
### 현재 구조 문제
- 현재: `CheckRecipeNo`(논블로킹 peek) → `CheckRecipeNoComp` → `CheckPageNo`(블로킹) → `CheckPageNoComp` **4노드 비대칭**.
- Recipe는 가끔/Page는 매 사이클 가정 → 동시 수신 시 완료비트 분리·REQ 드롭 race 위험.
### 확정 설계 (단일 PAGE 게이트 핸드셰이크, 4노드 → 1노드)
- **PAGE_REQ 하나만** 게이트로 사용(RECIPE_REQ 비트 미감시).
- 매 사이클: PAGE_REQ 수신 → **RecipeNum·PageNum 워드 각 3회 더블체크** → (변경 시) 레시피 활성화 → Page 검증 → 캔버스 `Layers[_CurrentPageNo].Objects`로 용접리스트 생성 → **PC_RECIPE_NO/PC_PAGE_NO 에코** → **PAGE COMPLETE 하나만** 송신.
- **조건부 활성화**: RecipeNum을 더블체크해 **이전 RecipeNum과 다를 때만** `ActivateRecipe`(동일 레시피면 스킵 → 택트·race 회피). `_prevRecipeNo` + `ActivatedRecipe().Id` 교차 확인.
- **페이지 수**: 레시피별 고정값이나 **상한 없음(사용자 페이지 추가 가능)** → 상한 하드코딩 금지, `Layers.Count()`만 신뢰(기존 260618 방향과 일치).
### ★ 코드 추적으로 확인한 핵심 리스크 (race, 실재)
- 캔버스 `Model.Layers`는 `MainPage.ReloadRecipe` → `DB.Dao.WeldingLinesRecipe.Load()`에서 재생성됨. `ActivateRecipe` 자체는 모델을 직접 안 건드림.
- `ActivateRecipe` → (SYS_RECIPE_ACTIVATED) → `UCScannerObjectTeachingViewModel.OnActivatedRecipe` → (EVENT_CHANGE_RECIPE_INFO) → `RMSHandlerForPTOP.OnUpdateCurrentActivatedRecipe` → `ReloadRecipe` 의 **UI Dispatcher 2-홉 비동기** 체인.
- 시퀀스 폴링이 보는 `ActivatedRecipe().Id`는 활성화 호출 즉시(동기) 바뀌지만, **Layers 교체는 2-홉 뒤**라 → 폴링 통과 직후 용접리스트가 **이전 레시피 레이어**를 읽을 수 있음 = 어제 본 "레시피축 stale"과 동일.
- 대응안: (A) 시퀀스에서 `Dispatcher.Invoke(ReloadRecipe)` 동기 강제 로드(권장) / (B) 로드 완료 신호 폴링 / (C) DataModel에 레시피 Id 키 추가. **불변식 기준은 "ActivatedRecipe().Id 일치"가 아니라 "Layers 교체 완료".**
### 가접 연계
- 핸드셰이크와 별개 축. Case B(본접 전 선행 패스)면 `ExecuteWelding`에 가접 리스트 선행 발진 추가(어제 132647 문서 참조).
### 미해결 확인 포인트 (적용 전)
- PC_PAGE_CHANGE_COMPLETE 극성(현재 코드 `false` write) PLC 기대값 재확인.
- PC_RECIPE_NO 에코만으로 PLC 만족하는지(레시피 COMP 비트 불필요 확정).
- Layers race 대응안 A/B/C 택1.
### 산출물
- 분석 문서: `docs/분석/2026/06/20/191351_recipe_page_통합신호_핸드셰이크_재구성_분석.md` (8장 = 현장 확정안)
- **소스 미수정** (분석/프리뷰만).
---
## [일일 요약] 06월 20일 동탄 PTOP 작업내용
1. Menu 영문화 및 미사용 옵션 삭제
2. AjinIO isConnected 함수 오류 수정
3. recipe 수정 시 Scout field 저장 안되는 현상 수정
4. PLC-PC I/O Check 완료
- **명일 작업**: 실제 Laser 동작 확인 / Recipe & Page Change 소스 수정 및 적용 테스트 / Seq 동작 테스트