From d537792c48bac42c2454b9042b050497f7859d9a Mon Sep 17 00:00:00 2001 From: NohDoyeon241104 Date: Sat, 20 Jun 2026 14:41:04 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20docs/=EB=B6=84=EC=84=9D=20git=20?= =?UTF-8?q?=EC=B6=94=EC=A0=81=20=ED=95=B4=EC=A0=9C=20=EB=B0=8F=20gitignore?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80,=20=EC=82=B0=EC=B6=9C=EB=AC=BC=20?= =?UTF-8?q?=EC=96=91=EC=8B=9D=20=ED=8F=B4=EB=8D=94=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /docs/분석/ 을 .gitignore에 추가 (로컬 전용 분석 문서, 커밋 제외) - 기존 추적되던 docs/분석 22개 파일 git rm --cached (로컬 파일은 유지) - Doc/산출물문서/양식/ 폴더 생성 (추후 필수 제출 2종 드롭 위치, README 포함) Co-Authored-By: Claude Opus 4.8 --- .gitignore | 3 + Doc/산출물문서/양식/README.md | 30 ++ .../06/16/135937_recipe_activation_analysis.md | 397 -------------- .../135937_recipe_activation_deep_analysis.md | 430 --------------- .../06/16/145623_recipe_tab_crash_analysis.md | 509 ------------------ .../06/17/143131_laser_ready_di9_analysis.md | 187 ------- ..._axis_position_interlock_setting_location.md | 162 ------ ...sequencesetting_axis_interlock_properties.md | 86 --- .../17/150031_laser_ready_di12_di24_change.md | 122 ----- .../17/162010_io_interface_map_path_source.md | 103 ---- .../06/17/165341_plc_devicemap_update_260617.md | 67 --- ...2_plc_map_io_direction_and_enum_alignment.md | 89 --- .../06/17/171642_plc_map_update_checklist.md | 48 -- .../06/17/184011_eroom_light_rs232_analysis.md | 264 --------- ...ual_laser_power_control_groupbox_analysis.md | 108 ---- ...655_ptop_cross_machine_reference_analysis.md | 81 --- .../18/111604_ptop_namespace_migration_guide.md | 135 ----- ...4445_analog_only_no_laser_toggle_analysis.md | 117 ---- .../18/122432_laser_on_no_emission_analysis.md | 90 ---- ...143345_io_interface_di_path_ptop_analysis.md | 106 ---- .../06/19/110333_alarm_subsystem_analysis.md | 111 ---- ...11326_mxcomponent_plc_connection_analysis.md | 82 --- .../06/19/122201_ajinio_readdobit_analysis.md | 68 --- .../06/19/124752_guidebeam_trupulse_analysis.md | 59 -- 24 files changed, 33 insertions(+), 3421 deletions(-) create mode 100644 Doc/산출물문서/양식/README.md delete mode 100644 docs/분석/2026/06/16/135937_recipe_activation_analysis.md delete mode 100644 docs/분석/2026/06/16/135937_recipe_activation_deep_analysis.md delete mode 100644 docs/분석/2026/06/16/145623_recipe_tab_crash_analysis.md delete mode 100644 docs/분석/2026/06/17/143131_laser_ready_di9_analysis.md delete mode 100644 docs/분석/2026/06/17/143900_axis_position_interlock_setting_location.md delete mode 100644 docs/분석/2026/06/17/144442_sequencesetting_axis_interlock_properties.md delete mode 100644 docs/분석/2026/06/17/150031_laser_ready_di12_di24_change.md delete mode 100644 docs/분석/2026/06/17/162010_io_interface_map_path_source.md delete mode 100644 docs/분석/2026/06/17/165341_plc_devicemap_update_260617.md delete mode 100644 docs/분석/2026/06/17/171312_plc_map_io_direction_and_enum_alignment.md delete mode 100644 docs/분석/2026/06/17/171642_plc_map_update_checklist.md delete mode 100644 docs/분석/2026/06/17/184011_eroom_light_rs232_analysis.md delete mode 100644 docs/분석/2026/06/18/102847_manual_laser_power_control_groupbox_analysis.md delete mode 100644 docs/분석/2026/06/18/110655_ptop_cross_machine_reference_analysis.md delete mode 100644 docs/분석/2026/06/18/111604_ptop_namespace_migration_guide.md delete mode 100644 docs/분석/2026/06/18/114445_analog_only_no_laser_toggle_analysis.md delete mode 100644 docs/분석/2026/06/18/122432_laser_on_no_emission_analysis.md delete mode 100644 docs/분석/2026/06/18/143345_io_interface_di_path_ptop_analysis.md delete mode 100644 docs/분석/2026/06/19/110333_alarm_subsystem_analysis.md delete mode 100644 docs/분석/2026/06/19/111326_mxcomponent_plc_connection_analysis.md delete mode 100644 docs/분석/2026/06/19/122201_ajinio_readdobit_analysis.md delete mode 100644 docs/분석/2026/06/19/124752_guidebeam_trupulse_analysis.md diff --git a/.gitignore b/.gitignore index 0c26fb2..25b9b64 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ bin/ # 로컬 전용 Aurora 에이전트 레퍼런스 (커밋 제외) /docs/Aurora에이전트레퍼런스모음/ + +# 로컬 전용 분석 문서 (커밋 제외) +/docs/분석/ diff --git a/Doc/산출물문서/양식/README.md b/Doc/산출물문서/양식/README.md new file mode 100644 index 0000000..59c9398 --- /dev/null +++ b/Doc/산출물문서/양식/README.md @@ -0,0 +1,30 @@ +# 산출물문서 — 양식(제출 세트) 폴더 + +이 폴더는 **추후 추가될 필수 제출 산출물**을 받아두는 드롭 위치입니다. +파일을 여기에 넣어주시면 동일한 방식으로 분석 → PTOP 프로젝트 내용으로 채움 진행합니다. + +## 제출 세트 현황 (총 4종 예정) + +| # | 산출물 | 위치 | 상태 | +|---|--------|------|------| +| 23 | SW 점검 결과 (일일 IO Check 진척 공유) `.xlsx` | `Doc/산출물문서/` | 입수 ✅ (PTOP 채움 필요) | +| 27 | HMI Manual `.pptx` | `Doc/산출물문서/` | 입수 ✅ (PTOP 채움 필요) | +| (TBD) | 필수 제출 ① | `Doc/산출물문서/양식/` | **대기 — 여기 넣어주세요** | +| (TBD) | 필수 제출 ② | `Doc/산출물문서/양식/` | **대기 — 여기 넣어주세요** | + +## 넣을 때 참고 + +- 파일명에 설비/날짜 식별자가 있으면 그대로 두셔도 됩니다(예: `*_UBISAM_yymmdd_PTOP.*`). +- 넣은 뒤 알려주시면: ① 구조 분석 → ② 타설비 템플릿 잔재 식별 → ③ PTOP 내용(코드 출처 명시) 채움 초안 작성 순서로 진행합니다. + +## 관련 분석 문서 + +- `docs/분석/2026/06/20/141528_산출물문서_분석_및_PTOP_채움안.md` + — #23(Excel)·#27(PPT)의 시트/슬라이드별 PTOP 채움 초안. + +## PTOP 프로젝트 핵심 사실 (채움 시 공통 참조) + +- 설비: 각형 배터리 레이저 용접기(ILSOS OC5 PtoP Welder), Aurora 2.0 / .NET 4.8.1 / WPF +- 주요 디바이스: PLC(Mitsubishi Melsec, MX Component), Laser Scanner(RTC), Laser(IO Gate), Ajin Digital IO +- 메인 시퀀스: `SeqAutoRunPTOP` (Recipe/Page 핸드셰이크 → 용접리스트 업로드 → 용접 발진 → 완료비트) +- PLC 인터페이스 정의: `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs` diff --git a/docs/분석/2026/06/16/135937_recipe_activation_analysis.md b/docs/분석/2026/06/16/135937_recipe_activation_analysis.md deleted file mode 100644 index df521a7..0000000 --- a/docs/분석/2026/06/16/135937_recipe_activation_analysis.md +++ /dev/null @@ -1,397 +0,0 @@ -# ILSOS_PrismaticRD_PTOP Recipe 동적 활성화 분석 - -**분석 일시:** 2026-06-16 13:59:37 -**대상:** SeqAutoRunPTOP.cs + Aurora RecipeManager 메커니즘 -**목표:** PLC에서 RecipeNum + PageNum을 받아 동적으로 Recipe를 변경하고 공정별 페이지 선택하는 설계 - ---- - -## 1. 현재 상황 분석 - -### 1.1 현재 구조 -``` -SeqAutoRunPTOP.cs (Line 215-216): - ├─ activatedRecipeId = Application.Current.AuroraEnv().RecipeManager.ActivatedRecipe().Id - ├─ recipeName = RecipeHelper.SearchRecipeItem(activatedRecipeId) - └─ 활성화된 Recipe 1개에 대해서만 작동 - └─ PLC에서 PageNum(1,2)만 받음 → 해당 Recipe의 페이지 선택 -``` - -### 1.2 현재 제약사항 -- **단일 Recipe 지원**: 미리 활성화된 Recipe 1개만 사용 -- **동적 변경 불가**: Recipe 활성화 코드 없음 (UI에서만 가능) -- **공정 분리 불가**: 3개 공정(CC, CAP, U-Frame)을 구분할 수 없음 - ---- - -## 2. Aurora RecipeManager 메커니즘 분석 - -### 2.1 Recipe 활성화 방법 - -**발견 위치:** `RMSHandlerForScanner.cs` Line 173 - -```csharp -// Recipe 활성화 핵심 메서드 -_aurora.RecipeManager.ActivateRecipe(recipe.Id, "RMS Request"); -``` - -**메서드 시그니처:** -```csharp -void ActivateRecipe(int recipeId, string reason) -``` - -**매개변수:** -- `recipeId` (int): Recipe의 고유 ID -- `reason` (string): 활성화 사유 (로그/감사용) - 예: "RMS Request", "Manual Change", "Auto Selection" - -### 2.2 Recipe 정보 조회 - -**Recipe ID로 조회:** -```csharp -// RMSHandlerForScanner.cs Line 125 -var recipe = _recipeEntry.Recipes().FirstOrDefault(x => x.Id == recipeId); -``` - -**Recipe 이름으로 조회:** -```csharp -// RMSHandlerForScanner.cs Line 132 -var recipeInfo = RecipeHelper.getRecipeInfo(recipe.Name); -``` - -### 2.3 Recipe 로드/저장 - -**Recipe 로드 (활성화 후):** -```csharp -// RMSHandlerForScanner.cs Line 136 -RecipeHelper.ReloadRecipe(recipe.Name); -``` - -**Recipe 저장:** -```csharp -// RMSHandlerForScanner.cs Line 146 -RecipeHelper.SaveRecipe(recipe.Name); -``` - -### 2.4 이벤트 트리거 - -**Recipe 변경 이벤트:** -```csharp -// EquipDefCommon.cs Line 145 -public const string EVENT_CHANGE_RECIPE_INFO = "EventChangeRecipeInfo"; - -// RMSHandlerForScanner.cs Line 56-59 (구독 예시) -aurora.EventObservable.AsObservable(CommonEventIds.EVENT_CHANGE_RECIPE_INFO) - .ObserveOnDispatcher() - .Select(param => (RecipeInfo)param.Param) - .Subscribe(OnUpdateCurrentActivatedRecipe); -``` - -**활성화 완료 흐름:** -``` -RecipeManager.ActivateRecipe(id, reason) - ↓ (EVENT_CHANGE_RECIPE_INFO 발행) - ↓ -RMSHandlerForScanner.OnUpdateCurrentActivatedRecipe(RecipeInfo info) - ├─ RecipeHelper.ReloadRecipe(info.Name) - └─ UploadActivatedRecipeParameter() -``` - ---- - -## 3. 변경 요구사항 분석 - -### 3.1 요구사항 - -| # | 요구사항 | 현재 상태 | 변경 필요 | -|---|---------|---------|----------| -| 1 | 3개 공정 지원 (CC, CAP, U-Frame) | 단일 Recipe만 지원 | ✓ 다중 Recipe 매핑 | -| 2 | 각 공정별 Recipe 활성화 | UI에서만 가능 | ✓ PLC 신호로 동적 활성화 | -| 3 | RecipeNum + PageNum 수신 | PageNum만 수신 | ✓ RecipeNum WORD 추가 | -| 4 | RecipeNum → RecipeId 매핑 | 없음 | ✓ 설정 테이블 추가 | -| 5 | UI에서 공정별 RecipeNum 관리 | 없음 | ✓ 설정 UI 추가 (후순위) | - -### 3.2 예상 동작 - -**변경 전:** -``` -PLC → ReadWord(PLC_PAGE_NO) → PageNum(1,2) - ↓ -SeqAutoRunPTOP → 활성된 Recipe의 Page 선택 - ↓ -_SelectedObjectList 구성 -``` - -**변경 후:** -``` -PLC → ReadWord(PLC_RECIPE_NO) + ReadWord(PLC_PAGE_NO) - ↓ -SeqAutoRunPTOP → RecipeNum으로 Recipe 동적 활성화 - ↓ -RecipeManager.ActivateRecipe(mappedRecipeId, "PLC Request") - ↓ -EVENT_CHANGE_RECIPE_INFO 발생 - ↓ -해당 Recipe 로드 + 설정 적용 - ↓ -Page 선택 → _SelectedObjectList 구성 -``` - ---- - -## 4. 구현 계획 - -### Phase 1: 설정 테이블 정의 - -**위치:** SeqAutoRunPTOP.cs 또는 별도 설정 클래스 - -```csharp -// 공정별 RecipeNum ↔ RecipeId 매핑 -public class ProcessRecipeMapping -{ - public enum eProcessType - { - CC = 1, // 공정1 - CAP = 2, // 공정2 - UFrame = 3 // 공정3 - } - - // 매핑 테이블 (사용자 설정 후 저장) - private Dictionary _recipeIdMap = new Dictionary() - { - { eProcessType.CC, 101 }, // CC 공정 → Recipe ID 101 - { eProcessType.CAP, 102 }, // CAP 공정 → Recipe ID 102 - { eProcessType.UFrame, 103 } // U-Frame 공정 → Recipe ID 103 - }; - - public int GetRecipeId(eProcessType process) => _recipeIdMap[process]; - public int GetRecipeId(int recipeNum) => _recipeIdMap[(eProcessType)recipeNum]; -} -``` - -### Phase 2: PLC 신호 정의 - -**SeqAutoRunPTOP.cs에 추가:** - -```csharp -// PLC에서 수신할 WORD 정의 -private int _plcRecipeNum = 0; // PLC_RECIPE_NO (Word) -private int _plcPageNum = 0; // PLC_PAGE_NO (Word) - -// CheckPageNo 시퀀스 수정 위치 -// Line 354: plcPageNoList.Add(_PLC.ReadWord(eWordInput.PLC_PAGE_NO).data); -// -// 변경 후: -// ┌─────────────────────────────────────────┐ -// │ Step 1: RecipeNum 읽기 │ -// │ _plcRecipeNum = _PLC.ReadWord(...) │ -// │ │ -// │ Step 2: RecipeId 매핑 │ -// │ int recipeId = mappingTable.GetRecipeId(_plcRecipeNum) │ -// │ │ -// │ Step 3: Recipe 활성화 │ -// │ _AuroraEnv.RecipeManager.ActivateRecipe │ -// │ (recipeId, "PLC Request") │ -// │ │ -// │ Step 4: Recipe 로드 완료 대기 │ -// │ (이벤트 핸들러에서 처리) │ -// │ │ -// │ Step 5: PageNum 읽기 │ -// │ _plcPageNum = _PLC.ReadWord(...) │ -// └─────────────────────────────────────────┘ -``` - -### Phase 3: 코드 구조 변경 - -**SeqAutoRunPTOP.CheckPageNo() 수정 전략:** - -```csharp -// 현재 (Line 282-333): -private SequenceActionResult CheckPageNo(SequenceContext context, string id, SequenceActionResult param) -{ - // PLC_PAGE_CHANGE_REQ 대기 → PageNum 읽기 → 완료 -} - -// 변경 후: -private SequenceActionResult CheckPageNo(SequenceContext context, string id, SequenceActionResult param) -{ - // Step 1: PLC_PAGE_CHANGE_REQ 대기 (기존 로직) - // Step 2: RecipeNum 읽기 추가 - // Step 3: Recipe 활성화 (동기/비동기 결정) - // Step 4: Recipe 로드 완료 확인 - // Step 5: PageNum 읽기 (기존 로직) - // Step 6: 완료 -} -``` - -**Recipe 활성화 이벤트 핸들러:** - -```csharp -// SeqAutoRunPTOP 클래스 내부 -private bool _recipeChangeCompleted = false; - -// Aurora 이벤트 구독 (Sequence_Enter에서) -_AuroraEnv.EventObservable - .AsObservable(CommonEventIds.EVENT_CHANGE_RECIPE_INFO) - .ObserveOnDispatcher() - .Subscribe(msg => - { - Log.I("[CheckPageNo] Recipe Change Event 감지"); - _recipeChangeCompleted = true; - }); - -// CheckPageNo에서 완료 대기 -private SequenceActionResult WaitForRecipeChange(SequenceContext context, int timeoutSec = 5) -{ - _recipeChangeCompleted = false; - var startTime = DateTime.Now; - - while (!context.Stopped && !_recipeChangeCompleted) - { - if (DateTime.Now.Subtract(startTime).TotalSeconds > timeoutSec) - { - Log.E("[CheckPageNo] Recipe Change Timeout"); - return null; - } - Thread.Sleep(10); - } - - Log.I("[CheckPageNo] Recipe Change 완료"); - return SequenceAction.Ok(); -} -``` - -### Phase 4: RecipeHelper를 통한 로드 확인 - -```csharp -// RMSHandlerForScanner.cs의 OnUpdateCurrentActivatedRecipe 패턴 활용 - -private void OnRecipeActivated(RecipeInfo info) -{ - Log.I($"[SeqAutoRunPTOP] Recipe 활성화됨: {info.Name}"); - - // Recipe 파라미터 로드 - RecipeHelper.ReloadRecipe(info.Name); - - // 필요시 로드 검증 - var loadedRecipe = RecipeHelper.getRecipeInfo(info.Name); - if (loadedRecipe == null) - { - Log.E("[SeqAutoRunPTOP] Recipe 로드 실패"); - AlarmManager.SetAlarm(AlarmCode.RECIPE_LOAD_FAILED); - return; - } - - Log.I("[SeqAutoRunPTOP] Recipe 로드 완료"); - _recipeChangeCompleted = true; -} -``` - ---- - -## 5. 구현 순서 (권장) - -### Step 1: 설정 클래스 생성 -- `ProcessRecipeMapping` 클래스 작성 -- RecipeNum ↔ RecipeId 매핑 설정 - -### Step 2: SeqAutoRunPTOP.cs 수정 -1. 이벤트 핸들러 추가 (Recipe 활성화 완료 감지) -2. CheckPageNo에 RecipeNum 읽기 로직 추가 -3. RecipeManager.ActivateRecipe() 호출 -4. Recipe 로드 완료 대기 - -### Step 3: PLC 통신 검증 -- ReadWord(RecipeNum) 정상 작동 확인 -- Recipe 활성화 타이밍 검증 -- PageNum 읽기가 Recipe 로드 후 실행되는지 확인 - -### Step 4: 에러 처리 추가 -- Recipe ID 미존재 시 처리 -- Recipe 로드 타임아웃 처리 -- Fallback 로직 (기본 Recipe 활용) - -### Step 5: UI 설정 추가 (후순위) -- 공정별 RecipeNum 설정 화면 -- 매핑 테이블 UI 편집 - ---- - -## 6. 예상 코드 위치 - -| 파일 | 라인 | 수정 내용 | -|------|------|---------| -| SeqAutoRunPTOP.cs | 50-100 | 클래스 멤버 추가 (매핑, 이벤트) | -| SeqAutoRunPTOP.cs | 202-240 | Sequence_Enter에서 이벤트 구독 | -| SeqAutoRunPTOP.cs | 282-333 | CheckPageNo에 RecipeNum 읽기 추가 | -| 별도 파일 | - | ProcessRecipeMapping 클래스 | - ---- - -## 7. 핵심 검증 포인트 - -1. **RecipeManager.ActivateRecipe() 호출 후 Event 발생 타이밍** - - 동기인가 비동기인가? - - 즉시 발생하는가 아니면 지연되는가? - -2. **RecipeHelper.ReloadRecipe()의 필요성** - - EVENT_CHANGE_RECIPE_INFO에서 자동으로 로드되는가? - - 수동으로 ReloadRecipe를 호출해야 하는가? - -3. **_SelectedObjectList 업데이트 시점** - - Recipe 활성화 후 새 Recipe의 Canvas가 로드되는가? - - 별도로 처리해야 하는가? - -4. **타이밍 문제** - - RecipeNum 읽기 → 활성화 → 로드 완료 → PageNum 읽기 - - 각 단계의 예상 소요시간? - ---- - -## 8. 참고 코드 - -### RecipeManager.ActivateRecipe 사용 사례 -**파일:** `RMSHandlerForScanner.cs` Line 173 - -```csharp -var preActivatedRecipe = _aurora.RecipeManager.ActivatedRecipe(); -_aurora.RecipeManager.ActivateRecipe(recipe.Id, "RMS Request"); -MessageUtil.ShowToast($"RMS Change Recipe ... ( {preActivatedRecipe.Id} -> {recipe.Id} )", Log); -``` - -### EVENT_CHANGE_RECIPE_INFO 구독 사례 -**파일:** `RMSHandlerForScanner.cs` Line 56-66 - -```csharp -aurora.EventObservable.AsObservable(CommonEventIds.EVENT_CHANGE_RECIPE_INFO) - .ObserveOnDispatcher() - .Select(param => (RecipeInfo)param.Param) - .Subscribe(OnUpdateCurrentActivatedRecipe); - -private void OnUpdateCurrentActivatedRecipe(RecipeInfo info) -{ - RecipeHelper.ReloadRecipe(info.Name); - UploadActivatedRecipeParameter(); -} -``` - ---- - -## 9. 다음 단계 - -1. **코드 분석 후 결정 필요:** - - RecipeManager.ActivateRecipe()의 정확한 동작 방식 (동기/비동기) - - EVENT_CHANGE_RECIPE_INFO 발생 타이밍 - - RecipeHelper.ReloadRecipe()의 필요성 - -2. **설계 결정:** - - RecipeNum을 WORD로 받을 것인가? (또는 별도 IO?) - - 매핑 테이블을 하드코딩할 것인가? (또는 설정 파일?) - - Fallback 시나리오 정의 - -3. **구현 시작:** - - 위 확인 후 Phase 1부터 순차 진행 - ---- - -**작성자:** Claude Code Analysis -**검토 필요:** 실제 Aurora RecipeManager 메서드 시그니처 확인 필요 diff --git a/docs/분석/2026/06/16/135937_recipe_activation_deep_analysis.md b/docs/분석/2026/06/16/135937_recipe_activation_deep_analysis.md deleted file mode 100644 index c0f415d..0000000 --- a/docs/분석/2026/06/16/135937_recipe_activation_deep_analysis.md +++ /dev/null @@ -1,430 +0,0 @@ -# Recipe 동적 활성화 — 깊이 있는 분석 (상세 검증) - -**분석 일시:** 2026-06-16 13:59:37 -**목표:** RecipeManager.ActivateRecipe()의 동작 방식과 이벤트 발생 타이밍, ReloadRecipe()를 통한 변경 검증 방법 명확화 - ---- - -## 1. RecipeManager.ActivateRecipe() 상세 분석 - -### 1.1 메서드 시그니처와 반환값 - -**위치:** `UCRecipeSelectViewModel.cs` Line 112 - -```csharp -int err = _RecipeManager.ActivateRecipe(SelectedRecipe.Id, "USER_via_CustomUI"); -if (err != E1.OK) { - LOG.FuncE("Activate Fail"); -} -ActivatedRecipe = SelectedRecipe; -``` - -**결론: 동기(Synchronous) 작동** - -| 특성 | 값 | 근거 | -|------|-----|------| -| **작동 방식** | **동기** | 반환값 확인 후 즉시 ActivatedRecipe 업데이트 | -| **반환 타입** | int (에러 코드) | E1.OK = 성공, 그 외 = 실패 | -| **성공 조건** | err == E1.OK | 에러 체크 후 진행 | -| **동작 보장** | 반환 시점에 완료됨 | 다음 라인 실행 전 완전히 활성화됨 | - -### 1.2 동기 작동의 의미 - -``` -[ActivateRecipe 호출] - ↓ -[RecipeManager 내부에서: - - 현재 Recipe 종료 - - 새 Recipe 활성화 - - 내부 상태 업데이트] - ↓ -[int err 반환 (이 시점에 모든 작업 완료)] - ↓ -[호출처에서 err == E1.OK 확인] - ↓ -[다음 작업 진행 (ActivatedRecipe 업데이트, 등)] -``` - -**당신의 직관이 맞습니다:** "작업이 끝난 다음 시도해야할 것으로 보입니다" → 100% 정확 - ---- - -## 2. EVENT_CHANGE_RECIPE_INFO 발생 타이밍 분석 - -### 2.1 두 가지 이벤트 경로 - -**Aurora 시스템 이벤트:** -```csharp -// UCRecipeSelectViewModel.cs Line 74 -_EventObservable.AsObservable(AuroraSysEvent.SYS_RECIPE_ACTIVATED) - .Select(x => (RecipeInfo)x.Param) - .Subscribe(x => SelectRecipe(x)); -``` - -- **언제**: ActivateRecipe() 성공 후 -- **누가**: Aurora RecipeManager 내부에서 자동 발행 -- **신뢰성**: Aurora 자체에서 보장 - -**커스텀 이벤트:** -```csharp -// UCScannerObjectTeachingViewModel.cs Line 159 -_Aurora.EventPublisher.Publish(new EventMessage( - "OnActivatedRecipe", - CommonEventIds.EVENT_CHANGE_RECIPE_INFO, - activeRecipe -)); -``` - -- **언제**: RecipeHelper.SaveRecipe() 완료 후 -- **누가**: 사용자가 수동으로 발행 -- **목적**: Aurora 이벤트보다 높은 우선순위 보장 -- **주석**: "Aurora가 저장 후 LASA가 저장하는 동작의 우선순위가 보장 안 되기 때문에" - -### 2.2 이벤트 발생 순서 (실제 흐름) - -``` -[PLC에서 RecipeNum 신호] - ↓ -[SeqAutoRunPTOP에서 ReadWord(RecipeNum)] - ↓ -[RecipeManager.ActivateRecipe(recipeId, reason)] - ↓ -[① ActivateRecipe() 내부: - - Recipe 활성화 완료 - - SYS_RECIPE_ACTIVATED 자동 발행] - ↓ -[② int err 반환 (err == E1.OK)] - ↓ -[③ 호출처에서 EVENT_CHANGE_RECIPE_INFO를 수동으로 발행] - (또는 이미 구독 중인 SYS_RECIPE_ACTIVATED 처리) - ↓ -[④ 이벤트 핸들러 실행: - - RecipeHelper.ReloadRecipe() - - 설정 적용] -``` - -### 2.3 결론: 50ms 지연이 필요한 이유 - -**당신의 분석 정확:** -- ActivateRecipe()는 동기이지만, **내부 이벤트 발행 → 이벤트 핸들러 처리**는 비동기일 수 있음 -- 특히 UI 스레드 관련 작업은 **Dispatcher 큐를 타고 실행**됨 -- **50ms 지연** = 이벤트 핸들러 완료 대기 - -```csharp -// 권장 패턴 -int err = _AuroraEnv.RecipeManager.ActivateRecipe(recipeId, "PLC Request"); -if (err != E1.OK) { - Log.E("Recipe Activation Failed"); - return null; -} - -Log.I($"Recipe Activated: {recipeId}"); -Thread.Sleep(50); // ← 이벤트 핸들러 완료 대기 - -// 이 시점에 ReloadRecipe()가 완료됨 -var loadedRecipe = RecipeHelper.getRecipeInfo(recipeName); -``` - ---- - -## 3. ReloadRecipe()를 통한 변경사항 검증 (더블 체크) - -### 3.1 ReloadRecipe()의 역할 - -**위치:** `RecipeHelper.cs` Line 64-69 - -```csharp -public static void ReloadRecipe(string recipeName) -{ - var camConfig = VisionEnv.AuroraEnv.DeviceManager.GetDeviceConfig( - eCamId.CAM1.ToString()) as CamConfig; - CommonHelper.GetMainPage().UpdateDefaultField(camConfig.ChildImage.ChildBufRowCols); - CommonHelper.GetMainPage().ReloadRecipe(GetTeachingEditorFullPath(recipeName)); -} -``` - -**역할:** -- 파일에서 Recipe 데이터를 **메모리로 로드** -- UI 캔버스에 그릴 수 있도록 준비 -- 스캐너 파라미터 등을 메모리 구조로 변환 - -### 3.2 getRecipeInfo()를 통한 검증 - -**위치:** `RecipeHelper.cs` Line 71 - -```csharp -public static RecipeWeldingLines getRecipeInfo(string recipeName) => - CommonHelper.GetMainPage().getRecipeInfo(GetTeachingEditorFullPath(recipeName)); -``` - -**반환 타입:** `RecipeWeldingLines` -- 현재 메모리에 로드된 Recipe 정보 -- 용접 라인, 파라미터 등 포함 - -### 3.3 변경사항 검증 패턴 (RMSHandlerForScanner 참고) - -**위치:** `RMSHandlerForScanner.cs` Line 132-149 - -```csharp -// Step 1: 이전 Recipe 정보 저장 -var RecipeBeforeChanged = RecipeHelper.getRecipeInfo(recipe.Name); - -// Step 2: Recipe 활성화 -Application.Current.Dispatcher.Invoke(() => { - RecipeHelper.ReloadRecipe(recipe.Name); -}); - -// Step 3: 파라미터 적용 (RMS에서 받은 데이터 적용) -for (int i = 0; i < 3; i++) { - if (!ApplyReportToParam(ScannerCanvasUserCtrl.ScannerParams[i], i)) - return; -} - -// Step 4: 변경사항 저장 및 재로드 -Application.Current.Dispatcher.Invoke(() => { - RecipeHelper.SaveRecipe(recipe.Name); -}); - -// Step 5: 변경된 정보 확인 -var ChangedRecipe = RecipeHelper.getRecipeInfo(recipe.Name); - -// Step 6: 변경사항 비교 (CompareHelper 사용) -var diffs = new ObservableCollection(); -for (int i = 0; i < 3; i++) { - CompareHelper.Fill(RecipeBeforeChanged.ReadOnlyScanParams[i], - ChangedRecipe.ReadOnlyScanParams[i], - diffs, - new CompareHelper.FieldSpec("Draw Speed", x => x.MarkSpeed), - // ... 기타 필드 비교 - ); -} - -// Step 7: 변경사항 로깅 -if (diffs.Count > 0) { - Log.FuncI($"ScanParameter{i + 1} Recipe Changed"); - foreach (var diff in diffs) - Log.V($"{diff.ToString()}"); -} - -// Step 8: Activated Recipe 확인 -var preActivatedRecipe = _aurora.RecipeManager.ActivatedRecipe(); -_aurora.RecipeManager.ActivateRecipe(recipe.Id, "RMS Request"); -``` - -### 3.4 SeqAutoRunPTOP을 위한 더블 체크 패턴 - -```csharp -// ===== 제안 패턴 ===== - -// 이전 Recipe 정보 저장 -var previousRecipeInfo = RecipeHelper.getRecipeInfo( - _AuroraEnv.RecipeManager.ActivatedRecipe().Name); -Log.I($"[CheckPageNo] Previous Recipe: {previousRecipeInfo.Name}"); - -// Recipe 활성화 -int err = _AuroraEnv.RecipeManager.ActivateRecipe(recipeId, "PLC Request"); -if (err != E1.OK) { - Log.E($"[CheckPageNo] Recipe Activation Failed: {err}"); - return null; -} - -// 이벤트 처리 대기 -Thread.Sleep(50); - -// 현재 활성화된 Recipe 확인 -var currentActivated = _AuroraEnv.RecipeManager.ActivatedRecipe(); -Log.I($"[CheckPageNo] Current Activated Recipe: {currentActivated.Id} - {currentActivated.Name}"); - -// Recipe 로드 및 정보 확인 -RecipeHelper.ReloadRecipe(currentActivated.Name); -var currentRecipeInfo = RecipeHelper.getRecipeInfo(currentActivated.Name); - -// 더블 체크: 실제로 변경되었는지 확인 -if (previousRecipeInfo.Name == currentRecipeInfo.Name) { - Log.W($"[CheckPageNo] Warning: Recipe did not change!"); - // Fallback 처리 (재시도, 강제 종료 등) -} - -Log.I($"[CheckPageNo] Recipe Successfully Changed: {previousRecipeInfo.Name} → {currentRecipeInfo.Name}"); - -// 이제 PageNum 읽기 진행 -// ... -``` - ---- - -## 4. 구체적인 검증 방법 - -### 4.1 ActivatedRecipe() 메서드로 확인 - -```csharp -// 활성화 전 -var beforeRecipe = _AuroraEnv.RecipeManager.ActivatedRecipe(); -Log.I($"Before: {beforeRecipe.Id} - {beforeRecipe.Name}"); - -// 활성화 -int err = _AuroraEnv.RecipeManager.ActivateRecipe(newRecipeId, "Test"); -if (err != E1.OK) return null; - -Thread.Sleep(50); - -// 활성화 후 -var afterRecipe = _AuroraEnv.RecipeManager.ActivatedRecipe(); -Log.I($"After: {afterRecipe.Id} - {afterRecipe.Name}"); - -// 검증 -if (beforeRecipe.Id != afterRecipe.Id) { - Log.I("✓ Recipe ID changed"); -} else { - Log.E("✗ Recipe ID did not change (Possible error)"); -} -``` - -### 4.2 getRecipeInfo() 로 상세 정보 확인 - -```csharp -// 활성화 후 -var recipeInfo = RecipeHelper.getRecipeInfo(afterRecipe.Name); -if (recipeInfo == null) { - Log.E("Failed to load recipe info"); - return null; -} - -// 주요 필드 검증 -Log.I($"Recipe Name: {recipeInfo.Name}"); -Log.I($"Layer Count: {recipeInfo.Layers.Count()}"); -Log.I($"Total Lines: {recipeInfo.Layers.Sum(l => l.Objects.Count())}"); - -// Recipe가 제대로 로드되었으면 Canvas에 반영됨 -// _SelectedObjectList = recipeInfo.ActiveLayer.Objects; -``` - -### 4.3 이벤트 핸들러 직접 구독 - -```csharp -// SeqAutoRunPTOP.Sequence_Enter에서: -_recipeChangeCompleted = false; - -_AuroraEnv.EventObservable - .AsObservable(AuroraSysEvent.SYS_RECIPE_ACTIVATED) - .Subscribe(msg => { - Log.I("[Sequence_Enter] SYS_RECIPE_ACTIVATED Event Received"); - _recipeChangeCompleted = true; - }); - -// CheckPageNo에서: -_recipeChangeCompleted = false; - -int err = _AuroraEnv.RecipeManager.ActivateRecipe(recipeId, "PLC Request"); -if (err != E1.OK) return null; - -// 이벤트 대기 (타임아웃 포함) -var startTime = DateTime.Now; -while (!_recipeChangeCompleted && !context.Stopped) { - if (DateTime.Now.Subtract(startTime).TotalSeconds > 5) { - Log.W("[CheckPageNo] Recipe Change Event Timeout"); - break; - } - Thread.Sleep(10); -} - -Log.I("[CheckPageNo] Recipe Change Event Completed"); -``` - ---- - -## 5. 정리: 세 가지 검증 질문에 대한 최종 답변 - -### Q1: ActivateRecipe()가 동기인가? - -**답: YES, 동기이다.** -- 반환값(int err)이 있고, 즉시 확인 가능 -- 반환 시점에 Recipe 활성화 작업 완료됨 -- 하지만 **이벤트 발행 → 이벤트 핸들러 실행**은 다른 스레드/큐를 탈 수 있음 - -### Q2: EVENT_CHANGE_RECIPE_INFO 발생 타이밍은? - -**답: 50ms 지연이 필요하다.** -- ActivateRecipe() 반환 직후 발행되지만, -- 이벤트 핸들러(ReloadRecipe 등)는 비동기로 처리됨 -- Dispatcher 큐를 통해 실행되므로 약간의 지연 필요 -- **권장: Thread.Sleep(50)** 이상 - -### Q3: ReloadRecipe()로 변경사항을 검증해야 하는가? - -**답: YES, 강력히 권장한다.** -- 이전 Recipe 정보 저장 → getRecipeInfo() -- 활성화 및 로드 → ReloadRecipe() -- 현재 Recipe 정보 확인 → getRecipeInfo() -- 비교 → CompareHelper (RMSHandler 참고) -- 변경사항 검증 → diffs 확인 - ---- - -## 6. 다음 단계: 구현 계획 수정 - -기존 "Phase 1-5"를 다음과 같이 정밀화할 수 있습니다: - -### Phase 1: 설정 & 준비 -- ProcessRecipeMapping 클래스 -- PLC WORD 신호 정의 - -### Phase 2: 이벤트 구독 설정 (Sequence_Enter) -```csharp -_recipeChangeCompleted = false; -_AuroraEnv.EventObservable - .AsObservable(AuroraSysEvent.SYS_RECIPE_ACTIVATED) - .Subscribe(msg => _recipeChangeCompleted = true); -``` - -### Phase 3: CheckPageNo 수정 -```csharp -// Step 1: 이전 Recipe 정보 저장 -var prevRecipe = RecipeHelper.getRecipeInfo(...); - -// Step 2: RecipeNum 읽기 & RecipeId 매핑 -int recipeId = mapping.GetRecipeId(plcRecipeNum); - -// Step 3: Recipe 활성화 (동기) -int err = _AuroraEnv.RecipeManager.ActivateRecipe(recipeId, "PLC Request"); -if (err != E1.OK) return null; - -// Step 4: 이벤트 처리 대기 -Thread.Sleep(50); - -// Step 5: 더블 체크 (변경사항 검증) -var currRecipe = RecipeHelper.getRecipeInfo(...); -if (prevRecipe.Name == currRecipe.Name) { - Log.W("Warning: Recipe did not change"); -} - -// Step 6: PageNum 읽기 (기존 로직) -// ... -``` - -### Phase 4: 에러 처리 -- 타임아웃 처리 (5초) -- Recipe 미존재 처리 -- Fallback 로직 - ---- - -## 7. 주의사항 - -1. **Dispatcher.Invoke()의 필요성** - - UI 스레드가 아닌 곳에서 호출 시 필수 - - RecipeHelper.ReloadRecipe()는 UI 스레드에서만 안전 - -2. **Recipe 로드 시간** - - Recipe 크기에 따라 로드 시간 다름 - - 복잡한 Recipe는 50ms 이상 필요할 수 있음 - - **타임아웃은 최소 5초로 설정 권장** - -3. **Rx.NET ObserveOnDispatcher()** - - RMSHandler에서 사용되는 패턴 - - UI 스레드 안전성 보장 - - SeqAutoRunPTOP에서도 고려 - ---- - -**결론:** 당신의 분석 방향이 100% 정확합니다. 코드 수정 전 이 세 가지 검증 항목을 더 깊이 있게 분석하는 것이 매우 현명한 선택입니다. diff --git a/docs/분석/2026/06/16/145623_recipe_tab_crash_analysis.md b/docs/분석/2026/06/16/145623_recipe_tab_crash_analysis.md deleted file mode 100644 index 4de0ea6..0000000 --- a/docs/분석/2026/06/16/145623_recipe_tab_crash_analysis.md +++ /dev/null @@ -1,509 +0,0 @@ -# Recipe 탭 크래시 분석 보고서 — IO Device DI 읽기 실패 - -**분석 일시:** 2026-06-16 14:56:23 -**대상:** ILSOS_PrismaticRD_PTOP Recipe 탭 시스템 셧다운 -**오류:** `System.Exception: ERR_IO_FAIL_TO_GET_BIT; ERROR CODE:3102` - ---- - -## 1. 오류 스택 트레이스 분석 - -### 1.1 호출 체인 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ [호출 경로] │ -├─────────────────────────────────────────────────────────────────────┤ -│ 1. System.Windows.Threading.DispatcherTimer.FireTick() │ -│ (타이머 이벤트 발생) │ -│ │ -│ 2. UCScannerObjectTeachingControlBoxViewModel.OnMonitoring() │ -│ 위치: Recipes/UCScannerObjectTeachingControlBoxViewModel.cs:181 │ -│ (타이머 콜백 핸들러) │ -│ │ -│ 3. IODevice.ReadDI(int index) │ -│ 위치: AuroraIO/IODevice.cs:212 │ -│ (디지털 입력 읽기 메서드) │ -│ │ -│ 4. AuroraIO.AJINEXTEK.AjinIO.ReadDiBit(int index) │ -│ 위치: AuroraIO/Vendor/AJINEXTEK/AjinIO.cs:510 │ -│ ❌ 예외 발생 │ -│ "ERR_IO_FAIL_TO_GET_BIT; ERROR CODE:3102" │ -│ │ -│ 5. CAXD.AxdiReadInport(index, ref data) ← 오류의 근원 │ -│ (Ajin IO 드라이버 호출 — 하드웨어 통신 실패) │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -### 1.2 에러 코드 분석: 3102 - -**Ajin 모션 컨트롤러 라이브러리 에러 코드 3102:** - -| 에러 코드 | 의미 | 가능한 원인 | -|----------|------|-----------| -| **3102** | `AXT_ERR_INVALID_PORT` | ① DI 포트 인덱스 범위 초과
② IO 모듈이 초기화되지 않음
③ IO 포트 설정 불일치
④ 하드웨어 미연결 | - ---- - -## 2. 코드 분석 - -### 2.1 오류 발생 지점: UCScannerObjectTeachingControlBoxViewModel.cs - -```csharp -[Line 171-198] -private void OnMonitoring(object sender, EventArgs e) -{ - // Laser Device 파일럿 상태 읽기 (성공) - if (_Laser.GetPilotLaserStatus()) { - IsGuidBeam = true; - } else { - IsGuidBeam = false; - } - - // ❌ 이 부분에서 크래시 발생 (Line 181) - if (_IODevice.ReadDI(9) && // ← ERROR CODE:3102 (DI 9 읽기 실패) - _IODevice.ReadDI(24)) // (도달하지 않음) - { - IsLaserReady = true; - } else { - IsLaserReady = false; - } -} -``` - -**타이머 시작 조건:** - -```csharp -[Line 151-159] -private void VisibleChanged(object obj) -{ - bool visible = (bool)obj; - if (visible) { - _MonitorTimer.Start(); // ← Recipe 탭이 보여질 때 시작 - } else { - _MonitorTimer.Stop(); - } -} - -[Line 127-130] -_MonitorTimer = new DispatcherTimer(); -_MonitorTimer.Tick += OnMonitoring; -_MonitorTimer.Interval = TimeSpan.FromMilliseconds(100); // 100ms마다 OnMonitoring 호출 -``` - -### 2.2 IO 인덱스 읽음 - -```csharp -[IODevice.cs:212-214] -public bool ReadDI(int index) => _iOUnit.ReadDiBit(index); -public bool ReadDO(int index) => _iOUnit.ReadDoBit(index); -public void WriteDO(int index, bool on) => _iOUnit.WriteDoBit(index, on); -``` - -### 2.3 Ajin IO 드라이버 호출 (실패 지점) - -```csharp -[AjinIO.cs:501-511] -public override bool ReadDiBit(int index) -{ - uint data = 0; - - // Ajin IO 라이브러리 호출 - int err = (int)CAXD.AxdiReadInport(index, ref data); - - if (err != (int)AXT_FUNC_RESULT.AXT_RT_SUCCESS) // err = 3102 - { - // 예외 발생 - throw new Exception(string.Format("ERR_IO_FAIL_TO_GET_BIT;ERROR CODE:{0}", err)); - // ↑ 3102 - } - return Convert.ToBoolean(data); -} -``` - ---- - -## 3. 근본 원인 분석 - -### 3.1 **DI 인덱스 범위 오류** (가능성 높음) - -#### 현상 -- 기존 프로젝트 (ILSOS_PrismaticScanner)에서 코드를 copy-paste하여 ILSOS_PrismaticRD_PTOP에 적용 -- 기존 프로젝트의 하드웨어 설정과 PTOP의 하드웨어 설정이 다름 - -#### 의심 지점 - -**기존 코드 (ILSOS_PrismaticScanner):** -```csharp -// ILSOS_PrismaticScanner의 Recipe 탭 모니터링 -_IODevice.ReadDI(9) // Scanner 하드웨어의 DI 포트 9 (유효) -_IODevice.ReadDI(24) // Scanner 하드웨어의 DI 포트 24 (유효) -``` - -**현재 코드 (ILSOS_PrismaticRD_PTOP):** -```csharp -// PTOP 하드웨어로 복사되었으나 핀 맵이 다름 -_IODevice.ReadDI(9) // PTOP 하드웨어에는 DI 9가 없음 또는 다른 목적 -_IODevice.ReadDI(24) // PTOP 하드웨어에는 DI 24가 없음 또는 다른 목적 -``` - -#### 검증 방법 - -**PTOP IO 설정 파일 확인:** -``` -ILSOS_PrismaticRD_PTOP/ -├── settings/ -│ └── IO 설정 파일 (IOConfig.xml 또는 유사) -└── EquipDef.cs ← DI/DO 인덱스 정의 확인 -``` - -**EquipDef.cs에서 DI 9, 24의 정의 확인:** -```csharp -// 있어야 함: -public const int DI_SOMETHING = 9; // 무엇을 읽는가? -public const int DI_LASER_READY = 24; // 또는 다른 용도? - -// 없을 수도 있음 → 인덱스 미스매치 -``` - ---- - -### 3.2 **IO 모듈 미초기화** (가능성 중간) - -#### 현상 -Recipe 탭 진입 시점에 IO 모듈이 완전히 초기화되지 않음 - -#### 의심 지점 - -**IODevice 초기화 순서:** - -```csharp -[IODevice.cs:146-198 DoActivate] -protected override int DoActivate(IDeviceConfig config) -{ - // ... 코드 생략 ... - - int nRet = _iOUnit.Open(m_IOUnitConfig.DIDOIP); // Step 1: 포트 오픈 - if (nRet != 0) - return nRet; - - LoadIOTable(); // Step 2: IO 테이블 로드 - - nRet = _iOUnit.Initialize(); // Step 3: 초기화 - - // 이 시점에서 Recipe 탭이 활성화되면? - // → OnMonitoring이 즉시 호출될 수 있음 - // → IO 테이블 로드 전에 DI 읽기 시도 가능 -} -``` - -**EquipmentSetup에서 IODevice 활성화 타이밍:** - -``` -1. Application 시작 -2. EquipmentSetup.Activate() 호출 - ├─ 각 Device 순차 활성화 - ├─ IODevice 활성화 (DoActivate 호출) - │ └─ LoadIOTable() → _iOUnit.Initialize() - │ (이 순간 IO 포트들이 설정됨) - │ - └─ View/ViewModel 초기화 - └─ Recipe 탭 ViewModel 생성 - └─ _MonitorTimer 등록 (하지만 아직 시작 안 함) - -3. UI 표시 - └─ Recipe 탭 클릭 → VisibleChanged(true) - └─ _MonitorTimer.Start() ❌ 크래시 (IO 미초기화 상태) -``` - ---- - -### 3.3 **하드웨어 미연결** (가능성 낮음) - -IO 모듈이 PC에 연결되지 않은 경우, 모든 DI 읽기가 실패합니다. - ---- - -## 4. 코드 추적: 기존 프로젝트와의 차이 - -### 4.1 ILSOS_PrismaticScanner vs ILSOS_PrismaticRD_PTOP - -#### ILSOS_PrismaticScanner (정상 작동) -``` -ILSOS_PrismaticScanner/ -├── EquipDef.cs -│ └─ DI 9, 24 정의됨 ✓ -├── Recipes/ -│ └─ UCScannerObjectTeachingControlBoxViewModel.cs -│ └─ OnMonitoring()에서 DI 9, 24 읽음 ✓ -└── settings/ - └─ IOConfig.xml (또는 유사) - └─ DI 포트 매핑 9, 24 정의됨 ✓ -``` - -#### ILSOS_PrismaticRD_PTOP (오류 발생) -``` -ILSOS_PrismaticRD_PTOP/ -├── EquipDef.cs -│ └─ DI 9, 24 정의? ??? (확인 필요) -├── Recipes/ -│ └─ UCScannerObjectTeachingControlBoxViewModel.cs (복사됨) -│ └─ OnMonitoring()에서 DI 9, 24 읽음 ❌ (PTOP 하드웨어에 없음) -└── settings/ - └─ IOConfig.xml 또는 설정 파일 - └─ DI 포트 매핑 정의? ??? (PTOP별 설정 필요) -``` - -### 4.2 같은 코드, 다른 하드웨어 - -**비전 시스템의 Laser Ready 신호를 읽는 로직:** - -- **Scanner**: Laser 보기 헤드에서 발생하는 신호 - - Pilot Laser 상태 (위치 확인용) - - Laser Ready 신호 (발사 가능 여부) - -- **PTOP (Prismatic Rectangular)**: 배터리 각형 용접 - - 고정 레이저 (이동하지 않음) - - Stage A / Stage B 교번 스캔 - - Laser Ready 신호의 의미가 다를 수 있음 - ---- - -## 5. 해결 방안 - -### **방안 A: DI 인덱스 검증 및 수정** (권장) - -**Step 1: PTOP의 실제 IO 설정 확인** - -```bash -# 1. PTOP의 IO 설정 파일 확인 -ILSOS_PrismaticRD_PTOP/settings/ 폴더에서 -IOConfig, IO.xml, 또는 유사 파일 검색 - -# 2. PTOP의 EquipDef.cs 확인 -상수로 정의된 DI/DO 인덱스 확인: -예: - public const int DI_SOMETHING = X; - public const int DI_LASER_READY = Y; - -# 3. 하드웨어 사양서 확인 -PTOP 머신의 IO 포트 매핑 문서 -→ DI 9, 24가 실제로 존재하는지 확인 -``` - -**Step 2: 올바른 DI 인덱스로 수정** - -만약 PTOP의 Laser Ready 신호가 DI 9, 24가 아니라면: - -```csharp -// 수정 전 (잘못된 코드) -if (_IODevice.ReadDI(9) && _IODevice.ReadDI(24)) - -// 수정 후 (PTOP의 실제 DI 인덱스 사용) -// 예: PTOP의 Laser Ready가 DI 5, 15라면 -if (_IODevice.ReadDI(5) && _IODevice.ReadDI(15)) - -// 또는 EquipDef에서 상수 사용 -if (_IODevice.ReadDI(CommonDeviceIds.DI_LASER_READY_1) && - _IODevice.ReadDI(CommonDeviceIds.DI_LASER_READY_2)) -``` - -**Step 3: Recipe 탭 모니터링 로직 검증** - -Recipe 탭의 Laser Ready 상태가 PTOP에 필요한가? -- 필요하면 → 올바른 DI 인덱스 사용 -- 불필요하면 → 모니터링 로직 제거 또는 비활성화 - -```csharp -// 방안 A-1: 불필요하면 모니터링 제거 -private void OnMonitoring(object sender, EventArgs e) -{ - // PTOP에서는 Laser Ready 상태를 Recipe 탭에서 읽을 필요 없음 - // OnMonitoring 전체 로직 제거 또는 주석 처리 -} - -// 방안 A-2: 오류 처리 추가 (임시) -private void OnMonitoring(object sender, EventArgs e) -{ - try - { - if (_IODevice.ReadDI(9) && _IODevice.ReadDI(24)) - { - IsLaserReady = true; - } - else - { - IsLaserReady = false; - } - } - catch (Exception ex) - { - Log.E($"[OnMonitoring] DI 읽기 실패: {ex.Message}"); - IsLaserReady = false; // 기본값으로 설정 - } -} -``` - ---- - -### **방안 B: 조건부 모니터링** (안정성 향상) - -PTOP과 Scanner 중 어느 것인지를 런타임에 판별하여 모니터링 수행: - -```csharp -private void OnMonitoring(object sender, EventArgs e) -{ - // PTOP인지 Scanner인지 판별 - bool isPTOP = _SystemParameter.IsEquipmentType("PTOP"); - - if (isPTOP) - { - // PTOP: 모니터링 스킵 또는 PTOP별 로직 - IsLaserReady = true; // 또는 PTOP의 실제 신호 읽기 - } - else - { - // Scanner: 기존 로직 유지 - if (_IODevice.ReadDI(9) && _IODevice.ReadDI(24)) - { - IsLaserReady = true; - } - else - { - IsLaserReady = false; - } - } -} -``` - ---- - -### **방안 C: IO 초기화 대기** (근본 원인이 미초기화인 경우) - -```csharp -private void VisibleChanged(object obj) -{ - bool visible = (bool)obj; - if (visible) - { - // IO 모듈이 초기화될 때까지 대기 - if (_IODevice._iOUnit.IsConnected()) - { - _MonitorTimer.Start(); - } - else - { - Log.W("[Recipe Tab] IO 모듈이 아직 초기화되지 않음. 모니터링 지연"); - // 100ms 후 재시도 - Task.Delay(100).ContinueWith(_ => - { - Dispatcher.CurrentDispatcher.Invoke(() => - { - if (_IODevice._iOUnit.IsConnected()) - { - _MonitorTimer.Start(); - } - }); - }); - } - } - else - { - _MonitorTimer.Stop(); - } -} -``` - ---- - -## 6. 즉시 조치사항 - -### **우선순위 1: DI 인덱스 확인** (1시간) - -```bash -ILSOS_PrismaticRD_PTOP 프로젝트에서: -1. EquipDef.cs 열기 → DI 상수 정의 확인 -2. settings/ 폴더에서 IO 설정 파일 찾기 -3. PTOP 하드웨어 사양서 또는 설정 문서 확인 -→ DI 9, 24이 PTOP에 실제로 존재하는가? -``` - -### **우선순위 2: Laser Ready 신호의 필요성 확인** (30분) - -```bash -Recipe 탭에서 Laser Ready 상태가 필요한가? -1. ILSOS_PrismaticScanner와 ILSOS_PrismaticRD_PTOP의 - 레이저 시스템 비교 -2. PTOP에서 Laser Ready가 필요하면: - → 올바른 DI 인덱스 찾기 - 필요 없으면: - → OnMonitoring 로직 제거 -``` - -### **우선순위 3: 임시 오류 처리** (30분) - -```csharp -// OnMonitoring에 try-catch 추가 (방안 A-2) -// → 크래시 방지 + 로그 확인 용이 -// → 근본 원인 파악 후 제거할 것 -``` - ---- - -## 7. 검증 포인트 - -### 7.1 수정 후 테스트 - -1. **Recipe 탭 진입 테스트** - ``` - 1. 애플리케이션 시작 - 2. 초기화 완료 확인 - 3. Recipe 탭 클릭 - 4. 시스템 셧다운 여부 확인 ✓ - ``` - -2. **로그 확인** - ``` - OnMonitoring에서 DI 값을 로깅하여 실제로 읽어지는지 확인: - Log.I($"[OnMonitoring] DI(9)={_IODevice.ReadDI(9)}, DI(24)={_IODevice.ReadDI(24)}"); - ``` - -3. **IO 하드웨어 상태 확인** - ``` - IODevice 초기화 시 IsConnected() 상태 로깅: - Log.I($"[IODevice] IsConnected={_IODevice._iOUnit.IsConnected()}"); - ``` - ---- - -## 8. 참고: Laser Ready 신호 아키텍처 - -### Scanner vs PTOP 비교 - -| 항목 | ILSOS_PrismaticScanner | ILSOS_PrismaticRD_PTOP | -|------|------------------------|----------------------| -| **레이저 타입** | 스캐너 (이동) | 고정 + Stage 교번 | -| **Pilot Laser** | 위치 확인용 신호 있음 | 고정이므로 필요성 낮음 | -| **Laser Ready DI** | DI 9, 24 | ? (확인 필요) | -| **Recipe 탭 용도** | 레이저 상태 모니터링 | ? (확인 필요) | - ---- - -## 9. 결론 - -| 단계 | 조치 | 예상 시간 | -|------|------|---------| -| **1. 근본 원인 확인** | DI 인덱스 + IO 초기화 상태 확인 | 1시간 | -| **2. 임시 조치** | try-catch 오류 처리 추가 | 30분 | -| **3. 근본 해결** | 올바른 DI 인덱스로 수정 또는 모니터링 제거 | 1시간 | -| **4. 테스트** | Recipe 탭 진입 테스트 + IO 로깅 확인 | 1시간 | - ---- - -**작성자:** Claude Code Analysis -**참고 파일:** -- `UCScannerObjectTeachingControlBoxViewModel.cs:171-198` — OnMonitoring 메서드 -- `IODevice.cs:212-214` — ReadDI 메서드 -- `AjinIO.cs:501-511` — ReadDiBit 구현 -- 오류 로그: `sys_error/20260616_103211.148.log` - diff --git a/docs/분석/2026/06/17/143131_laser_ready_di9_analysis.md b/docs/분석/2026/06/17/143131_laser_ready_di9_analysis.md deleted file mode 100644 index b7b0945..0000000 --- a/docs/분석/2026/06/17/143131_laser_ready_di9_analysis.md +++ /dev/null @@ -1,187 +0,0 @@ -# Recipe View Laser Ready (DI 9) — IsLaserReady 조건 분석 - -**분석 일시:** 2026-06-17 14:31:31 -**대상:** `WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs` -**비교 대상:** `docs/기존파일비교/UCScannerObjectTeachingControlBoxViewModel.cs` (이전 MOTF 프로젝트 버전) - ---- - -## 0. 요청 내용 (사용자) - -- 현장 RTC5보드는 갈보모터 X, Y 피드백값을 가져오는 게 없어서 DEVICE 상에서 COMMAND 값을 불러오도록 체크 해제 후 진행. 정확한 피드백값은 확인 불가한 상태. -- Laser 동작은 I/O로 하며, 프로그램 내에서 사용할 때 Simulation으로 동작한다. -- Recipe View에서 Laser Ready 부분이 DI(9)를 보고 있는데, 해당 BIT가 Laser를 IO On 해도 번들을 켜도 안 켜져서 전선 다시 확인 중인 상태. simulation이어서 true가 되긴 해야 하지만 한번 확인해볼 것. 이전 MOTF 작업하던 프로젝트랑 비교하기. -- **요청 1:** 현재 프로젝트의 `IsLaserReady` 변수 조건과 `docs/기존파일비교`의 같은 파일명 버전을 비교해서 달라진 점 분석. -- **요청 2:** IO(9)를 보고 있는 모든 코드 위치 파악. -- **요청 3:** 시뮬일 경우 버튼을 누르면 IsLaserReady 상태값이 변경되도록 하는 방안 검토 (DI(9)가 TRUE가 되지 않아 테스트가 막힘). — *코드 수정은 보류, 방안만 정리.* - ---- - -## 1. 결론 요약 - -- 점등이 안 되는 직접 원인은 **IsLaserReady 판정 소스가 바뀐 것**: - - 기존(MOTF): `_Laser.GetLaserReady()` (Laser 디바이스 상태, simulation이면 true) - - 현재: `_IODevice.ReadDI(9)` (**실제 하드웨어 DI 9번 비트**) -- 현재 코드는 시뮬레이션 Laser 디바이스가 아니라 **물리 DI(9)** 를 읽으므로, 배선으로 DI(9)가 실제로 들어오지 않으면 simulation이어도 true가 되지 않음. -- 작업일지에 적은 "simulation이면 true여야 한다"는 **기존 코드 기준**이며, 현재 코드는 그렇게 동작하지 않음. -- DI(9)는 "레이저 Ready 출력 → 장비 입력" 신호이므로 **OnMonitoring이 DI(9)를 읽는 것 자체는 설계상 정상.** 점등 미동작은 코드가 아니라 **레이저 Ready 출력 → DI(9) 결선** 문제 → 전선 확인이 맞는 방향. - ---- - -## 2. 코드 비교 — IsLaserReady 판정부 - -### 2.1 OnMonitoring() - -**기존(MOTF):** -```csharp -if (_Laser.GetLaserReady()) { - IsLaserReady = true; -} else { - IsLaserReady = false; -} -``` - -**현재:** -```csharp -try -{ - if (_IODevice.ReadDI(9)) // ← 물리 DI 9번을 직접 읽음 - { - IsLaserReady = true; - } - else - { - IsLaserReady = false; - } -} -catch (Exception ex) -{ - Log.E($"[OnMonitoring] DI 읽기 실패: {ex.Message}"); - IsLaserReady = false; -} -``` - -### 2.2 PerformCmdLaserReadyCheck() — Ready Check 버튼 - -**기존(MOTF):** -```csharp -if (!_Laser.GetLaserReady()) { - _Laser.LaserOn(); // 실질적으로 Laser Ready On -} else { - _Laser.LaserOff(); -} -``` - -**현재:** -```csharp -if(toggleIO) -{ - _IODevice._iOUnit.WriteDoBit(9, true); // ← DO 9 출력 - _IODevice._iOUnit.WriteDoBit(13, true); // ← DO 13 출력 - toggleIO = false; -} -else -{ - _IODevice._iOUnit.WriteDoBit(9, false); - _IODevice._iOUnit.WriteDoBit(13, false); - toggleIO = true; -} -``` - -### 2.3 부수 차이 (동작엔 직접 영향 적음) -- 필드 선언: 기존 `private readonly IODevice _IODevice;` → 현재 `private IODevice _IODevice;` (readonly 제거). 추가 필드 `public bool toggleIO = true;`. -- using 추가: `WeldingCommon`, `WeldingCommon.Devices.Laser`, `using static ...PAIX_PrismaticScanner`. -- 기존 OnMonitoring의 LaserOnLamp `WriteDoBit` 주석 블록은 현재 제거됨. -- `PerformCmdRunTeachingRun`: 기존은 `PopupSequenceDiagram(...)` 호출, 현재는 주석 처리. `OnCompleteTeachingRun`도 기존은 성공 처리/`ShowWarningToast` 활성, 현재는 주석 처리. - ---- - -## 3. 채널 충돌 — 버튼(DO9) vs 모니터링(DI9) - -``` -[버튼] WriteDoBit(9,true) ──▶ DO 9 (출력) - │ ?? 물리 배선 필요 ?? -[모니터] ReadDI(9) ◀─────────── DI 9 (입력) -``` - -- 버튼이 쓰는 것은 **DO9/DO13 출력**, 모니터링이 읽는 것은 **DI9 입력** → 별개 채널. -- DO9에 써도 DI9 입력으로 되돌아오려면 물리 배선(루프백/외부 결선)이 필요. 이 결선이 없으면 버튼을 눌러도 IsLaserReady가 안 켜짐 → 지금 전선 확인 중인 이슈와 일치. - ---- - -## 4. IO(9)를 보고 있는 모든 코드 위치 - -리터럴 `9`로 IO 9번에 접근하는 코드는 **한 파일, 3곳뿐**: - -`WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs` - -| 라인 | 코드 | 종류 | 의미 | -|---|---|---|---| -| 182 | `if (_IODevice.ReadDI(9))` | DI 9 읽기 | Laser Ready 판정 (모니터링) | -| 462 | `_IODevice._iOUnit.WriteDoBit(9, true)` | DO 9 쓰기 | Ready Check 버튼 ON | -| 468 | `_IODevice._iOUnit.WriteDoBit(9, false)` | DO 9 쓰기 | Ready Check 버튼 OFF | - -> 같은 버튼이 인접 라인에서 `WriteDoBit(13, …)`도 함께 씀. - -### enum 매핑상 9번의 실제 의미 (`AuroraDevices/AuroraIO/Vendor/PAIX/PAIXIO.cs`) - -DI와 DO는 별개이고, 9번이 가리키는 게 서로 다름: - -``` -ePDIAddress (입력) ePDOAddress (출력) - DoorLockOpen4 = 7 WorkLEDOn = 7 - (8 - 미지정/미사용) (8 - 미지정) - (9 - 미지정/미사용) ◀DI9 ResetSwitchLamp = 9 ◀DO9 - DoorLockCloseMonitoring=10 ... - ModeKeySolOn = 13 ◀DO13 -``` - -- **DI 9번**: `ePDIAddress`에 이름이 없는 raw 비트 (Laser Ready 정식 주소로 매핑돼 있지 않음) -- **DO 9번**: `ResetSwitchLamp` (리셋 스위치 램프) -- **DO 13번**: `ModeKeySolOn` (모드키 솔레노이드) -- → 버튼이 켜는 DO9/13은 enum상 레이저와 무관한 비트이며, DI9도 미매핑 raw 입력. 코드 레벨에서 임시 매핑된 자리. -- DO9(`ResetSwitchLamp`)·DO13(`ModeKeySolOn`)을 enum 이름으로 호출하는 코드는 없음 → IO 9번 관련 코드는 위 3줄이 전부. - ---- - -## 5. 시뮬일 때 버튼으로 IsLaserReady 토글하는 방안 (수정 보류 — 방안만) - -선택안: **버튼이 IsLaserReady를 직접 토글.** 단, `OnMonitoring()`이 100ms마다 `ReadDI(9)`로 덮어쓰므로 시뮬일 때 모니터링 덮어쓰기를 같이 막아야 함. - -- 시뮬 판별은 `_IODevice.IsSimul` 로 가능 (Device 기반 public 속성, 외부 사용 예: `SeqAlarmMonitor.cs:466` `_IO.IsSimul`). - -**(1) `OnMonitoring()` — 시뮬일 때 DI(9) 읽기 건너뛰기** -```csharp -if (!_IODevice.IsSimul) -{ - try - { - if (_IODevice.ReadDI(9)) { IsLaserReady = true; } - else { IsLaserReady = false; } - } - catch (Exception ex) { Log.E($"[OnMonitoring] DI 읽기 실패: {ex.Message}"); IsLaserReady = false; } -} -``` - -**(2) `PerformCmdLaserReadyCheck()` — 시뮬일 때 직접 토글** -```csharp -if (_IODevice.IsSimul) -{ - IsLaserReady = !IsLaserReady; - return; -} -// 실장: 기존 DO9/13 Write 로직 그대로 -``` - -- 시뮬: 버튼 → IsLaserReady 토글, 모니터링 미관여 → 점등 확인 가능 -- 현장: 기존 로직 그대로 (DI9 읽기 / DO9·13 Write) — 변경 없음 - -> **상태:** 위 방안은 검토만 완료. 실제 코드 수정은 적용하지 않음 (사용자 보류 요청). - ---- - -## 6. 확인/해결 방향 - -- DI(9) 판정을 유지하려면 **레이저 Ready 출력 → DI(9) 물리 결선**을 확인할 것 (전선 점검이 맞는 방향). -- 시뮬레이션에서 동작 확인이 목적이면 5절 방안 적용을 검토. -- 버튼이 출력하는 DO9(`ResetSwitchLamp`)·DO13(`ModeKeySolOn`)이 실제 레이저 Enable 배선과 맞는지 확인 후, 맞지 않으면 버튼 동작을 의미에 맞게 재정의 필요. diff --git a/docs/분석/2026/06/17/143900_axis_position_interlock_setting_location.md b/docs/분석/2026/06/17/143900_axis_position_interlock_setting_location.md deleted file mode 100644 index 0a598a2..0000000 --- a/docs/분석/2026/06/17/143900_axis_position_interlock_setting_location.md +++ /dev/null @@ -1,162 +0,0 @@ -# PLC 전축 위치값 모니터링 InterLock — Setting 추가 위치 분석/추천 - -**분석 일시:** 2026-06-17 14:39:00 -**대상:** PTOP 프로젝트 Settings 구조 -**목표:** PLC에서 받는 전축(全軸) 위치값을 모니터링하기 위한 기준값(범위)을 Setting에 미리 저장하고, 특정 범위를 벗어나면 InterLock을 거는 기능. 저장할 Setting 클래스/위치/그룹명 추천. - ---- - -## 0. 요청 내용 (사용자) - -- PLC에서 전축 위치값을 모니터링할 값을 미리 Setting에 저장해두고, 특정 범위를 벗어나면 InterLock을 걸려고 함. -- Settings에 여러 Setting 클래스들이 있는데 **어느 클래스 어느 위치에 추가**하는 게 좋을지 추천. -- 적당한 위치가 없으면 **어느 클래스에 어떤 그룹명**으로 추가하면 되는지도 추천해서 docs 분석에 기록. - ---- - -## 1. 결론 요약 - -- **기존 클래스 중 "딱 맞는" 자리는 없음.** 전축 위치값의 범위(Min/Max) InterLock은 *축마다 반복되는 구조화 데이터*라, 평면(scalar) 속성 위주인 `SystemParameter`/`SequenceSetting`에 그대로 넣기엔 부적합. -- **추천(권장): 신규 전용 Setting 클래스 신설.** - - 클래스: `AxisPositionInterlockSetting : ISettingItem` - - 위치: `WeldingComponents/WeldingCommon/Settings/AxisPositionInterlockSetting.cs` - - 등록 ID: `CommonSettingIds.AxisPositionInterlock = "AxisPositionInterlock"` (EquipDefCommon.cs) - - 등록: `CommonEquipmentSetup.RegisterSettings()`에 `register(CommonSettingIds.AxisPositionInterlock, new AxisPositionInterlockSetting());` - - 그룹명(Category): `"Axis Position Interlock"` - - 축별 항목 구조는 기존 `TeachingPositionSetting.SettingPosition`(PrismaticGun) 패턴을 차용. -- **간이 대안(축 수가 적고 빠른 적용이 목적):** `SequenceSetting`에 `[Category("Axis Position Interlock")]` 그룹으로 전역 On/Off + 축별 Min/Max scalar 추가. 단, 축 수 확장에 약함. - ---- - -## 2. 현재 Settings 구조 (확인 결과) - -### 2.1 Setting 클래스 패턴 -- 모든 Setting은 `Aurora.Api.ISettingItem` 구현. -- 프로퍼티에 `PropertyTools.DataAnnotations`의 `[Category("그룹명")]`, `[DisplayName(...)]`, `[Browsable]`, `[ReadOnly]` 부착 → Setting UI에 그룹/표시명으로 노출. - -### 2.2 등록 흐름 -- ID 상수: `WeldingComponents/WeldingCommon/EquipDefCommon.cs` 의 `class CommonSettingIds` - ```csharp - public const string SystemParameter = "SystemParameterILSOS"; - public const string DoorInterlock = "DoorInterlock"; - public const string Sequence = "Sequence"; - // ... - ``` -- 인스턴스 등록: `WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs` 의 `RegisterSettings(SettingRegister register)` - ```csharp - register(CommonSettingIds.SystemParameter, new SystemParameter()); - register(CommonSettingIds.LaserPowerCalParam, new LaserPowerCalParam()); - register(CommonSettingIds.Sequence, new SequenceSetting()); - register(CommonSettingIds.DoorInterlock, new DoorInterlockSetting()); - register(CommonSettingIds.RmsDatas, new SettingRmsDataList()); - ``` -- 소비(읽기): 시퀀스/뷰에서 `AuroraEnv.SettingEntry[CommonSettingIds.XXX] as XXX`, - 모니터 시퀀스에서는 `context.Settings[CommonSettingIds.Sequence] as SequenceSetting` (예: `SeqStatusMonitor.StartNode`). - -### 2.3 후보 클래스 평가 -| 클래스 | 위치 | 성격 | 전축 위치 InterLock 적합성 | -|---|---|---|---| -| `SystemParameter` | WeldingCommon/Settings | 전역 평면 파라미터 다수, `[Category]` 그룹 많음 | 축별 반복 구조엔 부적합. 단일 전역 토글 정도만 가능 | -| `SequenceSetting` | WeldingCommon/Settings | 모니터링 On/Off 토글 모음("Alarm Monitoring Device" 등), 모니터 시퀀스가 직접 읽음 | **전역 Enable 토글** 자리로는 적합. 축별 Min/Max 보관엔 부적합 | -| `DoorInterlockSetting` | WeldingCommon/Views/DoorInterlock | Door/Motor 인터락 전용(비번/해제 등) | 의미는 "인터락"으로 가깝지만 Door 도메인 전용 → 혼입 비권장 | -| `TeachingPositionSetting` | **PrismaticGun**/settings | 축별 X/Y/Z 위치 + ±Limit 배열(`SettingPosition[]`) | **구조 선례로 가장 유사**. 단 PrismaticGun 전용이라 PTOP에서 그대로 못 씀 → 패턴만 차용 | - -> 즉, "축별 범위" 구조의 정확한 선례는 `TeachingPositionSetting` 하나뿐이고, PTOP 공통 영역에는 대응 클래스가 없음. - -### 2.4 PLC 전축 위치값 매핑 현황 -- `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs` 의 `eWordInput`은 - Cell/Lot ID·Page·Step·Slot 위주이고 **축 위치값 워드는 아직 미정의(대부분 `EMPTY_xx`)**. -- 따라서 본 기능은 (1) Setting 신설 + (2) **PLC `eWordInput`에 축 위치 워드 주소 매핑 추가**가 함께 필요. -- 위치값 읽기는 `PLCController.Logic.cs`의 `ReadWord(eWordInput)` / `ReadWords(start,end)` 사용. - ---- - -## 3. 권장안 — 신규 전용 Setting 클래스 - -### 3.1 클래스 (스케치) -`WeldingComponents/WeldingCommon/Settings/AxisPositionInterlockSetting.cs` -```csharp -namespace WeldingCommon.Settings -{ - public class AxisPositionInterlockSetting : ISettingItem - { - [Category("Axis Position Interlock")] - [DisplayName("전축 위치 InterLock 사용")] - public bool UseAxisPositionInterlock { get; set; } = false; - - // 축별 범위 항목 (TeachingPositionSetting.SettingPosition 패턴 차용) - public AxisInterlockItem[] Axes { get; set; } - - public AxisPositionInterlockSetting() - { - // 전축 정의: 장비 축 개수에 맞춰 초기화 - Axes = new[] - { - new AxisInterlockItem("AxisX"), - new AxisInterlockItem("AxisY"), - new AxisInterlockItem("AxisZ"), - // ... 전축 - }; - } - } - - public class AxisInterlockItem : NotifyPropertyChangedBase - { - public string Name { get; set; } - public bool Use { get; set; } = true; // 축별 On/Off - public double Min { get; set; } // 하한 - public double Max { get; set; } // 상한 - // 필요 시: PLC 워드 주소 인덱스, AlarmLevel(Heavy/Light) 등 추가 - - public AxisInterlockItem() { } - public AxisInterlockItem(string name) { Name = name; } - } -} -``` - -### 3.2 ID 상수 추가 -`WeldingComponents/WeldingCommon/EquipDefCommon.cs` → `class CommonSettingIds` -```csharp -public const string AxisPositionInterlock = "AxisPositionInterlock"; -``` - -### 3.3 등록 -`WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs` → `RegisterSettings()` -```csharp -register(CommonSettingIds.AxisPositionInterlock, new AxisPositionInterlockSetting()); -``` - -### 3.4 InterLock 판정 로직 위치 -- 모니터 시퀀스에서 주기적으로 PLC 축 위치를 읽어 Min/Max 비교 → 벗어나면 InterLock/알람. -- 권장 위치: `Machines/ILSOS_PrismaticRD_PTOP/Sequences/Monitoring/SeqStatusMonitor.cs` (또는 `SeqAlarmMonitor.cs`). - - `StartNode`에서 `context.Settings[CommonSettingIds.AxisPositionInterlock] as AxisPositionInterlockSetting` 캐싱. - - `UpdateStatus`/`UpdateIO` 루프에서 `ReadWord(eWordInput.축위치)` ↔ `Axes[i].Min/Max` 비교. - - 위반 시 `AlarmManager`로 알람 발생 + 필요 시 `_plc.WriteBit(...)`로 인터락 신호 출력(기존 `PC_HEAVY_ALARM_STATUS` 패턴 참조). - ---- - -## 4. 간이 대안 (빠른 적용용) - -축 수가 적고 구조화가 과하다면, `SequenceSetting`에 그룹만 추가: -```csharp -[Category("Axis Position Interlock")] -public bool UseAxisPositionInterlock { get; set; } = false; -public double AxisXMin { get; set; } -public double AxisXMax { get; set; } -public double AxisYMin { get; set; } -public double AxisYMax { get; set; } -// ... -``` -- 장점: 신규 클래스/등록 불필요(이미 모니터가 `CommonSettingIds.Sequence`로 읽음). -- 단점: 축 수 확장/가독성 약함. "SequenceSetting 변경 시 재부팅 필요"(클래스 상단 주석) 제약 있음. - ---- - -## 5. 최종 추천 - -1. **권장:** 신규 `AxisPositionInterlockSetting`(WeldingCommon/Settings) + `CommonSettingIds.AxisPositionInterlock` + `CommonEquipmentSetup.RegisterSettings()` 등록, 그룹명 **"Axis Position Interlock"**, 축별 항목은 `TeachingPositionSetting.SettingPosition` 패턴 차용. -2. 함께 필요: PLC `eWordInput`에 전축 위치 워드 주소 매핑 추가. -3. 판정/인터락은 `SeqStatusMonitor`(또는 `SeqAlarmMonitor`)에서 Setting을 읽어 Min/Max 비교 후 `AlarmManager`/`WriteBit`로 처리. -4. 빠른 PoC가 목적이면 4절 간이 대안으로 `SequenceSetting`에 "Axis Position Interlock" 그룹 추가도 가능(확장성은 낮음). - -> 상태: 위치/구조 추천만 정리. 실제 코드 추가는 미적용. diff --git a/docs/분석/2026/06/17/144442_sequencesetting_axis_interlock_properties.md b/docs/분석/2026/06/17/144442_sequencesetting_axis_interlock_properties.md deleted file mode 100644 index 68db3fd..0000000 --- a/docs/분석/2026/06/17/144442_sequencesetting_axis_interlock_properties.md +++ /dev/null @@ -1,86 +0,0 @@ -# SequenceSetting "Axis Position Interlock" 그룹 추가 — 속성 설계 분석 - -**분석 일시:** 2026-06-17 14:44:42 -**대상 소스:** `WeldingComponents/WeldingCommon/Settings/SequenceSetting.cs` (`SequenceSetting` 클래스) -**관련 분석:** `docs/분석/2026/06/17/143900_axis_position_interlock_setting_location.md` (간이 대안 절) -**목표:** 전축 위치(섹터 각도) 모니터링 InterLock 기준값을 `SequenceSetting`에 그룹으로 추가. - ---- - -## 0. 확정 조건 (사용자) - -- **위치:** `SequenceSetting`에 `[Category("Axis Position Interlock")]` 그룹만 추가 (신규 클래스 신설 안 함). -- **속성:** 수직값 / 수평값을 **단일 기준값 + 허용오차** 형태로 등록 (Min/Max 아님). -- **단위:** 섹터값이라 **도(°, degree)**. -- **Enable 토글:** 추가 안 함 (`UseAxisPositionInterlock` 없음). -- **상태:** 코드 수정은 미적용. 분석/설계만 기록. - ---- - -## 1. 추가 위치 (소스) - -- 파일: `WeldingComponents/WeldingCommon/Settings/SequenceSetting.cs` -- 클래스: `public class SequenceSetting : ISettingItem` -- 추가 지점: 클래스 마지막 속성 `GroupRamingUse`(파일 기준 약 L69) **아래**에 신규 그룹 추가. -- 등록은 이미 되어 있음 — `CommonEquipmentSetup.RegisterSettings()`에서 - `register(CommonSettingIds.Sequence, new SequenceSetting());` (추가 등록 불필요). -- 소비(읽기): 모니터 시퀀스에서 `context.Settings[CommonSettingIds.Sequence] as SequenceSetting` - (예: `Machines/ILSOS_PrismaticRD_PTOP/Sequences/Monitoring/SeqStatusMonitor.cs` `StartNode`). - ---- - -## 2. 제안 속성 (스케치 — 미적용) - -```csharp -[Category("Axis Position Interlock")] -[DisplayName("수직 기준값 [deg]")] -public double VerticalSector { get; set; } = 0.0; - -[DisplayName("수직 허용오차 [deg]")] -public double VerticalTolerance { get; set; } = 0.0; - -[DisplayName("수평 기준값 [deg]")] -public double HorizontalSector { get; set; } = 0.0; - -[DisplayName("수평 허용오차 [deg]")] -public double HorizontalTolerance { get; set; } = 0.0; -``` - -> 속성명(`VerticalSector` / `HorizontalSector` 등)은 제안값. 명명 선호에 따라 조정 가능. - ---- - -## 3. 판정 로직 (참고) - -기준값 ± 허용오차 범위로 InterLock 판정: -``` -하한 = VerticalSector - VerticalTolerance -상한 = VerticalSector + VerticalTolerance -if (현재수직각도 < 하한 || 현재수직각도 > 상한) → InterLock -// 수평도 동일하게 HorizontalSector ± HorizontalTolerance -``` -- 위치: `SeqStatusMonitor`(또는 `SeqAlarmMonitor`)의 루프 노드에서 PLC 각도값과 비교. -- 위반 시 `AlarmManager`로 알람 발생 + 필요 시 `_plc.WriteBit(...)` 인터록 신호 - (기존 `PC_HEAVY_ALARM_STATUS` 출력 패턴 참조). - ---- - -## 4. 주의점 - -1. **재부팅 필요** — `SequenceSetting.cs` 상단 주석 *"SequenceSetting 이 변경되면, 재부팅 필요함."* - → 이 그룹 값을 변경하면 재부팅이 전제됨. -2. **PLC 각도 워드 미매핑** — `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs`의 - `eWordInput`에 전축(수직/수평) 섹터 각도 워드 주소가 아직 없음(대부분 `EMPTY_xx`). - → 실제 모니터링하려면 워드 주소 매핑 추가가 선행돼야 함. 읽기는 `PLCController.Logic.cs`의 - `ReadWord(eWordInput)` 사용. - ---- - -## 5. 적용 시 체크리스트 (요약) - -- [ ] `SequenceSetting.cs` 에 `Axis Position Interlock` 그룹 4속성 추가 (수직/수평 × 기준값/허용오차, 단위 deg) -- [ ] PLC `eWordInput` 에 수직/수평 섹터 각도 워드 주소 매핑 -- [ ] `SeqStatusMonitor`(또는 `SeqAlarmMonitor`)에 기준값±허용오차 판정 + 알람/인터록 처리 -- [ ] 설정 변경 후 재부팅 안내(설계상 제약) - -> 상태: 설계/위치 분석만. 실제 코드 추가는 미적용. diff --git a/docs/분석/2026/06/17/150031_laser_ready_di12_di24_change.md b/docs/분석/2026/06/17/150031_laser_ready_di12_di24_change.md deleted file mode 100644 index 9a60f67..0000000 --- a/docs/분석/2026/06/17/150031_laser_ready_di12_di24_change.md +++ /dev/null @@ -1,122 +0,0 @@ -# Laser Ready 판정 변경 — DI(9) → DI(12) & DI(24) 동시 조건 - -**분석 일시:** 2026-06-17 15:00:31 (15:10 정확한 매핑으로 갱신) -**대상:** `WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBoxViewModel.cs` -**관련:** `docs/분석/2026/06/17/143131_laser_ready_di9_analysis.md` (이전 DI9 분석) - ---- - -## 0. 요청/설명 (사용자) - -- 토글 버튼(`PerformCmdLaserReadyCheck`)을 누르면 **Control (9, 13)** 이 켜지고 꺼짐 (현행 `WriteDoBit(9)`, `WriteDoBit(13)` 유지). -- `OnMonitoring`에서는 현재 **DI(9)만** 보고 있는데, 9번이 아니라 **12번과 24번 둘 다(AND)** 보는 조건으로 변경. -- 확정 사항: **(1) AND 조건 맞음. (2) DI 24는 현장 배선 기준이 맞음(아래 enum 불일치 참고).** - ---- - -## 1. 매핑 정정 — 진짜 enum은 PAIXIO가 아니라 PAIX_PrismaticScanner - -처음엔 범용 `AuroraIO/Vendor/PAIX/PAIXIO.cs`의 `ePDIAddress`(DI12=StartSwitch, DI24=FanAlarm8)로 봤으나, -이 ViewModel은 상단에 `using static WeldingCommon.Devices.Laser.PAIX_PrismaticScanner;` 가 있고 -**레이저 전용 IO 매핑**(`WeldingComponents/WeldingCommon/Devices/Laser/LaserIOControl.cs` 내 `PAIX_PrismaticScanner`)을 사용함. -머신측 `Machines/ILSOS_PrismaticRD_PTOP/Device/IO/PAIX_PrismaticScanner.cs` 도 동일 enum. - -### 1.1 입력 `ePrismaticDIAddress` -| DI | 의미 | -|---|---| -| 9 | CoreLaser_Laser_Ready | -| 10 | CoreLaser_Error_State | -| 11 | CoreLaser_Laser_Output_State | -| **12** | **CoreLaser_Control_Status** | -| 13 | RingLaser_Laser_Ready | -| 14 | RingLaser_Error_State | -| 15 | RingLaser_Laser_Output_State | -| 23(enum) / **24(현장)** | **RingLaser_Control_Status** | - -### 1.2 출력 `ePrismaticDOAddress` -| DO | 의미 | -|---|---| -| 9 | CoreLaser_Control | -| 10 | CoreLaser_ErrorClearing | -| 13 | RingLaser_Control | -| 14 | RingLaser_ErrorClearing | -| 17 | EX_Red_Control | - -### 1.3 의미 해석 -- 토글 버튼: **DO9 = CoreLaser_Control**, **DO13 = RingLaser_Control** → Core·Ring 레이저 Control 출력 ON/OFF. -- 변경 후 모니터링: 그 피드백인 **Control_Status** 입력 2개를 봄 - - **DI12 = CoreLaser_Control_Status** - - **DI24 = RingLaser_Control_Status** (현장 배선 기준) -- 즉 "Core·Ring 두 레이저의 Control Status가 **모두 ON**" 일 때 `IsLaserReady = true`. - -> 참고: 기존 DI9는 **CoreLaser_Laser_Ready** 단독(Core만, Ring 미반영)이었음. -> 또한 `LaserIOControl.GetLaserReady()`는 `CoreLaser_Laser_Ready(9) && RingLaser_Laser_Ready(13)`로 *Laser_Ready* 비트를 봄(이번 변경은 *Control_Status* 비트 기준이라 다름). - ---- - -## 2. enum 불일치 (중요) - -`PAIX_PrismaticScanner.ePrismaticDIAddress`에 **`RingLaser_Control_Status = 23`** 으로 되어 있으나, -**현장 실제 DI는 24**. → enum 값이 outdated. - -- 영향 파일(둘 다 동일 enum): - - `WeldingComponents/WeldingCommon/Devices/Laser/LaserIOControl.cs` (L35) - - `Machines/ILSOS_PrismaticRD_PTOP/Device/IO/PAIX_PrismaticScanner.cs` (L28) - - (참고: `Machines/ILSOS_PrismaticScanner/Device/IO/PAIX_PrismaticScanner.cs` 도 동일) -- 권장: enum을 `RingLaser_Control_Status = 24` 로 정정. (정정 시 enum 상수로 깔끔하게 참조 가능) - ---- - -## 3. 수정 방안 (적용 예정) - -### 3.1 `OnMonitoring()` — DI 9 → (DI 12 && DI 24) -```csharp -try -{ - // Laser Ready: Core·Ring Control Status 둘 다 ON 일 때만 Ready - // DI 12 = CoreLaser_Control_Status, DI 24 = RingLaser_Control_Status(현장) - if (_IODevice.ReadDI(12) && _IODevice.ReadDI(24)) - { - IsLaserReady = true; - } - else - { - IsLaserReady = false; - } -} -catch (Exception ex) -{ - Log.E($"[OnMonitoring] DI 읽기 실패: {ex.Message}"); - IsLaserReady = false; -} -``` - -### 3.2 `PerformCmdLaserReadyCheck()` — 변경 없음 -- DO 9(CoreLaser_Control)·13(RingLaser_Control) 토글 유지. - -### 3.3 변경 요약 -| 위치 | 현재 | 변경 후 | -|---|---|---| -| `OnMonitoring()` | `ReadDI(9)` (CoreLaser_Laser_Ready) | `ReadDI(12) && ReadDI(24)` (Core·Ring Control_Status) | -| `PerformCmdLaserReadyCheck()` | `WriteDoBit(9/13)` | 변경 없음 | - ---- - -## 4. 구현 옵션 (가독성) - -- **옵션 A (최소 변경):** 리터럴 `ReadDI(12) && ReadDI(24)` + 주석으로 의미 표기. `_IODevice`가 base `IODevice` 타입이라 그대로 사용 가능. (즉시 적용 용이) -- **옵션 B (정공법):** enum `RingLaser_Control_Status = 24` 정정 후, `_IODevice`를 `PAIX_PrismaticScanner`로 캐스팅하여 - `ReadDiBit(ePrismaticDIAddress.CoreLaser_Control_Status) && ReadDiBit(ePrismaticDIAddress.RingLaser_Control_Status)` 로 상수 참조. - - 장점: 매직넘버 제거, 의미 명확. 단점: 캐스팅/타입 의존 추가. -- 권장: 우선 옵션 A로 적용하되, **enum 23→24 정정은 별도로 반드시 반영**(다른 코드가 23으로 참조 시 버그 가능성). - ---- - -## 5. 결론 / 적용 체크리스트 - -- [ ] `OnMonitoring()` 판정: `ReadDI(9)` → `ReadDI(12) && ReadDI(24)` (AND 확정) -- [ ] 버튼은 DO9/13 그대로 유지 -- [ ] `PAIX_PrismaticScanner.ePrismaticDIAddress.RingLaser_Control_Status` **23 → 24** 정정(LaserIOControl.cs / 머신 PAIX 파일) -- [ ] 의미: Core Control Status(12) AND Ring Control Status(24) 모두 ON → Laser Ready - -> 상태: 분석/수정 방안만 기록. 실제 코드 수정은 미적용. diff --git a/docs/분석/2026/06/17/162010_io_interface_map_path_source.md b/docs/분석/2026/06/17/162010_io_interface_map_path_source.md deleted file mode 100644 index d5ee814..0000000 --- a/docs/분석/2026/06/17/162010_io_interface_map_path_source.md +++ /dev/null @@ -1,103 +0,0 @@ -# IO Interface Map(DI/DO 데이터 맵) 경로 — 소스상 설정 위치 분석 - -**분석 일시:** 2026-06-17 16:20:10 -**대상:** IODevice가 읽는 DI/DO 데이터 맵(CSV) 경로가 소스 어디서 설정되는가 -**관련:** `docs/분석/2026/06/17/150031_laser_ready_di12_di24_change.md` (DI12/24 판정 변경) - ---- - -## 0. 질문 (사용자) - -- `OnMonitoring`에서 읽는 DI 12 / 24 같은 IO의 **데이터 맵(CSV)** 의 실제 로드 경로가 소스상 어디서 설정되는지. - ---- - -## 1. 결론 - -- **실제 경로 문자열 값은 소스에 하드코딩돼 있지 않음.** -- 소스에서 확인되는 것은 (1) 경로를 **읽는 메커니즘**, (2) 경로 **속성 정의**, (3) 디바이스 **등록 시 기본값(빈 값)** 까지. -- 실제 경로는 **저장된 디바이스 Config(설정)** 에 들어 있으며, **Setting → Device(IODevice) Config 화면의 `IOInterfaceMapDI` 필드**(또는 디스크의 해당 config 파일)에서 지정됨. -- 영속화/역직렬화는 Aurora 프레임워크 DLL이 처리 → 이 레포 소스에는 경로 문자열 자체가 없음. - ---- - -## 2. 소스 흐름 (확인된 부분) - -### ① 디바이스 등록 — 기본 config 주입 (경로 미지정) -`Machines/ILSOS_PrismaticRD_PTOP/EquipmentSetup.cs:153` -```csharp -registry.Register(CommonDeviceIds.IODevice, - () => new PAIX_PrismaticScanner(AuroraEnv, CommonDeviceIds.IODevice, isSimul), - new IOUnitConfig()); // 기본 IOUnitConfig (경로 빈 값) -``` - -### ② config의 경로 속성 — 기본값 빈 문자열 -`AuroraDevices/AuroraIO/IOUnitConfig.cs` -```csharp -internal class IOUnitConnConfig : IConnConfig -{ - [InputFilePath] - public string IOInterfaceMapDI { get; set; } = ""; // DI 맵 경로 - [InputFilePath] - public string IOInterfaceMapDO { get; set; } = ""; // DO 맵 경로 - ... -} -``` -- `[InputFilePath]` 어트리뷰트 → Setting 화면에서 파일 경로 지정 필드로 노출. - -### ③ 활성화 시 그 경로로 CSV 로드 -`AuroraDevices/AuroraIO/IODevice.cs` -```csharp -protected override int DoActivate(IDeviceConfig config) { - _connConfig = config.ConnConfig; // L148 - ... - LoadIOTable(); // → MappingDIO() -} -// MappingDIO(): -var m_connConfig = _connConfig as IOUnitConnConfig; -fileName = m_connConfig.IOInterfaceMapDI; // 실제 로드 경로 -StreamReader sr = new StreamReader(fileName, ...); -``` -- 읽기 진입: `ReadDI(int)` → `_iOUnit.ReadDiBit(index)` (`IODevice.cs:212`). - ---- - -## 3. 데이터 맵(CSV) 실제 위치 (현장/레포) - -- 레포 복사본: `Doc/ILSOSConfig/ILSOS_PrismaticScanner/_device/_io/` -- 현장 런타임: `D:\ILSOSConfig\ILSOS_PrismaticScanner\_device\_io\` -- 기본 파일: `DI.csv`, `DO.csv` + 라인/공정별 변형 다수 - (`DI_LINE_CC-CAP_ASSY_WELDING.csv`, `DI_PRIBAT_L2.csv`, `DI_SMW.csv`, `DI_SEALPIN.csv`, `DI_PRE_WELDING.csv`, `DI_PILOT_LINE_ANODE_WELDING.csv`, `DI_PILOT LINE CATHODE WELDING.csv` 등) -- 어떤 파일이 로드되는지는 ②의 `IOInterfaceMapDI` 값(=저장된 Config)이 결정. - -### 주의 — 기본 `DI.csv`의 12/24/25는 레이저 Control Status가 아님 -``` -12,12,START SWITCH,START SWITCH -24,24,FAN 8 ALARM,FAN 8 ALARM -25,25,LASER Ready,LASER Ready -``` -- 기본 `DI.csv` 기준 12=START SWITCH, 24=FAN 8 ALARM, 25=LASER Ready. -- 코드(`PAIX_PrismaticScanner.ePrismaticDIAddress`)는 12=CoreLaser_Control_Status, 23=RingLaser_Control_Status로 다르게 보고 있음 → **enum과 실제 로드되는 CSV 맵이 불일치할 수 있음.** - ---- - -## 4. 정리표 - -| 알고 싶은 것 | 소스 확인 | 위치 | -|---|---|---| -| 경로를 읽는 코드 | O | `IODevice.cs` `MappingDIO()` / `ReadDI()` | -| 경로 속성 정의 | O | `IOUnitConfig.cs` `IOInterfaceMapDI` | -| 디바이스 등록(기본값) | O | `EquipmentSetup.cs:153` | -| 실제 경로 문자열 값 | X (소스에 없음) | 저장된 디바이스 Config / Setting UI | - ---- - -## 5. [TODO / 추후 확인할 것] - -- [ ] **현장 PC에서 실제 로드되는 DI/DO 맵 파일 확정**: Setting → Device → IODevice Config 의 `IOInterfaceMapDI` / `IOInterfaceMapDO` 값 확인 (또는 `D:\ILSOSConfig\ILSOS_PrismaticScanner\_device\_io\` 에서 실제 사용 파일). -- [ ] **해당 CSV의 DI 12 / 24 줄 의미 확인**: 레이저 Core/Ring Control Status가 맞는지, 아니면 START SWITCH / FAN 8 ALARM 인지. - - 만약 기본 `DI.csv`(12=START SWITCH, 24=FAN 8 ALARM)가 로드 중이면, `ReadDI(12) && ReadDI(24)` 판정이 의도와 다르게 동작 → CSV 맵 또는 인덱스 재확인 필요. -- [ ] **enum vs CSV 정합성**: `PAIX_PrismaticScanner.ePrismaticDIAddress`(12=CoreLaser_Control_Status, 23=RingLaser_Control_Status)와 현장 CSV 맵이 일치하는지 대조. 불일치 시 어느 쪽을 기준으로 맞출지 결정. -- [ ] (참고) `RingLaser_Control_Status` enum 값은 현재 23으로 두기로 함(현장 표기 23, 조건은 24 리터럴 사용). CSV 확인 후 최종 정합성 점검. - -> 상태: 소스 추적/위치 분석만. 실제 현장 Config 값 확인은 미완(위 TODO). diff --git a/docs/분석/2026/06/17/165341_plc_devicemap_update_260617.md b/docs/분석/2026/06/17/165341_plc_devicemap_update_260617.md deleted file mode 100644 index ab3d92f..0000000 --- a/docs/분석/2026/06/17/165341_plc_devicemap_update_260617.md +++ /dev/null @@ -1,67 +0,0 @@ -# PLC DeviceMap 갱신 (260617 Laser Interface 반영) - -**작업 일시:** 2026-06-17 16:53:41 -**근거 문서:** `docs/PtoP 유니셀 용접기 PLC 인터페이스_260617/유니셀 PtoP 단동기 Laser Interface_260617.xlsx` -**대상 경로:** `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\External\PLC` - ---- - -## 0. 작업 요약 - -- 기존 `PLC_DeviceMap_*` 각 bit/word 파일을 `260617_backup` 으로 백업. -- 위 Excel(시트 M70K=Bit, D70K=Word)을 분석해 4개 맵 파일의 **내부 매핑(Comment)만** 갱신. 파일명/주소행 범위 유지. -- 네이밍: 기존 코드 스타일(`PC_*` / `PLC_*`) 유지. 양극(+)은 `_P` 접미사. 음/양극 모두 Excel대로 채움. - -## 1. Excel 구조 → 파일 매핑 - -| Excel 블록 | 방향 | 대상 파일 | 네이밍 | -|---|---|---|---| -| M70K 좌(70000~) | Laser→PLC (PC 출력) | Bit_Output_M | `PC_*` | -| M70K 우(70500~) | PLC→Laser (PC 입력) | Bit_Input_M | `PLC_*` | -| D70K 좌(70000~) | Laser1→PLC (PC 출력) | Word_Output_D | `PC_*` | -| D70K 우(70500~) | PLC→Laser1 (PC 입력) | Word_Input_D | `PLC_*` | - -- Excel 메모: "음/양극 레이저 하나로 사용 중이라 음극만 사용 하기로" (70101 셀). → 단, 요청에 따라 양극(+)도 전부 매핑함. - -## 2. 생성 결과 (매핑된 주소) - -### ① Bit_Output_M (M70000~70143) -- (-) 70000 LIVE, 70001 AUTO_STATUS, 70002 AUTO_COMPLETE, 70003 MANUAL_COMPLETE, 70004 LIGHT_AL, 70005 HEAVY_AL, 70008 PAGE_CHANGE_COMPLETE, 70009 RECIPE_CHANGE_COMPLETE, 70016~70018 WELD READY_ACK/BUSY/COMPLETE -- (+) 70100~70105, 70108, 70116~70118 WELD_01, 70119~70121 WELD_02, 70124 WELD_MONI_COMPLETE, 70125~70128 WELD_CH2(+MoniComplete), 70133~70135 M_POWER, 70137~70139 PARA(READ_ACK/SET_ACK/CHANGE_REQ) - -### ② Bit_Input_M (M70500~70643) -- (-) 70500~70509(LIVE,AUTO,AUTO_REQ,MANUAL_REQ,LIGHT_AL,HEAVY_AL,DOOR,EMO,PAGE_CHANGE_REQ,RECIPE_CHANGE_REQ), 70516~70518 WELD REQ/WAIT/COMP_ACK -- (+) 70600~70608, 70610 DISPLACE_USE, 70616~70618 WELD_01, 70619~70621 WELD_02, 70624 WELD_MONI_COMP_ACK, 70625~70628 WELD_CH2(+MoniCompAck), 70630 LASER2_ERROR_RESET, 70632~70635 LASER2_M_POWER, 70637~70639 LASER2_PC_PARA - -### ③ Word_Output_D (D70000~70099) -- 70000 PC_ErrorCode, 70001 PC_PageNo, 70002 PC_RecipeNo - -### ④ Word_Input_D (D70500~70599) -- 70500 PLCErrorCode, 70501 PLC_PageNo, 70502 PLC_RecipeNo, 70510 PLC_Head_Theta_Position - -## 3. 기존 대비 주요 변경 (delta) - -- **70502 (Word_Input): PLC_StepNo → PLC_RecipeNo** (의미 변경) -- **70509 (Bit_Input): PLC_RMS_USED → PLC_RECIPE_CHANGE_REQ** (의미 변경) -- 신규: Recipe_CHG(70009/70509), Weld_02·Weld_CH2·M_Power·PC_Para(양극), DisplaceUse(70610), Head_Theta_Position(70510), PC_RecipeNo(70002/70502) -- 신규 스펙에서 빠진 기존 항목: PC_WELD_NG_JUDGE(70020), PC_RMS_PARAMETER_READ_REQUEST_ACK(70037), PLC_ALARM_RESET(70530), PLC_RMS_PARAMETER_READ_REQUEST(70537) -- 기존 파일 버그 수정: Word_Input 70585~70599 가 `M`으로 오기 → `D`로 정정. - -## 4. [TODO / 추후 확인할 것] — 코드 정합성 ⚠️ - -PLC 맵(주소→의미)이 바뀌었으므로, 주소를 enum으로 읽는 코드와 **불일치** 가능성. 반드시 대조 필요: - -- [ ] **`Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs`** - - `eWordInput.PLC_STEP_NO = 2` (D70502) → 새 맵은 **RecipeNo**. StepNo를 코드가 D70502로 읽으면 의미 깨짐. (StepNo 주소 재배정 or 코드 수정 필요) - - `eBitInput.PLC_RMS_USED = 0x09` (M70509) → 새 맵은 **RECIPE_CHANGE_REQ**. `SeqStatusMonitor.UpdateIO`가 `PLC_RMS_USED` 읽음 → 충돌. - - `eBitInput.PLC_ALARM_RESET = 0x1E`(M70530), `PLC_RMS_PARAMETER_READ_REQUEST = 0x25`(M70537) → 새 맵엔 없음. 사용처(`SeqStatusMonitor`, RMS) 확인. - - `eBitOutput.PC_WELD_NG_JUDGE = 0x14`(M70020), `PC_RMS_PARAMETER_READ_REQUEST_ACK = 0x25`(M70037) → 새 맵엔 없음. -- [ ] 신규 양극(+)/Laser2 신호(Weld_02, Weld_CH2, M_Power, PC_Para 등) 사용 여부 및 enum 추가 검토. -- [ ] **70628 Excel 라벨 중복**: Excel J열에서 70627·70628 모두 "Weld_CH2Comp_ACK"로 표기됨. 70628은 출력측(70128 Weld_CH2MoniComp) 대칭으로 보아 `PLC_WELD_CH2_MONI_COMP_ACK_P`로 매핑함 → **현장/PLC 측에 실제 의미 확인 필요.** -- [ ] "음극만 사용" 메모 vs 양극 전부 매핑 — 실제 운영에서 양극 사용 여부 확정. - -## 5. 백업 - -- `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\External\PLC\260617_backup\` 에 기존 `PLC_DeviceMap_*` (복사본 포함) 6개 보관. - -> 상태: 4개 CSV 갱신 완료(라이브 config). 코드 정합성(4절)은 미확인 — 추후 작업. diff --git a/docs/분석/2026/06/17/171312_plc_map_io_direction_and_enum_alignment.md b/docs/분석/2026/06/17/171312_plc_map_io_direction_and_enum_alignment.md deleted file mode 100644 index 66d04cd..0000000 --- a/docs/분석/2026/06/17/171312_plc_map_io_direction_and_enum_alignment.md +++ /dev/null @@ -1,89 +0,0 @@ -# PLC 맵 ↔ 소스 enum 정합성 분석 (Input/Output 검증 + StepNo + 배수) - -**분석 일시:** 2026-06-17 17:13:12 -**연계:** `docs/분석/2026/06/17/165341_plc_devicemap_update_260617.md` (맵 갱신 작업) -**대상 소스:** -- `WeldingComponents/WeldingCommon/Devices/PLC/PLCController.Address.cs` (base: `eCommonBit/WordInput/Output`) -- `WeldingComponents/WeldingCommon/Devices/PLC/PLCController.Config.cs` (base 로직) -- `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCController.Address.cs` (machine: `eBit/WordInput/Output`) -- `Machines/ILSOS_PrismaticRD_PTOP/Device/PLC/PLCContoller.Config.cs` (생성자만) - -> 상태: **분석/판단만**. 소스·맵 추가 수정은 미적용. - ---- - -## 1. Input/Output 뒤바뀜 여부 → **안 바뀜 (정상)** - -판단 기준 = "기존 Input/Output 파일이 담던 방향" 과 "Excel 블록 방향" 대조: - -| 구분 | 기존 CSV | Excel 블록 | 방향 | 결론 | -|---|---|---|---|---| -| **Output** | `Bit_Output_M` = M70000~ , `PC_*` | 좌측 70000 = **"Laser→PLC"** | PC가 PLC로 **씀(OUT)** | 70000 = Output ✓ | -| **Input** | `Bit_Input_M` = M70500~ , `PLC_*` | 우측 70500 = **"PLC→Laser"** | PC가 PLC에서 **읽음(IN)** | 70500 = Input ✓ | - -- "Laser" = 우리(레이저측) PC. Laser→PLC = 우리가 PLC로 출력 = **Output**. PLC→Laser = 우리가 읽는 입력 = **Input**. -- 이번에 생성한 4파일도 동일 규칙(Output=70000블록 PC_*, Input=70500블록 PLC_*)으로 작성됨 → **방향 정상, 파일명 안 바뀜.** -- Word도 동일: `Word_Output_D`=D70000(PC_*), `Word_Input_D`=D70500(PLC_*). ✓ - ---- - -## 2. enum index = (주소 − base) 오프셋 (★ 배수/읽기 핵심) - -- CSV 파서(`ParsePLCBitWordAddressFromCSV`)는 행 순서대로 index 0,1,2… 부여. 행이 연속(M70500,70501,…)이므로 - **enum 값(offset) = (M주소 − base)**. - - 예: `PLC_WELD_READY_REQ = 0x10`(16) ↔ M70516, `PLC_AUTO_STATUS_P = 0x65`(101) ↔ M70601. ✓ -- 즉 소스 enum은 "주소"가 아니라 "오프셋"을 들고 있음. 맵에서 주소 의미가 바뀌면 **같은 오프셋을 읽는 코드가 다른 신호를 읽게 됨.** - -### 배수(읽기 블록) 우려 — 확인 필요 -- Bit는 word(16bit) 단위로 읽힘 → 대량 read count는 16의 배수. -- 이번 신규 최대 오프셋: **Bit In 139**(M70639 Laser2 PC_Para CHG_ACK), **Bit Out 139**(M70139), **Word In 10**(D70510 Head_Theta), **Word Out 2**(D70002). -- offset 139는 word #8(128~143) 안 → Bit는 **최소 9 word(144bit)** 읽어야 갱신됨. Word In은 **최소 11 word** 읽어야 D70510 갱신. -- [확인필요] PLCBase/통신의 **read count(블록 길이)** 가 위 최대 오프셋을 포함하는지. 작으면 고오프셋(특히 Laser2 130~139, Head_Theta 10)이 **안 읽힘**. (사용자 지적 "배수라 못 읽을 수도") - ---- - -## 3. StepNo "왜?" — 코드가 D70502를 StepNo로 읽음 - -- 코드: `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPrismaticScanner.cs:422` - `_TotalWeldingStep = ReadWord(eWordInput.PLC_STEP_NO)` → **offset 2 = D70502** 를 "총 용접 step" 으로 사용. -- 기존 `Word_Input` CSV: D70502 = PLC_StepNo (코드와 일치). -- **신규 Excel: D70502 = PLC_RecipeNo** 로 재배정 → 같은 offset 2를 코드는 StepNo로 읽음 → `_TotalWeldingStep` 이 **RecipeNo 값을 읽게 됨(오동작)**. -- 판단: PLC 인터페이스가 StepNo를 빼고 offset 2에 RecipeNo를 넣은 것. **StepNo 신호 위치(또는 폐지 여부)와 RecipeNo offset 확정 필요.** (CSV 코멘트만으론 코드 동작 안 바뀜 — 실제 PLC가 그 주소에 무엇을 싣는지가 핵심) - ---- - -## 4. 신규 맵 ↔ 코드 enum 충돌 (사용처 확인됨) - -신규 Excel 맵이 **기존 사용 중인 신호를 빈칸/재배정** 한 것들: - -| 신호(offset/주소) | 코드 사용처 | 신규 맵 | 영향 | -|---|---|---|---| -| `PLC_RMS_USED` (0x09 / M70509) | `SeqStatusMonitor.cs:106` `Status.RMSUse` | 70509=RECIPE_CHANGE_REQ | RMSUse가 Recipe변경요청 비트를 읽음 ⚠️ | -| `PLC_ALARM_RESET` (0x1E / M70530) | `SeqStatusMonitor.cs:89` | 70530 빈칸 | 알람리셋 신호 안 들어옴 ⚠️ | -| `PLC_RMS_PARAMETER_READ_REQUEST` (0x25 / M70537) | `PLCController.Config.cs:114`, `RMSHandler.cs:69` | 70537 빈칸 | RMS 파라미터 read req 동작 안 함 ⚠️ | -| `PC_WELD_NG_JUDGE` (0x14 / M70020) | `SeqAutoRunPrismaticScanner.cs:429/896` write | 70020 빈칸 | NG 판정 PLC로 안 나감 ⚠️ | -| `PC_RMS_PARAMETER_READ_REQUEST_ACK` (0x25 / M70037) | `RMSHandler.cs:63/70/76` write | 70037 빈칸 | RMS ACK 안 나감 ⚠️ | - -- 신규 추가 신호(코드 enum 없음): Recipe_CHG(70009/70509), DisplaceUse(70610), Weld_02(70619~621), WeldMoni(70624), Weld_CH2(70625~628), Laser2 M_Power/PC_Para(70630~639), Head_Theta(D70510), RecipeNo(D70002/70502). - ---- - -## 5. 정합성 작업 시 방향 (제안 — 미적용) - -- enum 2계층 모두 정렬 필요: - 1. **base** `eCommonBit/WordInput/Output` (WeldingCommon) — Live/AutoReq/RMS 등 공통 로직. - 2. **machine** `eBit/WordInput/Output` (PrismaticRD_PTOP) — StepNo/NG_JUDGE/ALARM_RESET/RMS_USED 등. -- 기존 소스 최대한 유지 + **enum 값(offset)만** 신규 맵에 맞게 재매핑. 단: - - 빠진 신호(RMS, ALARM_RESET, NG_JUDGE)를 신규 맵에 **다시 넣을지 / 코드에서 제거할지** = PLC 인터페이스 측 확정 필요. - - StepNo offset 확정(폐지 or 이동). - - read count(배수)가 신규 최대 offset(139, Word 10) 포함하도록 확인. -- ⚠️ 동일 enum이 4개 머신(PrismaticRD_PTOP / PrismaticScanner / PrismaticCleanLaser / PrismaticGun)에 중복 존재 → PTOP만 바꿀지 공통 정책인지 결정. - ---- - -## 6. [TODO] - -- [ ] PLC 측에: StepNo 폐지/이동 여부, 빠진 RMS·ALARM_RESET·NG_JUDGE 주소 확정. -- [ ] read count(블록 길이/배수) 가 offset 139·Word10 포함하는지 검증. -- [ ] 확정 후 base + machine enum offset 재매핑 (기존 코드 유지, 값만). -- [ ] 70502(StepNo→RecipeNo), 70509(RMS_USED→RecipeCHG) 의 코드 영향 반영. diff --git a/docs/분석/2026/06/17/171642_plc_map_update_checklist.md b/docs/분석/2026/06/17/171642_plc_map_update_checklist.md deleted file mode 100644 index 1a1adf2..0000000 --- a/docs/분석/2026/06/17/171642_plc_map_update_checklist.md +++ /dev/null @@ -1,48 +0,0 @@ -# PLC DeviceMap 갱신 — 추후 확인 체크리스트 (260617) - -**작성:** 2026-06-17 17:16:42 -**상태:** 분석/맵파일 갱신만 완료. **소스(enum) 수정·코드 반영은 미적용 (보류).** - -## 관련 자료 (이미 남긴 분석) -- 맵 갱신 작업·델타: `docs/분석/2026/06/17/165341_plc_devicemap_update_260617.md` -- 방향/StepNo/enum/배수 분석: `docs/분석/2026/06/17/171312_plc_map_io_direction_and_enum_alignment.md` -- 근거 Excel: `docs/PtoP 유니셀 용접기 PLC 인터페이스_260617/유니셀 PtoP 단동기 Laser Interface_260617.xlsx` -- 백업: `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\External\PLC\260617_backup\` - ---- - -## 이미 완료한 것 (참고) -- [x] 기존 `PLC_DeviceMap_*` 6개 → `260617_backup` 백업 -- [x] Excel(M70K/D70K) 분석, 4개 CSV 내부 매핑 갱신(파일명·범위 유지, 코드 스타일 네이밍, 음/양극 전부) -- [x] Input/Output 방향 검증 → **안 바뀜(정상)** 확인 -- [x] Word_Input 70585~70599 `M`오기 → `D` 정정 - ---- - -## 추후 확인 체크리스트 (PLC 인터페이스 측 협의 필요) - -### A. 의미 재배정된 주소 (코드 영향 큼) -- [ ] **D70502: StepNo → RecipeNo** — `SeqAutoRunPrismaticScanner.cs:422` 가 `eWordInput.PLC_STEP_NO`(offset 2)로 `_TotalWeldingStep` 읽음. StepNo **폐지? 다른 offset 이동?** 확정. RecipeNo offset 확정. -- [ ] **M70509: PLC_RMS_USED → RECIPE_CHANGE_REQ** — `SeqStatusMonitor.cs:106` `Status.RMSUse` 가 offset 9 읽음. RMS_USED 위치 재확정. - -### B. 신규 맵에서 빠진(빈칸) 기존 사용 신호 — 코드 제거 vs 주소 재부여 결정 -- [ ] **PLC_ALARM_RESET** (M70530) — `SeqStatusMonitor.cs:89` 사용. 빠지면 알람리셋 동작 안 함. -- [ ] **PLC_RMS_PARAMETER_READ_REQUEST** (M70537) — `PLCController.Config.cs:114`, `RMSHandler.cs:69` 사용. -- [ ] **PC_WELD_NG_JUDGE** (M70020) — `SeqAutoRunPrismaticScanner.cs:429/896` write. 빠지면 NG 판정 PLC 미전달. -- [ ] **PC_RMS_PARAMETER_READ_REQUEST_ACK** (M70037) — `RMSHandler.cs:63/70/76` write. - -### C. 신규 추가 신호 — enum 추가/사용 여부 검토 -- [ ] Recipe_CHG(70009/70509), DisplaceUse(70610), Weld_02(70619~621), WeldMoni(70624), Weld_CH2(70625~628), Laser2 M_Power/PC_Para(70630~639), Head_Theta_Position(D70510), RecipeNo(D70002/70502) -- [ ] **70628 Excel 라벨 중복**("Weld_CH2Comp_ACK" 70627·70628 동일) — 출력 대칭상 70628=MoniComp_ACK로 매핑함. 실제 의미 확인. - -### D. 읽기 블록(배수) 검증 -- [ ] PLC **read count(블록 길이)** 가 신규 최대 offset 포함하는지 — Bit In/Out **offset 139**(M70639/M70139), **Word In offset 10**(D70510). Bit는 16비트(word) 배수로 읽힘 → 최소 9 word(144bit) / Word In 11 word 필요. 작으면 Laser2(130~139)·Head_Theta 미갱신. - -### E. enum 정렬 범위/정책 -- [ ] **enum 2계층** 모두 정렬: base `eCommonBit/WordInput/Output`(WeldingCommon) + machine `eBit/WordInput/Output`(PrismaticRD_PTOP). 기존 소스 유지, **offset 값만** 재매핑. -- [ ] 동일 enum이 **4개 머신**(PrismaticRD_PTOP / PrismaticScanner / PrismaticCleanLaser / PrismaticGun)에 중복 — PTOP만 변경할지, 공통 적용할지 정책 결정. - ---- - -## 결론 -- 맵 CSV 갱신은 반영됨. **소스 enum/코드는 위 A~E 확정 후** 수정 예정. 지금은 보류. diff --git a/docs/분석/2026/06/17/184011_eroom_light_rs232_analysis.md b/docs/분석/2026/06/17/184011_eroom_light_rs232_analysis.md deleted file mode 100644 index ebde20e..0000000 --- a/docs/분석/2026/06/17/184011_eroom_light_rs232_analysis.md +++ /dev/null @@ -1,264 +0,0 @@ -# 현장 조명(EROOM) PC 제어 — 소스 구조 분석 및 수정 가이드 - -**분석 일시:** 2026-06-17 18:40:11 -**요청자:** 안동준 과장님 요청사항 — *"조명을 PC에서 조정할 수 있도록"* -**대상 조명:** 현장 EROOM 조명 (제조사: **이룸인더스트리**, eroomi.com) -**연결 상태:** PC ↔ 조명 컨트롤러 **RS232 9핀** 직결 -**목적:** 현재 조명을 무엇이 제어하는지 파악하고, 나중에 PC 제어로 수정하기 위한 기준 문서 - ---- - -## 0. 요청 내용 (사용자) - -- 현장 조명에 **MANUAL / REMOTE** 두 모드가 있고, **REMOTE**로 두면 PC와 통신 가능할 텐데 현재는 **소스 내에서 연결이 안 된 상태**. -- 조명을 조정하는 게 무엇인지 분석해서 docs에 정리(나중에 수정 예정). -- 조명은 **EROOM 조명**이며 PC에 **RS232 9핀 포트로 연결**되어 있음. → *장치관리자에서 포트 확인해야 하나?* -- Device에 **`Light`** 라는 디바이스로 등록되어 있을 것. - ---- - -## 1. 결론 요약 - -1. 소스에는 이미 **`Light` 디바이스**가 등록되어 있다. - - 위치: `WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs:226` - - 등록: `CommonDeviceIds.Light` → `new LightDevice(..., eLight.ERI, isSimul)` - - 즉 **드라이버 타입이 `eLight.ERI`(ERI 4채널)** 로 고정되어 있다. -2. 조명 통신은 **RS232 시리얼** 기반이다. (`LightConfig` 의 `SerialConnConfig`) - - 기본값: **PortNum=1, BaudRate=19200, 8-N-1, ReadTimeout=500** -3. **"연결 안 됨"의 유력한 원인 3가지**: - - (a) **드라이버 불일치**: 현재 `eLight.ERI` 드라이버는 ERI사 ASCII 프로토콜(`setbrightness ch val\r` 등)을 쓴다. EROOM 컨트롤러의 프로토콜과 **명령어 포맷이 다르면** 같은 포트에 연결돼도 동작하지 않는다. - - (b) **시리얼 설정 불일치**: EROOM(EUV 계열) 컨트롤러는 **38,400bps** 사양이 확인된다. 현재 소스 기본값은 **19200**. → BaudRate/포트번호 불일치 가능. - - (c) **컨트롤러 모드**: 컨트롤러 전면 스위치가 **MANUAL**이면 RS232 명령을 무시한다. → 반드시 **REMOTE**로 둬야 PC 명령이 먹는다. -4. 따라서 PC 제어를 살리려면 **① 컨트롤러 REMOTE 전환 → ② COM 포트/Baud 맞추기 → ③ (필요 시) EROOM 전용 드라이버 추가** 순서로 접근한다. - ---- - -## 2. 장치관리자 COM 포트 확인 (사용자 질문 답변) - -**네, 먼저 장치관리자에서 포트를 확인하는 게 맞습니다.** - -- RS232 9핀이 메인보드 시리얼이든 USB-to-Serial 젠더든, Windows에서는 **`포트(COM & LPT)`** 항목에 **`COMx`** 로 잡힙니다. -- 확인 순서: - 1. `장치관리자` → `포트(COM & LPT)` 펼치기 → 조명 케이블 연결/분리 시 나타났다 사라지는 항목이 해당 포트. - 2. 그 항목 더블클릭 → `포트 설정` 탭에서 **비트/초(Baud), 데이터 비트, 패리티, 정지 비트**가 컨트롤러 사양과 일치하는지 확인. - 3. 잡힌 **COM 번호**를 소스의 `LightConfig.ConnConfig.PortNum` 과 맞춰야 함. -- USB-Serial인 경우 칩셋 드라이버(예: FTDI, Prolific, CH340)가 설치돼 있어야 COM 포트가 잡힙니다. 안 잡히면 드라이버부터 설치. -- 포트가 다른 프로그램(터미널 등)에서 점유 중이면 프로그램에서 열 수 없으니, 점검용 터미널은 닫고 테스트. - -> 점검 팁: 장치관리자에서 COM 번호/Baud를 확인한 뒤, 시리얼 터미널(예: 테라텀)로 컨트롤러를 **REMOTE 모드**에 두고 EROOM 매뉴얼 명령을 직접 쳐서 조명이 반응하는지부터 확인하면, 배선/포트 문제인지 소스 문제인지 분리할 수 있다. - ---- - -## 3. 소스 구조 — 조명을 제어하는 경로 - -### 3.1 디바이스 등록 (시작점) - -`WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs:226` -```csharp -registry.Register(CommonDeviceIds.Light, - () => new LightDevice(CommonDeviceIds.Light, eLight.ERI, isSimul), // ← 드라이버 타입 ERI 고정 - new LightConfig(), - device => new UCLight() { DataContext = new UCLightViewModel(AuroraEnv, CommonDeviceIds.Light, _SequenceSetting.LightValue) }); -``` - -### 3.2 디바이스 본체 / 드라이버 선택 - -`AuroraDevices/AuroraLight/LightDevice.cs` -- `eLight` enum: **`LvsPn`, `ALT_4CH`, `ALT_8CH`, `ERI`** — **EROOM 없음.** -- `DoActivate()` 에서 타입에 따라 실제 드라이버 인스턴스 생성: - - `eLight.ERI` → `new ERILight(DeviceId, IsSimul, 4)` (4채널) -- 연결: `_light.InitInternal(config.ConnConfig as SerialConnConfig)` - -### 3.3 시리얼 설정 - -`AuroraDevices/AuroraLight/LightConfig.cs` -```csharp -ConnConfig = new SerialConnConfig() { - PortNum = 1, // ← 장치관리자 COM 번호와 맞춰야 함 - BaudRate = 19200, // ← EROOM(EUV 계열)은 38400 사양 확인됨, 불일치 가능 - Parity = Parity.None, StopBits = StopBits.One, DataBits = 8, - ReadTimeout = 500 -}; -``` - -### 3.4 ERI 드라이버 프로토콜 (현재 연결되는 명령어) - -`AuroraDevices/AuroraLight/ERI/ERILight.cs` — **ASCII 텍스트 + `\r` 종결** -| 동작 | 명령 포맷 | -|---|---| -| 밝기 설정 | `setbrightness {channel} {value}\r` (value 0~255) | -| On/Off | `seton {channel} {0|1}\r` | -| On 상태 조회 | `geton {channel}\r` | - -> EROOM 컨트롤러 매뉴얼의 명령어가 위와 다르면, ERI 드라이버로는 통신이 안 된다(=현재 "연결 안 됨" 상태의 핵심 원인 후보). - -### 3.5 시퀀스에서의 사용 (자동운전) - -`Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPrismaticScanner.cs` -- 변수: `private LightDevice _Light;` (L138) -- 획득: `_Light = context.GetLight();` (L1132) — `SequenceContextEx.GetLight()` → `context.Devices[CommonDeviceIds.Light]` -- 점등: 자동운전 시 `_Light.SetLightValue(lightValues)` 로 4채널 ON (L356~359, L535~537) -- 기본 밝기값: `lightValues = {255,255,255,255}` (L193), 레시피값은 `SequenceSetting.LightValue` / `CompensationLightValue` - ---- - -## 4. EROOM 조명 컨트롤러 (웹 조사 결과) - -- **제조사:** 이룸인더스트리 (eroomi.com) — 머신비전 조명/컨트롤러 전문. (사이트 TLS 인증서 문제로 직접 페이지 파싱은 실패, 검색 요약 기준) -- **대표 컨트롤러 사양(EUV-FX7 등 기준):** - - 통신: **RS232, 38,400 bps**, MANUAL/REMOTE 지원 - - 출력: **0~100%** 파워 제어 - - 채널: **최대 4채널** 점등 start/stop 입력 - - 조사 시간(irradiation time) 설정, 전면 엔코더로 파라미터 조정 및 채널값 메모리 저장 -- **주의:** 정확한 RS232 **명령어 포맷/체크섬/종결문자**는 제품 매뉴얼로 확정 필요. (밝기 스케일이 ERI의 0~255가 아니라 0~100%일 가능성 높음 → 변환 필요) - ---- - -## 5. 별도로 존재하는 CCS 조명 코드 (참고 — 본 건과 무관) - -- `AuroraVision/VisionLight/Light_CCS.cs` + `LightControlCCS_Library.dll`(C++) 가 별도로 존재. -- 단, 이건 **Socket(TCP/IP) 기반**(`managementMain.Connect(ip, port)`)이라 **RS232 EROOM 건과는 연결 방식이 다름**. -- PTOP 빌드 산출물 폴더에 `LightControlCCS_Library.dll` 이 복사돼 있으나, PTOP 소스에서 `Light_CCS` 를 직접 참조하지는 않음. → **이번 건과 혼동 주의.** - ---- - -## 6. 나중에 수정할 때 체크리스트 (코드 수정은 보류, 방안만 정리) - -### Step 1 — 하드웨어/포트 확정 (코드 수정 전) -- [ ] 컨트롤러 전면 스위치 **REMOTE** 전환 -- [ ] 장치관리자에서 **COM 번호** 확인, **Baud(38400 추정)/패리티/스톱비트** 확인 -- [ ] 시리얼 터미널로 EROOM 매뉴얼 명령 직접 전송 → 조명 반응 확인 (배선/포트 문제 격리) - -### Step 2 — 설정만으로 해결되는 경우 (EROOM ↔ ERI 프로토콜이 호환될 때) -- [ ] `LightConfig` 의 `PortNum` 을 실제 COM 번호로 -- [ ] `BaudRate` 19200 → 38400 등 사양에 맞게 -- [ ] (설정값은 가능하면 장비 Setting/UI에서 변경하도록, 하드코딩 지양) - -### Step 3 — 프로토콜이 다른 경우 (EROOM 전용 드라이버 신규 추가) -- [ ] `AuroraDevices/AuroraLight/eLight` enum 에 `EROOM` 추가 -- [ ] `AuroraDevices/AuroraLight/EROOM/EROOMLight.cs` 신규 (`SerialConn, IEROOMLight` 구현, ERI 클래스 패턴 참고) - - EROOM 매뉴얼대로 명령 포맷/밝기 스케일(0~100%) 구현 -- [ ] `LightDevice.DoActivate()` 의 `switch(_lightType)` 에 `case eLight.EROOM` 분기 추가 -- [ ] `CommonEquipmentSetup.cs:226` 의 `eLight.ERI` → `eLight.EROOM` 으로 변경 -- [ ] `ILight.cs` 에 필요 시 `IEROOMLight` 인터페이스 추가 - -### Step 4 — 검증 -- [ ] 시뮬레이션 해제 후 자동운전에서 `_Light.SetLightValue` 로 점등 확인 -- [ ] 수동/UI(`UCLight`)에서 채널별 밝기 제어 확인 - ---- - -## 7. 핵심 파일 위치 정리 - -| 역할 | 경로 | -|---|---| -| 조명 디바이스 등록 | `WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs:226` | -| 조명 디바이스 본체/드라이버 선택 | `AuroraDevices/AuroraLight/LightDevice.cs` | -| 조명 드라이버 인터페이스 | `AuroraDevices/AuroraLight/ILight.cs` | -| 시리얼/포트 설정 | `AuroraDevices/AuroraLight/LightConfig.cs` | -| 현재 사용 중인 ERI 드라이버 | `AuroraDevices/AuroraLight/ERI/ERILight.cs` | -| 시퀀스 사용처 | `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPrismaticScanner.cs` (L138, 1132, 356~, 535~) | -| 디바이스 접근 헬퍼 | `WeldingComponents/WeldingCommon/Sequences/SequenceContextEx.cs:54` (`GetLight`) | -| (참고) CCS Socket 조명 | `AuroraVision/VisionLight/Light_CCS.cs` | - ---- - ---- - -## 8. eLight.ERI 제어 소스 — 참고 코드/위치 모음 - -> 현재 `Light` 디바이스가 **`eLight.ERI` 드라이버로 등록**되어 동작하므로, EROOM 제어 구현 시 **그대로 베껴 쓸 수 있는 레퍼런스**가 ERI 계열이다. 아래는 제어 전체 흐름(등록 → 디바이스 → 드라이버 → UI → 시퀀스)의 실제 코드/위치. - -### 8.1 전체 제어 흐름 한눈에 - -``` -CommonEquipmentSetup.RegisterDevices() ← 디바이스 등록 (eLight.ERI 지정) - │ - ▼ -LightDevice (AuroraLight) ← 추상 디바이스, DoActivate에서 드라이버 선택 - │ _light = new ERILight(...) - ▼ -ERILight : SerialConn, IERILight ← RS232 ASCII 프로토콜 실제 송수신 - ▲ ▲ - │ │ -UCLightViewModel (수동 UI) SeqAutoRun...(자동운전) ← 두 곳에서 제어 호출 -``` - -### 8.2 ① 디바이스 등록 — `WeldingComponents/WeldingCommon/CommonEquipmentSetup.cs` - -- L48: 디바이스 활성상태 표시 등록 `.AddDeviceActiveStatus(CommonDeviceIds.Light)` -- L226~227: 디바이스 + UI 등록 -```csharp -registry.Register(CommonDeviceIds.Light, - () => new LightDevice(CommonDeviceIds.Light, eLight.ERI, isSimul), // 드라이버 타입 - new LightConfig(), // 시리얼 설정 - device => new UCLight() { // 제어 UI - DataContext = new UCLightViewModel(AuroraEnv, CommonDeviceIds.Light, _SequenceSetting.LightValue) - }); -``` - -### 8.3 ② 디바이스 본체 — `AuroraDevices/AuroraLight/LightDevice.cs` - -- `eLight` enum (L16~22): `LvsPn, ALT_4CH, ALT_8CH, ERI` → **EROOM 추가 지점** -- `DoActivate()` (L40~64): `switch(_lightType)` 에서 드라이버 생성 + `InitInternal(SerialConnConfig)` → **EROOM case 추가 지점** -- 외부 공개 제어 API: - - `SetLightValue(List values)` (L88) / `SetLightValue(int ch, int val)` (L93) - - `CheckLightOnAndValue(int val)` (L100) — ERI 전용으로 채널별 On+밝기 검증 -- `DoDeactivate()` (L66~79): ERI는 전 채널 0으로 끄고 시리얼 Close - -### 8.4 ③ ERI 시리얼 드라이버 — `AuroraDevices/AuroraLight/ERI/ERILight.cs` (RS232 프로토콜 본체) - -| 기능 | 메서드 | 명령/응답 포맷 | 라인 | -|---|---|---|---| -| 연결 | `InitInternal(SerialConnConfig)` | `Open(connConfig)` | L30 | -| 밝기 설정 | `SetLightBrightness(ch, val)` | TX `setbrightness {ch} {val}\r` (val 0~255) | L134 | -| On/Off | `SetLightPowerStatus(ch, on)` | TX `seton {ch} {0\|1}\r` | L122 | -| On 조회 | `GetLightOn(ch, out isOn)` | TX `geton {ch}\r` / RX `geton {idx} {ch} {0\|1}` | L186 | -| 밝기 조회 | `GetLightValue(ch, out val)` | TX `getbrightness {ch}\r` / RX `getbrightness {idx} {ch} {val}` | L219 | -| 채널 검증 | `ChannelValidCheck` (0~ch-1) / `ValueValidCheck` (0~255) | — | L162 / L172 | - -- 부모 `SerialConn` 의 `Open / Write / WriteAndReadLine / IsConnected / Close` 사용. -- **EROOM 적용 시 주의:** ASCII 명령어/종결문자(`\r`)/밝기 스케일(0~255)이 EROOM 매뉴얼과 다르면 이 클래스를 그대로 못 쓰고, 동일 구조로 `EROOMLight.cs` 신규 작성. - -### 8.5 ④ 수동 제어 UI — `AuroraDevices/AuroraLight/View/UCLightViewModel.cs` (+ `UCLight.xaml`) - -- 생성자(L67): `aurora.DeviceManager[deviceId] as LightDevice` 로 디바이스 획득 -- ERI일 때 채널/슬라이더 세팅(L76~84): `nChannel=4, nSliderMax=255` -- 슬라이더 → 밝기: `nSliderValue` setter(L21~24) → `_light.SetLightValueInternal(cbChannel, value)` -- 버튼 커맨드: - - `CMD_LightOnOff` → `LightOnOff()` (L111): 채널 토글 On/Off - - `CMD_ALLLightOn` → `ALLLightOn()` (L130): 전 채널 ON - - `CMD_ALLLightOff` → `ALLLightOff()` (L140): 전 채널 OFF -- **참고:** 채널수/스케일이 코드에 하드코딩(`nChannel=4`, `nSliderMax=255`)되어 있음 → EROOM(0~100%) 적용 시 여기 함께 손봐야 함. - -### 8.6 ⑤ 자동운전 사용처 — `Machines/ILSOS_PrismaticRD_PTOP/Sequences/SeqAutoRunPrismaticScanner.cs` - -- L138: `private LightDevice _Light;` -- L193: `private List lightValues = new List { 255, 255, 255, 255 };` -- L1132: `_Light = context.GetLight();` -- L353~359 / L532~537: 자동운전 진입 시 점등 -```csharp -if (_SequenceSetting.UseCellOffset) UpdateAutoLightValue(_SequenceSetting.CompensationLightValue); -else UpdateAutoLightValue(_SequenceSetting.LightValue); -if (!_Light.CheckLightOnAndValue(lightValues[0])) { // 현재 밝기와 다르면 - _Light.SetLightValue(lightValues); // 4채널 점등 - Log.I($"Light All On !"); -} -``` -- L1357: `UpdateAutoLightValue(int value)` — `lightValues` 갱신 헬퍼 - -### 8.7 ⑥ 디바이스 접근 헬퍼 — `WeldingComponents/WeldingCommon/Sequences/SequenceContextEx.cs` - -```csharp -public static LightDevice GetLight(this SequenceContext context) // L54 - => context.Devices[CommonDeviceIds.Light] as LightDevice; // L55 -``` - -### 8.8 인터페이스 / 설정 정의 - -- `AuroraDevices/AuroraLight/ILight.cs` — `ILight` 공통 인터페이스 + `IERILight`(L30, `IsOn`/`GetLightOn`/`GetLightValue` 추가). → EROOM용 `IEROOMLight` 추가 지점. -- `AuroraDevices/AuroraLight/LightConfig.cs` — `SerialConnConfig`(PortNum/BaudRate/Parity/StopBits/DataBits/ReadTimeout) 정의. - ---- - -> ⚠️ 본 문서는 **분석/가이드 전용**이며 코드는 수정하지 않았다. 실제 수정 전 EROOM 컨트롤러 매뉴얼(RS232 명령어/Baud/밝기 스케일)로 사양을 확정할 것. diff --git a/docs/분석/2026/06/18/102847_manual_laser_power_control_groupbox_analysis.md b/docs/분석/2026/06/18/102847_manual_laser_power_control_groupbox_analysis.md deleted file mode 100644 index 8102ee4..0000000 --- a/docs/분석/2026/06/18/102847_manual_laser_power_control_groupbox_analysis.md +++ /dev/null @@ -1,108 +0,0 @@ -# Manual Laser Power Control GroupBox 2개 분석 (Percent vs Watt) - -- **작성일:** 2026-06-18 -- **대상 파일:** - - View: `WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerLaserControl.xaml` - - ViewModel: `WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerLaserControlViewModel.cs` - ---- - -## 0. 내 요청사항 (배경) - -> Laser Voltage를 1V 주면 몇 W 나오는지 측정하는 **캘리브레이션(Cal) 파일**을 만들고 싶다. -> 이때 **레이저는 실제로 발진시키지 말고**, **아날로그 신호만 살려서** RTC5의 기록은 그대로 저장되도록 할 수 있는가? -> -> - 메뉴얼 화면 → Manual Laser Power Control 영역에서 레이저를 쏠지/안 쏠지 제어. -> - 보통 **레이저 컨트롤러 IO를 안 살리면** 레이저가 실제로 안 나간다. -> - 아날로그 신호만 활성화해서, 레이저는 안 나가되 RTC5 출력 기록은 정상 저장 가능한지 검토. - -→ 이 분석은 위 요청과 직접 연관됨. 특히 **아날로그 출력(WriteDA)** 과 **레이저 발진 신호(LaserSignalOn)** 가 코드상 분리되어 있다는 점이 핵심. - ---- - -## 1. 결론 요약 - -- XAML에 `Header="Manual Laser Power Control"` GroupBox가 **2개**(Grid.Row="0", Grid.Row="1") 있고, **레이아웃·구성 컨트롤은 100% 동일**하다. -- **유일한 실질 차이는 "파워 입력 단위"** 다. - - **Row 0 = Percent(%) 기준** → `LaserPower1Percent`/`LaserPower2Percent`, 버튼 → `CMD_LaserShotOnPercent` - - **Row 1 = Watt(W) 기준** → `LaserPower1Watt`/`LaserPower2Watt`, 버튼 → `CMD_LaserShotOnWatt` -- 즉 **똑같은 UI를 % 입력용 / W 입력용 두 벌로 둔 것**. Frequency·Pulse Width·Timer·Laser Off·Result 표시는 둘이 **완전히 공유**(같은 바인딩/커맨드). - ---- - -## 2. 두 GroupBox 차이 상세 - -| 항목 | Row 0 GroupBox (Percent) | Row 1 GroupBox (Watt) | -|---|---|---| -| Power #1 바인딩 | `LaserPower1Percent` (Unit `%`) | `LaserPower1Watt` (Unit `W`) | -| Power #2 바인딩 | `LaserPower2Percent` (Unit `%`) | `LaserPower2Watt` (Unit `W`) | -| Frequency | `LaserPeriod` (kHz) **공유** | `LaserPeriod` (kHz) **공유** | -| Pulse Width | `LaserPulseWidth` (us) **공유** | `LaserPulseWidth` (us) **공유** | -| Timer Set (Spot Time) | `IsUseLaserAutoOffChecked`, `LaserOnTime` **공유** | 동일 **공유** | -| Laser Shot Result | `BusyStatus`, `ListInterationCount` **공유** | 동일 **공유** | -| **Laser On 버튼** | `CMD_LaserShotOnPercent` | `CMD_LaserShotOnWatt` | -| **Laser Off 버튼** | `CMD_LaserShotOff` **공유(동일)** | `CMD_LaserShotOff` **공유(동일)** | - -> 정리: **다른 것은 "Power 입력 단위(% vs W)와 그에 대응하는 Laser On 커맨드" 뿐**. 나머지는 같은 ViewModel 프로퍼티/커맨드를 그대로 쓴다. - ---- - -## 3. 각 동작이 어디에 영향을 끼치는가 (ViewModel 분석) - -### 3-1. Laser On — `PerformCmdLaserShotOnPercent` / `PerformCmdLaserShotOnWatt` - -두 메서드는 구조가 동일하며, **`IsUseLaserAutoOffChecked`(Use Spot Time 토글) 값에 따라 두 갈래**로 갈린다. - -#### (A) `IsUseLaserAutoOffChecked == true` (= "Use Spot Time") -- 시퀀스(`SeqTeachingRun`)를 통해 발사. `CreateDotForLaserOn()`으로 현재 위치에 Dot 1개를 만들고 시퀀스 Start. -- **주의:** 이 분기는 Percent/Watt 둘 다 `CreateDotForLaserOn()`을 호출하는데, 이 함수 내부는 **`LaserPower1Percent`/`LaserPower2Percent`(% 기준)만 사용**한다(`LaserPowerCalParam.MaxCenterPower/MaxRingPower` 비율 환산). 즉 **Spot Time 모드에서는 Watt 버튼을 눌러도 실제로는 Percent 값으로 동작**한다. (Watt 입력이 무시되는 잠재 이슈) -- 사전 가드: Scanner Busy 체크 → `_Laser.GetLaserReady()` 체크 → 시퀀스 Running 체크 → 모델 선택 체크 → Blow 설정 반영. - -#### (B) `IsUseLaserAutoOffChecked == false` (= "No Use Spot Time") ★ Cal 작업 관련 핵심 -직접 아날로그/펄스 제어 후 레이저 신호 ON: -``` -powerCalParam.Initialize(...); -// 단위 환산만 다름: -// Percent 버튼: CalibratePercentToMeasuredVoltage(LaserPowerNPercent) * 10 -// Watt 버튼: CalibrateWattToMeasuredVoltage(LaserPowerNWatt) * 10 -Thread.Sleep(50); -_Scanner.WriteDA(DA1, powerVolt1, COMMAND); // ← 아날로그 전압 출력 (DA1) -_Scanner.WriteDA(DA2, powerVolt2, COMMAND); // ← 아날로그 전압 출력 (DA2) -_Scanner.SetLaserPulseCtrl(LaserPeriod*1000, LaserPulseWidth); // 펄스 주파수/폭 -_Scanner.LaserSignalOn(); // ← 레이저 발진 게이트 ON (실제 발사 트리거) -``` -- **TruFiberType 분기:** `TruFiberS`는 DA1만(단일빔), `TruFiberP` 등은 DA1=Center / DA2=Ring 두 빔으로 환산. -- **`* 10`**: 환산된 전압(0~1V 등 정규화 값)을 RTC5 DA 풀스케일(10V 기준)로 스케일링하는 것으로 보임. - -> **요청사항과의 연결:** -> - **아날로그 출력(`WriteDA`)** 과 **레이저 발진(`LaserSignalOn`)** 이 **코드상 별개 호출**이다. -> - 즉, 구조적으로 "아날로그 전압은 DA 포트로 내보내되 `LaserSignalOn()`은 호출하지 않거나, 레이저 컨트롤러 IO를 비활성"으로 두면 → **DA 아날로그 값은 출력되지만 실제 발진은 안 되는** 형태가 성립할 여지가 있다. -> - 단, "RTC5 기록이 그대로 저장되는가"는 별도 확인 필요(아래 §5 미해결 질문). - -### 3-2. Laser Off — `PerformCmdLaserShotOff` (두 GroupBox 공통) -- `IsUseLaserAutoOffChecked == true` → `_Scanner.StopExecution()` (시퀀스/리스트 중단) -- `false` → `_Scanner.LaserSignalOff()` (발진 게이트 OFF) -- 공통으로 `_Scanner.SetStandby(10, 0)`. - -### 3-3. Monitoring — `OnMonitoring` (100ms DispatcherTimer) -- Scanner 활성 체크, Encoder X/Y/속도 갱신, `GetMarkingInfo()`로 FieldOut X/Y 판정, `ReadStatus()`로 List1/List2 Busy 및 반복 횟수(`ListInterationCount`) 갱신. -- View가 Visible일 때만 동작(`CMD_VisibleChanged`). - -### 3-4. Marking On The Fly (Row 2, 별개 GroupBox) -- Encoder 기반 OTF 마킹 영역. `CMD_ActivateOnTheFly`/`Deactivate`, `ResetEncoder`, `SimulateEncoder(X/XY)`. -- 본 분석의 핵심(Manual Laser Power Control 2개)과는 별개 기능. - ---- - -## 4. "똑같은가?" 에 대한 답 -- **UI 레이아웃/구성: 똑같다.** (복붙 수준으로 동일) -- **기능: 다르다.** 한쪽은 **% 입력 → Percent 캘리브레이션**, 다른 한쪽은 **W 입력 → Watt 캘리브레이션**으로 전압을 만든다. -- **공유 리스크:** "Use Spot Time" 분기에서는 Watt 버튼도 내부적으로 Percent 값으로 동작(`CreateDotForLaserOn`이 Percent 전용) → Watt 의도와 불일치 가능. Cal 측정은 보통 "No Use Spot Time"(직접 DA 제어) 분기를 쓰므로 이 리스크와는 분리됨. - ---- - -## 5. 미해결 / 추가 확인 필요 (Cal 파일 작업 관련) -1. **`WriteDA`만으로 RTC5가 출력을 "기록/저장"하는가?** — 발진 없이 DA만 출력했을 때 RTC5 로깅 동작 확인 필요. -2. **`LaserSignalOn()` 생략 또는 레이저 컨트롤러 IO 비활성** 조합이 실제 장비에서 "아날로그만 살아있고 발진 안 됨"으로 동작하는지 현장 검증 필요. -3. **`* 10` 스케일과 `CalibrateWattToMeasuredVoltage` / `CalibratePercentToMeasuredVoltage`** 의 입출력 단위(반환값이 V 인지, 0~1 정규화인지) 정확한 정의 확인 → 1V당 W 환산표 작성의 기준. -4. Cal 측정 시 권장 경로: **"No Use Spot Time"(IsUseLaserAutoOffChecked=false) + Watt(또는 직접 전압) 모드**로 DA 출력값을 단계적으로 인가하며 파워미터 측정 → CSV(`PowerCalibrationCenterBeamCSVFile` / `RingBeamCSVFile`) 작성. diff --git a/docs/분석/2026/06/18/110655_ptop_cross_machine_reference_analysis.md b/docs/분석/2026/06/18/110655_ptop_cross_machine_reference_analysis.md deleted file mode 100644 index 66119d9..0000000 --- a/docs/분석/2026/06/18/110655_ptop_cross_machine_reference_analysis.md +++ /dev/null @@ -1,81 +0,0 @@ -# PTOP 가 비-PTOP(Scanner/TDC/Gun/CleanLaser) 경로에서 참조/가져오는 파일 분석 - -- **작성일:** 2026-06-18 -- **대상:** `Machines/ILSOS_PrismaticRD_PTOP` -- **목적:** PTOP 경로가 아닌 Scanner/TDC/Gun/CleanLaser 경로에서 참조해 가져오는 파일을 식별하고, PTOP 경로로 복사할 대상을 정리. (소스 무수정 원칙) - ---- - -## 0. 내 요청사항 - -> 참조해서 가져오는 파일 중에 PTOP 경로가 아닌 **SCANNER, TDC, GUN, CleanLaser** 경로에서 가져오는 파일들을 **PTOP 경로에 복사**해줘. 분석내용도 늘 정리해주고 **소스는 절대 건들지 마**. - ---- - -## 1. 결론 요약 (먼저) - -- **소스/프로젝트 레벨에서는 다른 머신 폴더의 파일을 가져오지 않는다.** - - PTOP `.csproj`의 `Compile/Content/Page/Resource Include`는 **전부 자기 폴더 내 상대경로**. - - `ProjectReference`는 **공유 프로젝트(AuroraDevices, AuroraVision, WeldingComponents)뿐** — Scanner/Gun/CleanLaser/TDC 프로젝트를 참조하지 않음. - - 소스/XAML 문자열에 다른 머신 폴더(`ILSOS_PrismaticScanner\` 등)를 가리키는 **파일 경로 참조 없음**(컴파일된 `.dll` 바이너리 매칭만 존재 → 무의미). - - 즉 **PTOP는 Scanner 프로젝트의 자체 포크**다. `EquipmentSetup.cs`가 `namespace ILSOS_PrismaticScanner`를 그대로 사용하고, `using ILSOS_PrismaticScanner.*`도 자기 자신(같은 네임스페이스) 또는 WeldingCommon으로 해석됨. - -- **유일한 비-PTOP 경로 참조는 "빌드 출력/실행 경로"가 TDC를 가리키는 것**이다. (아래 §2) - -- 따라서 **git 트래킹된 소스 중 복사할 "참조 파일"은 없다.** 실제로 다른 머신 경로에서 가져오는 것은 **런타임 출력(_out) 폴더 안의 빌드 산출물**이며, 이 폴더들은 **git 비추적(.gitignore)** 이다. (아래 §3) - ---- - -## 2. 빌드/실행 경로의 TDC 참조 (PTOP.csproj) - -| 항목 | 값 | 의미 | -|---|---|---| -| `OutputPath` (Debug\|x64) | `..\_out_prismatic_rd_tdc\x64-Debug\` | PTOP DLL이 **TDC 출력 폴더**로 빌드됨 | -| `OutputPath` (Release\|x64) | `..\_out_prismatic_rd_tdc\x64-Release\` | 동일 | -| `StartProgram` | `$(SolutionDir)\Machines\_out_prismatic_rd_tdc\x64-Debug\Aurora.exe` | 실행 시 **TDC 폴더의 Aurora.exe** 기동 | - -→ PTOP는 빌드/실행을 **`_out_prismatic_rd_tdc`**(TDC 경로)에서 수행한다. 별도로 존재하는 `_out_prismatic_rd_ptop` 폴더는 현재 빌드 구성에서 **사용되지 않음**. - -> 참고: `EquipmentSetup.cs` 주석에 `// 변경 TDC->PTOP`, `register(... SeqAutoRunTDC)` 등 TDC→PTOP 이행 흔적이 다수 있음. 즉 TDC 산출물 위에서 PTOP가 동작하도록 과도기 상태. - ---- - -## 3. 런타임 출력 폴더 구조 (실제 "가져오는" 파일의 위치) - -`Machines/_out_prismatic_rd_tdc/x64-Debug/` (PTOP가 실제 구동되는 폴더) 내부: - -- 공유 데이터/설정: `_alarm`, `_config_ingredient`, `_config_model`, `_config_recipe`, `_config_setting`, `_process`, `_vision`, `Light`, `RTC5_2024_09_27`, `AccountStorage` 등 -- **머신별 프로파일 서브폴더** (각 폴더에 `Aurora.exe.config`, `hislog.config` 2개씩): - - `ILSOS_PrismaticScanner/` - - `ILSOS_PrismaticGun/` - - `ILSOS_PrismaticCleanLaser/` - - `ILSOS_PrismaticRD_TDC/` - - **→ `ILSOS_PrismaticRD_PTOP/` 폴더는 없음** (PTOP 전용 프로파일 폴더가 부재) - -**git 추적 상태:** `_out_prismatic_*` 폴더는 **추적 파일 0개** (전부 .gitignore 빌드 산출물). - ---- - -## 4. "복사 대상" 판정 - -- **소스 레벨 복사 대상: 없음.** (다른 머신 폴더의 소스를 참조하지 않음 → 복사할 것이 없음. 소스 무수정 원칙과도 부합.) -- **런타임 레벨 후보:** 머신별 프로파일 폴더(`Aurora.exe.config`, `hislog.config`)가 Scanner/TDC/Gun/CleanLaser용으로는 있는데 **PTOP용(`ILSOS_PrismaticRD_PTOP/`)이 없음.** - - 가장 그럴듯한 작업: TDC(또는 Scanner) 프로파일을 **`ILSOS_PrismaticRD_PTOP/` 폴더로 복사**해 PTOP 전용 프로파일을 만드는 것. - - 단, 이 폴더는 **빌드 시 재생성되는 비추적 산출물**이라 다음 빌드에서 덮어쓰일 수 있음(영속성 한계). - ---- - -## 5. 미해결 / 확인 필요 - -1. **사용자가 의도한 "참조 파일"이 §3의 머신별 프로파일(config) 폴더가 맞는지** 확인 필요. (소스가 아니라 런타임 config로 보임) -2. **목적지 "PTOP 경로"** 가 (a) 런타임 `_out_prismatic_rd_tdc/.../ILSOS_PrismaticRD_PTOP/` 인지, (b) `_out_prismatic_rd_ptop/` 인지, (c) 소스 트리의 PTOP 프로젝트 폴더인지 확정 필요. -3. 빌드 OutputPath가 TDC를 가리키는 것이 **의도된 과도기 설정인지, 정리 대상(버그)인지** — 향후 PTOP 독립 출력(`_out_prismatic_rd_ptop`)으로 전환할지 결정 필요. - ---- - -## 6. 진행 방향 (2026-06-18 결정) - -- 어차피 **시작 프로젝트를 PTOP로 지정**하고, **언로드 중인 나머지(Scanner/Gun/CleanLaser/TDC)는 제외**하는 작업을 할 예정. -- 그 과정에서 PTOP `.csproj`의 빌드/실행 경로(현재 TDC 참조: `OutputPath`, `StartProgram`)도 **결국 PTOP 경로로 변경**할 것임 → §2의 TDC 참조는 그때 정리됨. -- 따라서 지금은 **분석만 남기고 복사는 보류**. (소스 무수정 원칙 유지) -- PTOP 단독 구성으로 전환 시: OutputPath/StartProgram을 `_out_prismatic_rd_ptop`로 바꾸고, 런타임 프로파일 폴더도 `ILSOS_PrismaticRD_PTOP/`로 생성/정리. diff --git a/docs/분석/2026/06/18/111604_ptop_namespace_migration_guide.md b/docs/분석/2026/06/18/111604_ptop_namespace_migration_guide.md deleted file mode 100644 index d22b2f3..0000000 --- a/docs/분석/2026/06/18/111604_ptop_namespace_migration_guide.md +++ /dev/null @@ -1,135 +0,0 @@ -# PTOP 네임스페이스/클래스 정리 마이그레이션 분석 (Scanner → PTOP) - -- **작성일:** 2026-06-18 -- **대상:** `Machines/ILSOS_PrismaticRD_PTOP` (현재 작업본) -- **정답 참고본(빌드 성공):** `D:\Git\LGES-OC5-PtoP-Welder_정리후(빌드완)\Machines\ILSOS_PrismaticRD_PTOP` -- **목적:** 프로젝트명만 바꾸고 Sequence 일부만 PTOP로 바꾼 현재 상태를, 참고본처럼 **네임스페이스·클래스·파일명을 PTOP로 안전하게 정리**(빌드 깨짐 방지)하기 위한 분석. - ---- - -## 0. 내 요청사항 - -> 현재 PTOP 프로젝트에 `using`이 SCANNER로 남아있는(=PTOP로 변경 안 한) 부분이 있어서, 수정하다 실수하면 빌드가 아예 안 될까 봐 걱정. Sequence만 바꾸고 프로젝트명만 바꿔둔 상태라 잘 수정하기 위한 분석이 필요. 잘 정리된 좋은 예시가 `D:\Git\LGES-OC5-PtoP-Welder_정리후(빌드완)`에 있음. 이런 형태로 수정하려고 함. Machines가 PTOP만 있는 것도 확인 가능. - ---- - -## 1. 현재 상태 vs 참고본 (요약) - -| 항목 | 현재(CUR) | 참고본(REF, 빌드완) | -|---|---|---| -| `Machines/` 구성 | PTOP + Scanner/Gun/CleanLaser(+_out들) | **PTOP 단독** | -| `namespace ILSOS_PrismaticScanner` | **26개 파일** (obj 4개 포함) | 0개 | -| `namespace ILSOS_PrismaticRD_PTOP` | 3개 파일만 (Properties 2 + SeqAutoRunPTOP) | 30개 파일 | -| `using ILSOS_PrismaticScanner.*` | **15개 파일** | 0개 | -| RootNamespace / AssemblyName | `ILSOS_PrismaticRD_PTOP` (이미 동일) | `ILSOS_PrismaticRD_PTOP` | - -→ **csproj의 RootNamespace/AssemblyName은 이미 PTOP**인데, **실제 소스의 namespace/using/클래스명/파일명이 Scanner로 남아있는** 불일치 상태. - ---- - -## 2. 변경 매핑 (참고본 기준 — 이대로 맞추면 됨) - -### 2-1. 네임스페이스 (전 파일 공통) -- `namespace ILSOS_PrismaticScanner` → `namespace ILSOS_PrismaticRD_PTOP` -- `using ILSOS_PrismaticScanner.Alarm|Device|Device.IO|Models|Recipes|Settings|Sequences|Views;` - → 동일 경로의 `ILSOS_PrismaticRD_PTOP.*` 로 변경 - -### 2-2. XAML (x:Class + clr-namespace) — 2개 파일 -- `Views/Main/UCMainAutoRun.xaml` -- `Views/Model/UCEquipmentSpecificEditor.xaml` -- 각: `x:Class="ILSOS_PrismaticScanner.Views..."` → `ILSOS_PrismaticRD_PTOP.Views...`, - `xmlns:local="clr-namespace:ILSOS_PrismaticScanner.Views"` → `...ILSOS_PrismaticRD_PTOP.Views` -- ⚠ 짝꿍 `.xaml.cs`(code-behind)의 namespace도 함께 변경. `obj/.../*.g.cs`·`*.g.i.cs`는 **빌드시 자동 재생성**이므로 직접 수정 불필요(무시). - -### 2-3. 파일명 + 클래스명 리네임 (참고본이 실제로 바꾼 것) - -| 현재 파일 | 현재 클래스 | → 참고본 파일 | → 참고본 클래스 | -|---|---|---|---| -| `Alarm/AlarmCode_PrismaticScanner.cs` | `AlarmCode_PrismaticScanner` | `Alarm/AlarmCode_PTOP.cs` | `AlarmCode_PTOP` | -| `Alarm/AlarmManager_PrismaticScanner.cs` | `AlarmManager_PrismaticScanner` | `Alarm/AlarmManager_PTOP.cs` | `AlarmManager_PTOP` | -| `Device/IO/PAIX_PrismaticScanner.cs` | `PAIX_PrismaticScanner` | `Device/IO/PAIX_PTOP.cs` | `PAIX_PTOP` | -| `Models/EquipmentSpecificModel_PrismaticScanner.cs` | `EquipmentSpecificModel_PrismaticScanner` | `Models/EquipmentSpecificModel_PTOP.cs` | `EquipmentSpecificModel_PTOP` | -| `Recipes/RMSHandlerForScanner.cs` | `RMSHandlerForScanner` | `Recipes/RMSHandlerForPTOP.cs` | `RMSHandlerForPTOP` | -| `Settings/SettingUploadDataListForScanner.cs` | `SettingUploadDataListForScanner` | `Settings/SettingUploadDataListForPTOP.cs` | `SettingUploadDataListForPTOP` | -| `Sequences/SeqAutoRunPrismaticScanner.cs` | `SeqAutoRunPrismaticScanner` | `Sequences/SeqAutoRunPrismatic.cs` | `SeqAutoRunPrismatic` | -| `Device/PLC/PLCContoller.Config.cs` 외 2 (파일명 유지) | `PLCControllerPrismaticScanner` | (파일명 동일) | `PLCControllerPTOP` | - -> PLC 3개 파일(`PLCContoller.Config.cs`, `PLCController.Address.cs`, `PLCController.Logic.cs`)은 **파일명은 유지**, 내부 **클래스명만 `PLCControllerPrismaticScanner` → `PLCControllerPTOP`**. - -### 2-4. 클래스명 참조 전파 (find & replace 대상 파일 수, obj 제외) - -| 심볼 | 영향 파일 수 | 비고 | -|---|---|---| -| `PLCControllerPrismaticScanner` | **13** | 가장 광범위 — 주의 | -| `PAIX_PrismaticScanner` | 5 | ⚠ **충돌 주의(아래 §3-1)** | -| `SeqAutoRunPrismaticScanner` | 5 | | -| `AlarmCode_PrismaticScanner` | 2 | | -| `AlarmManager_PrismaticScanner` | 2 | | -| `EquipmentSpecificModel_PrismaticScanner` | 2 | | -| `RMSHandlerForScanner` | 2 | | -| `SettingUploadDataListForScanner` | 2 | | - -### 2-5. csproj `Compile Include` 경로 갱신 (파일명 변경분 7줄) -``` -Alarm\AlarmCode_PrismaticScanner.cs → Alarm\AlarmCode_PTOP.cs -Alarm\AlarmManager_PrismaticScanner.cs → Alarm\AlarmManager_PTOP.cs -Device\IO\PAIX_PrismaticScanner.cs → Device\IO\PAIX_PTOP.cs -Models\EquipmentSpecificModel_PrismaticScanner.cs → Models\EquipmentSpecificModel_PTOP.cs -Recipes\RMSHandlerForScanner.cs → Recipes\RMSHandlerForPTOP.cs -Settings\SettingUploadDataListForScanner.cs → Settings\SettingUploadDataListForPTOP.cs -Sequences\SeqAutoRunPrismaticScanner.cs → Sequences\SeqAutoRunPrismatic.cs -``` - ---- - -## 3. 빌드 깨짐 유발 — 반드시 피해야 할 함정 ★ - -### 3-1. (최우선) `PAIX_PrismaticScanner` 이름 충돌 — **전역 일괄치환 금지** -- **머신 클래스**: `ILSOS_PrismaticScanner.Device.IO.PAIX_PrismaticScanner` → `PAIX_PTOP`로 변경 대상. -- **그러나 공유 클래스가 별도로 존재**: `WeldingCommon.Devices.Laser.PAIX_PrismaticScanner` - (선언: `WeldingComponents/WeldingCommon/Devices/Laser/LaserIOControl.cs`). -- 현재 `Sequences/SeqTestRun.cs:32` 가 **공유 쪽**을 참조: `using static WeldingCommon.Devices.Laser.PAIX_PrismaticScanner;` -- ⚠ `PAIX_PrismaticScanner` 를 **문자열 전역 치환하면 이 공유 참조까지 `PAIX_PTOP`로 바뀌어 컴파일 에러**. (WeldingCommon은 수정 금지 영역이기도 함.) -- **올바른 방법:** 머신 IO를 가리키는 4곳만 `PAIX_PTOP`로 변경하고, `SeqTestRun.cs`의 `WeldingCommon.Devices.Laser.PAIX_PrismaticScanner` 는 **그대로 둔다**. - - 변경 대상(머신): `EquipmentSetup.cs`(L153 생성/주석 L130), `Sequences/Manual/SeqComponentTeachingRun.cs`(L80/197 및 주석), `Sequences/Monitoring/SeqAlarmMonitor.cs`(L19 `using static ILSOS_PrismaticScanner.Device.IO.PAIX_PrismaticScanner` / L49 / L130). - - 유지(공유): `Sequences/SeqTestRun.cs:32`. - -### 3-2. WeldingCommon / Aurora 등 공유 영역은 손대지 않는다 -- 본 작업은 **PTOP 머신 프로젝트 내부에 한정**. 공유 프로젝트(WeldingCommon, AuroraDevices…)의 `PAIX_PrismaticScanner`, `RMSHandlerForScanner` 등 **동명 심볼이 있더라도 건드리지 않음**. - -### 3-3. obj 생성물은 무시 -- `obj/x64/Debug/Views/.../*.g.cs`, `*.g.i.cs`(현재 Scanner namespace) 는 **XAML 기준 자동 재생성**. 직접 수정 X. (clean 후 재빌드하면 갱신됨) - -### 3-4. 짝(파트너) 누락 주의 -- XAML 변경 시 **3종 세트**가 일치해야 함: `*.xaml`(x:Class) ↔ `*.xaml.cs`(namespace/class) ↔ csproj ``/``. -- 파일명 리네임 시 **csproj `Compile Include` 경로**도 같이 바꿔야 빌드 인식(§2-5). - -### 3-5. 파일 리네임은 git 추적 유지 -- `git mv` 로 리네임하여 히스토리 유지(클래스명/파일명 동시 변경). - ---- - -## 4. 권장 안전 작업 순서 -1. **백업/브랜치**: 별도 작업 브랜치에서 진행. -2. **파일 리네임**(`git mv`) 7개 → 클래스명 변경(파일 내부 선언부). -3. **namespace 일괄 변경**: `namespace ILSOS_PrismaticScanner` → `ILSOS_PrismaticRD_PTOP` (26 파일, obj 제외). -4. **using 일괄 변경**: `using ILSOS_PrismaticScanner.` → `using ILSOS_PrismaticRD_PTOP.` (15 파일). -5. **클래스명 참조 전파**: §2-4 심볼별로 **머신 참조만** 신규명으로 치환. ★ `PAIX_PrismaticScanner` 는 §3-1 규칙대로 공유 참조 1곳 제외. -6. **XAML 3종 세트** 변경(§2-2). -7. **csproj `Compile Include` 경로** 갱신(§2-5). -8. **clean → rebuild**. obj 재생성 확인. 참고본과 `namespace`/`using` 카운트(0 / 0) 일치 확인. - ---- - -## 5. 검증 체크리스트 (작업 후) -- [ ] `grep -r 'namespace ILSOS_PrismaticScanner' (obj 제외)` → 0건 -- [ ] `grep -r 'using ILSOS_PrismaticScanner'` → 0건 -- [ ] `WeldingCommon.Devices.Laser.PAIX_PrismaticScanner` 참조(SeqTestRun.cs)는 **그대로 존재** -- [ ] csproj `Compile Include` 7줄이 새 파일명과 일치 -- [ ] XAML x:Class / clr-namespace / code-behind namespace 3종 일치 -- [ ] Debug|x64 리빌드 성공 (참고본과 동일 구성) - ---- - -## 6. 참고: 참고본에만 있는 추가 파일(범위 밖, 참고만) -참고본에는 현재본 대비 `Sequences/SeqTestRun.cs`, `Sequences/Monitoring/SeqAlarmMonitor.cs`, `SeqStatusMonitor.cs` 등이 정리되어 있음. 본 마이그레이션의 핵심(네임스페이스/리네임)과 별개의 기능 정리이므로, 우선 §2~§5의 리네임 정합성에 집중. diff --git a/docs/분석/2026/06/18/114445_analog_only_no_laser_toggle_analysis.md b/docs/분석/2026/06/18/114445_analog_only_no_laser_toggle_analysis.md deleted file mode 100644 index 6765e59..0000000 --- a/docs/분석/2026/06/18/114445_analog_only_no_laser_toggle_analysis.md +++ /dev/null @@ -1,117 +0,0 @@ -# Manual Laser Power Control — "레이저 컨트롤러 OFF + 전압만 인가(NoLaser)" 분석 및 채택안 - -- **작성일:** 2026-06-18 (갱신: 최종 채택안 = IO Control DO9/13 + `IsNoLaser`) -- **관련 View/VM:** - - 채택 지점: `WeldingComponents/WeldingCommon/Views/Recipes/UCScannerObjectTeachingControlBox.xaml` (+ `...ViewModel.cs`) - - 초기 검토 지점(원복): `WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerLaserControl.xaml` (+ VM) -- **디바이스:** `AuroraDevices/AuroraLaserScanner/Scanner.cs`, `ScannerRTC5.cs`, `WeldingCommon/Devices/Laser/LaserIOControl.cs` -- **선행 분석:** `102847_manual_laser_power_control_groupbox_analysis.md` - ---- - -## 0. 내 요청사항 - -> Manual Laser Power Control 영역에서 **레이저(발진, lasing)는 안 나가게 하고 전압(Voltage)만 인가**되도록 하고 싶다. `Laser On`을 눌렀을 때 동작하도록 **체크박스/토글** 하나가 있어야 할 것 같다. (Cal 파일 작성용: 레이저는 안 쏘고 전압만 인가, 기록은 남기기) -> -> 추가 확인(현장): "Laser On 누르면 원래 **IO Control 9번이 true**가 되는데, 그 IO를 true로 안 켜면(false 유지) 발진 없이 동작한다." → 그 지점을 막는 방식으로. - ---- - -## 1. 결론 요약 (최종 채택안) - -- **실제 발진을 만드는 지점은 `LaserSignalOn`(RTC5 게이트)이 아니라 IO Control 비트였다.** - - **DO 9 = `CoreLaser_Control`**, **DO 13 = `RingLaser_Control`** (PAIX `ePrismaticDOAddress`). - - 이 둘이 true가 되면 레이저 컨트롤러가 발진 가능(enable) 상태가 됨. -- 따라서 **이 DO9/13을 켜는 부분을 NoLaser일 때 false로 유지**하면, 전압(WriteDA)은 인가되되 레이저 컨트롤러는 OFF → 발진 없이 `Laser On` 가능. -- 채택 위치: `UCScannerObjectTeachingControlBoxViewModel.PerformCmdLaserReadyCheck` (Laser Control GroupBox의 **Laser Ready** 버튼). -- 구현: ViewModel에 `IsNoLaser`(bool, 기본 false) 추가 + 켜는 분기에 `ctrlOn = !IsNoLaser` 적용 + XAML 체크박스. - ---- - -## 2. 메커니즘 근거 - -### 2-1. 전압 출력과 발진은 독립 (선행 확인) -- `Scanner.WriteDA(port, value)` → RTC5 `n_write_da_x` (아날로그 전압 출력). `ScannerRTC5.WriteDA`: `targetValue = value * 40.95` (0~4095 = 0~10V). 즉 **출력 전압[V] = value/10**. -- `Scanner.LaserSignalOn()` → RTC5 `n_laser_signal_on` (스캐너측 레이저 Active 신호). 전압 출력과 별개 호출. - -### 2-2. ★ 실제 레이저 컨트롤러 Enable = IO DO9/DO13 -`WeldingCommon/Devices/Laser/LaserIOControl.cs` 의 `ePrismaticDOAddress`: -``` -CoreLaser_Control = 9 -CoreLaser_ErrorClearing -RingLaser_Control = 13 -RingLaser_ErrorClearing -``` -→ DO9/DO13(Core/Ring Laser Control)을 true로 주면 레이저 컨트롤러가 켜짐(발진 가능). **이게 "발진을 만드는 IO".** -(참고: DI9 = `CoreLaser_Laser_Ready`는 읽기 신호로 별개.) - -### 2-3. DO9/13을 켜는 코드 위치 -`UCScannerObjectTeachingControlBoxViewModel.PerformCmdLaserReadyCheck` — `Laser Ready` 버튼이 `toggleIO`로 DO9/DO13을 true/false 토글. -```csharp -if (toggleIO) { - _IODevice._iOUnit.WriteDoBit(9, true); // CoreLaser_Control ON - _IODevice._iOUnit.WriteDoBit(13, true); // RingLaser_Control ON - toggleIO = false; -} else { - _IODevice._iOUnit.WriteDoBit(9, false); - _IODevice._iOUnit.WriteDoBit(13, false); - toggleIO = true; -} -``` - ---- - -## 3. 채택 구현 (적용 완료) - -### 3-1. ViewModel — `IsNoLaser` 추가 -```csharp -private bool _IsNoLaser; -public bool IsNoLaser { get => _IsNoLaser; set => SetProperty(ref _IsNoLaser, value); } -// InitVariable()에서 IsNoLaser = false (기본: 기존 동작) -``` - -### 3-2. 켜는 분기를 NoLaser면 false로 -```csharp -if (toggleIO) { - // IsNoLaser면 레이저 컨트롤러(BIT9/13)를 켜지 않음(false 유지) → 발진 없이 Laser On 가능 - bool ctrlOn = !IsNoLaser; - _IODevice._iOUnit.WriteDoBit(9, ctrlOn); - _IODevice._iOUnit.WriteDoBit(13, ctrlOn); - toggleIO = false; -} else { - _IODevice._iOUnit.WriteDoBit(9, false); - _IODevice._iOUnit.WriteDoBit(13, false); - toggleIO = true; -} -``` - -### 3-3. XAML — 체크박스 (Laser Control GroupBox) -```xml - -``` - -### 3-4. 동작 표 -| IsNoLaser | Laser Ready 버튼 | 결과 | -|---|---|---| -| **false (기본)** | DO9/13 ON/OFF 토글 | 기존과 동일(레이저 컨트롤러 켜짐 → 발진 가능) | -| **true (NoLaser)** | DO9/13을 **false 유지** | 레이저 컨트롤러 OFF, 발진 없이 진행(전압만) | - -> 적용 = 커밋 `8ec4fd0`. 디바이스 레이어(Scanner/RTC5) 무변경, IO 비트 9/13만 제어. - ---- - -## 4. 초기 검토안(원복) 기록 -- 처음엔 `UCScannerLaserControl`(Manual Laser Power Control)에 `IsAnalogOnly`를 추가해 `LaserSignalOn()`(RTC5 게이트)을 조건부로 막는 방식을 구현했음. -- 그러나 현장 확인 결과 **실제 발진 Enable은 다른 소스(ObjectTeachingControlBox)의 IO DO9/13**임이 확인되어, 해당 변경은 **원복**(Watt 숨김 커밋 `ad6cf68`은 유지)하고 본 §3의 DO9/13 방식으로 재구현함. -- 교훈: "발진을 막는 지점"이 RTC5 게이트(`LaserSignalOn`)가 아니라 **레이저 컨트롤러 Enable IO(DO9/13)** 였음. - ---- - -## 5. 주의 / 추가 확인 -1. **전압 표시(요청)**: 인가 전압을 화면에 표시하려면, 하드웨어 readback API는 없으므로 **명령 전압값(= WriteDA에 쓰는 powerVolt/10 [V])** 을 VM 프로퍼티로 노출해 표시하는 방식 권장(디바이스 무변경). → 별도 작업으로 진행 가능. -2. **RTC5 기록 저장**: WriteDA(전압)는 발진과 무관하게 수행되므로 전압 명령 자체는 남음. "기록 저장"이 앱 로깅을 의미하면 별도 로깅 경로 확인 필요. -3. **안전**: NoLaser는 기본 false. 체크 상태를 화면에 명확히 표시(체크박스 라벨). NoLaser일 때는 DO9/13이 절대 true로 가지 않음(켜는 분기·끄는 분기 모두 false). -4. **Manual Laser Power Control(UCScannerLaserControl) 쪽**: 현재 Watt GroupBox는 숨김(`Collapsed`), Analog Only 변경은 원복됨. 추후 그쪽에도 동일 NoLaser 연동이 필요한지는 운영하며 판단. diff --git a/docs/분석/2026/06/18/122432_laser_on_no_emission_analysis.md b/docs/분석/2026/06/18/122432_laser_on_no_emission_analysis.md deleted file mode 100644 index 6a67301..0000000 --- a/docs/분석/2026/06/18/122432_laser_on_no_emission_analysis.md +++ /dev/null @@ -1,90 +0,0 @@ -# "Laser On 눌러도 레이저가 안 나옴" 원인 분석 (기능 존재 여부) - -- **작성일:** 2026-06-18 -- **대상:** `WeldingComponents/WeldingCommon/Views/Manual/Scanner/UCScannerLaserControlViewModel.cs` (`PerformCmdLaserShotOnPercent` / `...Watt`) -- **디바이스:** `AuroraDevices/AuroraLaserScanner/Scanner.cs`, `ScannerRTC5.cs` (Trumpf TruFiber 레이저) -- **선행:** `102847_...groupbox_analysis.md`, `114445_...no_laser_toggle_analysis.md` - ---- - -## 0. 증상 / 질문 - -> IO Control(DO9/13)도 true, DI(Laser Ready)도 잘 읽혀서 Laser Ready도 true → 레이저가 나와야 하는데 **Laser On 눌러도 레이저가 안 나온다.** 기능이 현재 없는 건지 분석. - ---- - -## 1. 결론 - -- **준비조건(IO/Ready)은 문제 없음.** 문제는 발사 명령 자체에서 **레이저 Gate 신호 셋업이 빠져있는 것**. -- **Trumpf 레이저는 `SetLaserTiming`으로 Gate 신호를 살려줘야 출사된다**(코드 주석으로 명시 + 241025 출사 확인 기록). -- 그런데 수동 **Laser On 직접 분기(No Use Spot Time)** 에서는 `SetLaserTiming`(과 `SetStandby`)이 **주석 처리**되어 있어, `LaserSignalOn()`만으로는 Gate가 안 열려 **실제 출사가 안 됨.** -- 즉 "직접 출사" 기능은 **사실상 미완성(셋업 라인 비활성)** 상태. 기능 자체가 아예 없는 건 아니고, **Gate 셋업 한 줄이 빠진 것**. - ---- - -## 2. 근거 - -### 2-1. Laser On 직접 분기 (현재 코드, `PerformCmdLaserShotOnPercent` else 분기) -```csharp -_Scanner.WriteDA(DA1, powerVolt1, COMMAND); // 전압 OK -_Scanner.WriteDA(DA2, powerVolt2, COMMAND); -_Scanner.SetLaserPulseCtrl(LaserPeriod*1000, LaserPulseWidth); -//_Scanner.SetLaserTiming(LaserPeriod*1000, LaserPulseWidth, LaserPulseWidth); // ← 주석! -//_Scanner.SetStandby(LaserPeriod*1000, LaserPulseWidth); // ← 주석! -_Scanner.LaserSignalOn(); // Gate(LASERON) 신호만 올림 -``` -→ `LaserSignalOn()`은 RTC5 `n_laser_signal_on`(레이저 Active 신호)만 올림. **Gate 타이밍(SetLaserTiming) 설정이 없으면 Trumpf 레이저는 출사 안 됨.** - -### 2-2. "Trumpf는 SetLaserTiming으로 Gate 살려야 함" 명시 (SeqUploadWeldingList.cs:624~626) -``` -//TrumpLaser의 경우 SetLaserTiming 함수로 Gate 신호를 살려줘야 함 -//우선 무조건 셋팅하는 것으로 쓰고 Trump_IO Class 만들면 ... Trump의 경우에만 셋팅 -//LaserFrequency = 10, LaserPulseDuration = 0.1로 셋팅하면 출사 되는 것 확인_241025 -``` -→ **출사 확인된 셋업: `SetLaserTiming(주파수=10, PulseDuration=0.1ms)`** (= `SetLaserTiming(10, 100, 100)` 형태). - -### 2-3. 실제 출사되는 경로는 List + Timing 셋업 사용 -- `SeqUploadWeldingList` / `SeqMarkGrid` 등 실제 마킹 경로는 `SetLaserTiming(...)` + `LaserOnList(...)`(리스트 기반)로 출사. -- 수동 버튼 직접 분기는 리스트가 아닌 `LaserSignalOn` 직접 호출인데 Timing 셋업이 빠짐. - ---- - -## 3. 두 가지 Laser On 경로 정리 - -| 모드(토글) | 경로 | 출사 여부 | -|---|---|---| -| **Use Spot Time** (기본값 `IsUseLaserAutoOffChecked=true`) | `SeqTeachingRun.Start(...)` (리스트 기반, `NeedSetLaserTiming=true` 전달) | 모델 선택 등 전제 충족 시 출사(스팟). 단 모델 null/Busy/LaserReady false면 경고 후 미발사 | -| **No Use Spot Time** (직접) | `WriteDA → SetLaserPulseCtrl → LaserSignalOn` | **SetLaserTiming 주석 → Gate 안 열림 → 출사 안 됨** ★ | - -> 사용자가 "직접 한 방 쏘려고" No Use Spot Time로 눌렀다면 §2-1 이유로 안 나옴. -> Use Spot Time(기본)인데도 안 나온다면 §3 표의 전제(모델 선택/Busy/`_Laser.GetLaserReady()`)에서 막혔을 가능성(토스트 메시지 확인 필요). - ---- - -## 4. 권장 수정 (직접 분기 출사 활성화) - -직접 분기에서 `LaserSignalOn()` **앞에** Gate 타이밍을 살려준다: -```csharp -_Scanner.SetLaserPulseCtrl(LaserPeriod*1000, LaserPulseWidth); -_Scanner.SetLaserTiming(LaserPeriod, LaserPulseWidth, LaserPulseWidth); // ← 주석 해제/추가 (Gate 신호 enable) -_Scanner.LaserSignalOn(); -``` -- 우선 검증은 **확인된 값**으로: `SetLaserTiming(10, 100, 100)`(주파수 10, PulseDuration 0.1ms) → 출사 확인된 셋업(241025). -- 그 후 화면 입력값(`LaserPeriod`/`LaserPulseWidth`) 단위를 SetLaserTiming 인자 단위(periodHz, pulseWidthUs)에 맞춰 매핑 필요. -- Laser Off 시 `LaserSignalOff()` 유지(이미 있음). - -> ⚠ 안전: 이 수정은 **실제 레이저를 출사**시키므로, 적용/테스트는 차폐·인터락·저파워에서 단계적으로. (NoLaser 옵션과 함께 쓰면 컨트롤러 OFF로 무발광 검증 가능.) - ---- - -## 5. 추가 확인 필요 -1. 사용자가 누른 Laser On이 **어느 모드(Use Spot Time vs No Use Spot Time)** 였는지 → 토스트 메시지/화면 토글 상태 확인. -2. `SetLaserTiming` 인자 단위(periodHz/us)와 화면 입력값(`LaserPeriod` kHz, `LaserPulseWidth` us) 정합 매핑. -3. 현재 레이저 타입(`_LaserConfig.TruFiberType`)이 Trumpf 계열인지(맞으면 Gate 셋업 필수). -4. Use Spot Time 경로가 막히는 경우 전제조건(`_Laser.GetLaserReady()`, 모델 선택, Scanner Busy) 로그 확인. - ---- - -## 6. 답: "기능이 없는 건가?" -- **완전히 없는 건 아님.** Use Spot Time(시퀀스) 경로는 출사 로직이 있음. -- 다만 **직접 출사(No Use Spot Time) 경로는 Gate 셋업(`SetLaserTiming`)이 주석 처리되어 미완성** → 그래서 IO/Ready가 다 맞아도 안 나옴. §4 한 줄(또는 확인값) 추가가 핵심. diff --git a/docs/분석/2026/06/18/143345_io_interface_di_path_ptop_analysis.md b/docs/분석/2026/06/18/143345_io_interface_di_path_ptop_analysis.md deleted file mode 100644 index 9b23b20..0000000 --- a/docs/분석/2026/06/18/143345_io_interface_di_path_ptop_analysis.md +++ /dev/null @@ -1,106 +0,0 @@ -# Monitoring IOInterfaceView DI 파일 경로 — PTOP 바라보게 하기 분석 - -- **작성일:** 2026-06-18 -- **목표:** 작업환경에서 IOInterfaceView가 `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\_device\_io` 의 DI 파일을 보게 하기 -- **대상:** `AuroraDevices/AuroraIO/IODevice.cs`, `AuroraDevices/AuroraIO/View/UCIOInterfaceViewModel.cs`, 설정 `IO_Board.json` - ---- - -## 0. 요청 - -> 마이그레이션 전(현장 소스)에는 PTOP 프로젝트 시작이어도 Monitoring의 IOInterfaceView가 `D:\ILSOSConfig\ILSOS_PrismaticScanner\_device\_io` 의 DI 파일을 참조해 보여줬다. 내 작업환경에서 **PTOP(`D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP`)** 데이터를 보게 하려면 어디를 고쳐야 하나? 수정 위치 + diff. 문제 없으면 그 이유도. - ---- - -## 1. 결론 (요약) - -- **소스 코드는 고칠 필요가 없다(IO DI 한정).** DI 파일 경로는 **소스에 없고**, **설정 파일 `IO_Board.json` 의 `IOInterfaceMapDI`(절대경로)** 값으로 결정된다. -- `IODevice.MappingDIO()`는 그 절대경로를 **그대로 읽는다**(DataPath 등 가공 없음). 즉 **경로 무관(path-agnostic)** 코드 → 버그 없음. -- 따라서 PTOP를 바라보게 하는 건 **설정/환경 변경**: - 1. 앱이 로드하는 config 폴더(DataPath)가 `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP` 여야 함. - 2. 그 폴더의 `_config_device\IO_Board.json` 의 `IOInterfaceMapDI`/`IOInterfaceMapDO` 가 **PTOP 경로**를 가리켜야 함. - 3. 실제 `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\_device\_io\DI.csv`(DO.csv) 파일이 존재해야 함. - ---- - -## 2. 경로 결정 메커니즘 (코드 근거) - -### 2-1. DI/DO 매핑 로드 — `AuroraDevices/AuroraIO/IODevice.cs` `MappingDIO()` -```csharp -var m_connConfig = _connConfig as IOUnitConnConfig; -switch (type) { - case InfoIO.IoType.DI: fileName = m_connConfig.IOInterfaceMapDI; break; // ← 설정값(절대경로) - case InfoIO.IoType.DO: fileName = m_connConfig.IOInterfaceMapDO; break; -} -FileInfo fi = new FileInfo(fileName); -if (!fi.Directory.Exists) fi.Directory.Create(); -if (File.Exists(fileName) == false) { - Log.I($"파일({fileName})이 존재하지 않습니다. Device Config의 File 경로를 확인하세요."); - return 0; // ← 없으면 로그만, 크래시 없음(빈 리스트) -} -StreamReader sr = new StreamReader(fileName, ...); // ← 절대경로 그대로 읽음 -... ListDI/ListDO 채움 -``` -→ **`fileName`은 `IOInterfaceMapDI` 값 그 자체.** 코드가 폴더명을 만들거나 바꾸지 않음. - -### 2-2. 설정 출처 — `IO_Board.json` (`_config_device`) -`Doc/ILSOSConfig/ILSOS_PrismaticScanner/_config_device/IO_Board.json`: -```json -"IOInterfaceMapDI": "D:\\ILSOSConfig\\ILSOS_PrismaticScanner\\_device\\_io\\DI.csv", -"IOInterfaceMapDO": "D:\\ILSOSConfig\\ILSOS_PrismaticScanner\\_device\\_io\\DO.csv", -``` -→ **여기 절대경로가 `ILSOS_PrismaticScanner`로 박혀 있어서**, 어떤 config 폴더를 로드하든 이 값이면 Scanner DI를 읽었던 것. - -### 2-3. 화면 — `UCIOInterfaceViewModel` -ViewModel은 위에서 로드된 `_iOUnit.ListDI`를 표시할 뿐, 경로를 직접 다루지 않음. → 화면 쪽도 수정 불필요. - -### 2-4. 앱이 어느 config 폴더를 로드하는가 (DataPath) -- `D:\ILSOSConfig\<폴더>` 의 `<폴더>` 는 Aurora 프레임워크(`ApplicationExt.DataPath`, External\Aurora 바이너리)가 결정. **repo 소스에 없음.** -- 관례상 머신 플러그인 **AssemblyName** 과 일치(과거 `ILSOS_PrismaticScanner`). 이번 마이그레이션으로 **AssemblyName = `ILSOS_PrismaticRD_PTOP`** 이 되었으므로, DataPath도 `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP` 로 해석될 가능성이 큼 → **그 폴더가 준비돼 있어야 함**(없으면 §2-1처럼 "파일 없음" 로그 후 빈 표시). - ---- - -## 3. 수정 위치 (= 설정/환경, 소스 아님) - -### (필수) IO_Board.json 경로 값 — `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\_config_device\IO_Board.json` -```diff -- "IOInterfaceMapDI": "D:\\ILSOSConfig\\ILSOS_PrismaticScanner\\_device\\_io\\DI.csv", -- "IOInterfaceMapDO": "D:\\ILSOSConfig\\ILSOS_PrismaticScanner\\_device\\_io\\DO.csv", -+ "IOInterfaceMapDI": "D:\\ILSOSConfig\\ILSOS_PrismaticRD_PTOP\\_device\\_io\\DI.csv", -+ "IOInterfaceMapDO": "D:\\ILSOSConfig\\ILSOS_PrismaticRD_PTOP\\_device\\_io\\DO.csv", -``` -### (필수) 데이터 파일 존재 -`D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\_device\_io\DI.csv`, `DO.csv` 가 실제로 있어야 함(Scanner 폴더에서 복사 또는 260618 zip 사용). - -### (확인) DataPath -앱 실행 시 로드되는 config 루트가 `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP` 인지 확인(로그의 파일 경로/“파일 없음” 메시지로 검증). - -> **즉 IO DI에 대한 C# 소스 diff 는 없음.** §2-1 코드가 path-agnostic 이라 고칠 게 없다(= "문제 없는 이유"). - ---- - -## 4. "문제 없는 이유" 정리 -- `MappingDIO`는 `IOInterfaceMapDI` 절대경로를 **그대로** `StreamReader`로 연다. 경로 조립/머신명 치환 로직이 없음 → **코드가 머신에 종속되지 않음.** -- 파일이 없으면 예외 없이 로그 후 빈 리스트(`return 0`) → IOInterfaceView가 비어 보일 뿐 크래시 없음. -- 그래서 "PTOP를 바라보게" 하는 건 코드 수정이 아니라 **(1) PTOP config 폴더 준비 + (2) 그 안 IO_Board.json 경로를 PTOP로 + (3) DI/DO.csv 존재**로 끝난다. - ---- - -## 5. (참고) 별개 이슈 — 소스에 하드코딩된 `ILSOS_PrismaticScanner` 절대경로 -IO DI와 무관하지만, **PTOP config로 완전 전환** 시 같이 손봐야 할 하드코딩 경로(공유 소스, WeldingCommon/LASA — 수정 시 승인 필요): -| 파일 | 용도 | -|---|---| -| `WeldingCommon/Settings/SystemParameter.cs:17,18` | FieldScanImage, WORK_RECIPE_FILE | -| `WeldingCommon/RMS/EDC.cs:171,177` | cellTracker CSV | -| `LASA/GUI/DB/RecipeFieldScan.cs:24` | 기본 Scout 이미지 | -| `LASA/GUI/Page/MainPage.cs:259,533` | 기본 FieldScan 이미지 | -| `WeldingCommon/Views/Manual/Scanner/WDSelectScoutFieldViewModel.cs:47` | 기본 FieldScan 이미지 | -> 이들은 모두 `D:\ILSOSConfig\ILSOS_PrismaticScanner\...` 로 박혀 있음. **IOInterfaceView DI 표시에는 영향 없음**(vision/recipe/cellTracker 기본값). 필요 시 `ILSOS_PrismaticRD_PTOP`로 일괄 치환하는 별도 작업으로 진행. - ---- - -## 6. 권장 조치 (작업환경) -1. `D:\ILSOSConfig\ILSOS_PrismaticRD_PTOP\` 준비(없으면 Scanner 복사 또는 260618 zip 해제). -2. `…\_config_device\IO_Board.json` 의 DI/DO 경로를 §3 diff 대로 PTOP로 변경. -3. `…\_device\_io\DI.csv`, `DO.csv` 존재 확인. -4. 앱 실행 → IOInterfaceView에 PTOP DI 표시 확인. (안 보이면 로그의 파일 경로로 DataPath/파일유무 점검) diff --git a/docs/분석/2026/06/19/110333_alarm_subsystem_analysis.md b/docs/분석/2026/06/19/110333_alarm_subsystem_analysis.md deleted file mode 100644 index 8800e3f..0000000 --- a/docs/분석/2026/06/19/110333_alarm_subsystem_analysis.md +++ /dev/null @@ -1,111 +0,0 @@ -# ILSOS PTOP 알람부(AlarmManager) 종합 분석 — 2026-06-19 - -> 계기: 현장 PLC 미연결 상태에서 `SeqComponentTeachingRun` 시작 시 H(중)알람 인터락(202003)으로 차단됨. -> 본 문서는 알람부 전체 구조·동작·시퀀스 참조·리스크·개선안을 정리한다. - ---- - -## 1. 클래스 구조 - -| 클래스 | 위치 | 역할 | -|--------|------|------| -| `AlarmManager` (base) | `WeldingCommon/Alarm/AlarmManager.cs` | 정적 싱글톤(`_Instance`). 알람 set/조회/시퀀스 정지 | -| `AlarmManager_PTOP` | `PTOP/Alarm/AlarmManager_PTOP.cs` | `StopSequenceByAlarm` — H알람 시 AutoRun 정지 | -| `AlarmCode` / `AlarmCode_PTOP` | `.../Alarm/AlarmCode*.cs` | 알람 코드 상수. PTOP는 base 상속 후 21xxxx 추가 | -| `IAlarmService` (Aurora 프레임워크) | External/Aurora | **활성 알람 목록·설명·레벨(H/L)** 보유 | - -- 알람 **레벨(H/L)** 은 소스 상수가 아니라 **Aurora 런타임 알람 테이블**에 정의됨(코드에 `AlarmLevel.H` 할당부 없음). - -## 2. 핵심 메서드 동작 (주의점) - -- **`Alarm(code)`** (79줄): 이미 활성이면 재set 안 함 → **set-once(중복 없음)**. -- **`HasAlarmLevelHigh()`** (172줄): 활성 알람 중 H 하나라도 있으면 true. **⚠️ Simulation이면 무조건 false**(시뮬은 인터락 우회). -- **`IsAlarmLevelHigh(code)`** (194줄): 설명 모르는 코드(`desc==null`)는 **위험으로 보고 H 취급** → 미정의 코드도 인터락 유발. -- **Clear**: 개별 `ClearAlarm()`은 **빈 함수**(주석만). 실제 해제는 **`ClearAllAlarm()`(전체 일괄)** 뿐. - - 트리거: `SYS_ALARM_RESET_ALL_ALARM` / `EVENT_RESET_ALL_ALARMS_REQUEST` → `_AlarmService.ClearAllAlarm()`. -- **PLC 알람워드 직접쓰기**(`WriteBit(HeavyAlarmStatusAddr..)`)는 AlarmManager 내에서 전부 **주석 처리**됨(대신 SeqStatusMonitor가 수행). - -## 3. 알람 생성·소멸·전파 흐름 - -``` -[생성] SeqAlarmMonitor (상시 주기 감시) - ├ PLC: EMO / Heavy / Door / 통신끊김 - ├ Laser/Scanner/LBAD/OCT/Chiller: Deactivated/Disconnect/Error - └ IO: Leak / null / deactivated - → AlarmManager.Alarm(code) ← set-only, ❌조건 해소 시 Clear 안 함(LATCH) - -[전파] EquipmentSetup.OnSetAlarm(code, isHeavy) (EquipmentSetup.cs:82) - → isHeavy 면 AlarmManager_PTOP.StopSequenceByAlarm(code) → H면 StopSequence(SeqAutoRun) 비상정지 - -[PLC보고] SeqStatusMonitor.UpdateStatus (SeqStatusMonitor.cs:86) - → PC_HEAVY_ALARM_STATUS = HasAlarmLevelHigh(), PC_LIGHT_ALARM_STATUS = HasAlarmLevelLow() - → PLC_ALARM_RESET 입력 시 EVENT_RESET_ALL_ALARMS_REQUEST 발행 → ClearAllAlarm() - -[차단] 각 시퀀스 시작 시 CheckCondition_Interlock() → HasAlarmLevelHigh() H면 시작 거부 -``` - -## 4. 주요 시퀀스별 알람 참조 - -| 시퀀스 | 인터락 체크 | 비고 | -|--------|------------|------| -| **SeqComponentTeachingRun** | `HasAlarmLevelHigh()` → "Clear Heavy Alarms" + 202003 (Door/EMO 주석) | **이번에 막힌 시퀀스** | -| SeqTeachingRun (Common) | `CheckCondition_Interlock()` = **빈 체크(true)** | 알람 체크 없음 | -| **SeqAutoRunPTOP** | `CheckCondition_Interlock()` = **빈 체크(true)** | 차단은 시작이 아니라 OnSetAlarm→StopSequence | -| SeqAutoRunPrismatic / ComponentWeld | 동일 패턴(빈 체크) | | -| ScoutNormal/FlyingVision, UploadWeldingList, ReceiveCellInfo, ReportCellResult, MarkGrid, ScannerCalibration | 대부분 빈 체크 | 공통 시퀀스 | -| **SeqStatusMonitor** | 알람을 **읽어 PLC 출력** + Reset 처리 | 차단 아님 | -| **SeqAlarmMonitor** | 알람을 **생성(set-only)** | latch 원인 | - -> 실제 알람으로 "시작 차단"하는 곳은 `SeqComponentTeachingRun`이 거의 유일. AutoRun 계열은 "운전 중 H알람 발생 시 비상정지" 방식. - -## 5. 알람 코드 표 (추정 레벨 — 권위값은 런타임 테이블) - -| 코드 | 이름 | 용도 | 추정 | -|------|------|------|------| -| 200xxx~203xxx | ALARM_SEQUENCE_* | 시퀀스 결과/초기조건 실패 | 혼합 | -| 203004 | AutoRun_HeavyAlarmOccured | 자동운전 중 중알람 | H | -| 210001/2/3 | PLC Deactivated/Disconnect/**EMO** | PLC 상태/비상정지 | H | -| 210004/5/6 | PLC Light / **Heavy** / Door Interlock | PLC 경/중/도어 | 210005·6 H, 210004 L | -| 210101/2/3 | Scanner Deactivated/Disconnect/Error | 스캐너 | H/혼합 | -| 210201~205 | Laser Deactivated/Disconnect/Error/Warning/**EMO** | 레이저 | 205·201 H, 204 L | -| 210206/7 | Laser Warning/Error MSG | 레이저 메시지(동적) | L/H | -| 210301~303 | LBAD Deactivated/Disconnect/Error | LBAD | H/혼합 | -| 210401~403 | OCT Deactivated/Disconnect/Error | OCT | H/혼합 | -| 210501~503 | Chiller Deactivated/Disconnect/Error | 냉각기 | H/혼합 | -| 210600~604 | IO Null/Deactivated/Disconnect/**Leak**(Scanner/Chiller) | IO보드/누액 | H | -| 210605~614 | IO MC/BoxTemp/ChillerLeak/Fan1~6 | (현재 대부분 주석) | 혼합 | -| 210700~705 | RMS_* 타임아웃/설정 | 레시피 관리 | L 추정 | - -## 6. 리스크 / 개선 포인트 - -1. **알람 Latch (최우선)** — `SeqAlarmMonitor`가 set만 하고 조건 정상화 시 `ClearAlarm` 안 함. → **이번 PLC 미연결 사례**가 정확히 이것: 연결 복구돼도 알람이 남아 차단, 수동 ClearAll 필요. -2. **개별 Clear 부재** — `ClearAlarm()`이 빈 함수 → 전체 일괄 해제만 가능, 특정 알람만 해제 불가. -3. **Unknown=H 취급** — 미정의 코드 활성 시 무조건 H 차단 → 알람 테이블 누락 시 의도치 않은 인터락. -4. **시뮬 우회** — `HasAlarmLevelHigh()`가 Simulation이면 false → 시뮬/현장 동작 차이. -5. **인터락 일관성 부재** — TeachingRun만 알람 차단, 나머지 시작 시퀀스는 빈 체크. 안전 정책 제각각. -6. **Door/EMO 주석** — SeqComponentTeachingRun·UCMainAutoRunViewModel의 Door/EMO 인터락 주석 → 현재 중알람 1종만 차단. - -## 7. 개선 제안 — SeqAlarmMonitor self-clearing - -각 검사 항목을 "조건 나쁨 → Alarm, 조건 좋음 → Clear" 대칭 구조로. 예(개념): - -```csharp -// AS-IS -if (!_Laser.IsActivated) AlarmManager.Alarm(ALARM_DEVICE_LASER_DEACTIVATED); -else { /* clear 없음 → latch */ } - -// TO-BE (self-clearing) — AlarmManager에 개별 ClearAlarm(code) 구현 전제 -if (!_Laser.IsActivated) AlarmManager.Alarm(ALARM_DEVICE_LASER_DEACTIVATED); -else AlarmManager.ClearAlarm(ALARM_DEVICE_LASER_DEACTIVATED); -``` - -- 전제: `AlarmManager.ClearAlarm(int code)` 실구현 필요(현재 빈 함수). `_AlarmService`에 단건 해제 API 확인 후 연결. -- 효과: PLC 재연결 등 조건 복구 시 알람 자동 해제 → 수동 ClearAll 불필요, 인터락 latch 해소. -- 범위: AlarmManager(공통) 수정 → **승인 필요 영역**. 적용 전 별도 협의. - -## 8. 부록 — 이번(PLC 미연결) 사례 요약 - -- PLC 미연결 → `SeqAlarmMonitor`가 PLC Deactivated/Disconnect(H) set → latch. -- `SeqComponentTeachingRun` 시작 → `HasAlarmLevelHigh()` true → 202003, FORCE_ERROR. -- 임시조치: `SeqComponentTeachingRun.CheckCondition_Interlock()`의 H알람 블록 주석(테스트용, **원복 예정**) → 레이저 출사 확인. -- 관련: [[axis-position-interlock-todo]] diff --git a/docs/분석/2026/06/19/111326_mxcomponent_plc_connection_analysis.md b/docs/분석/2026/06/19/111326_mxcomponent_plc_connection_analysis.md deleted file mode 100644 index d6de06e..0000000 --- a/docs/분석/2026/06/19/111326_mxcomponent_plc_connection_analysis.md +++ /dev/null @@ -1,82 +0,0 @@ -# MX Component PLC 연결 설정 분석 — 2026-06-19 - -> 계기: 현장 PLC 미연결 → 중알람 latch → TeachingRun 인터락 차단. -> PLC를 정상 연결하려면 무엇을 PLC측에 요청하고, PC 소스/설정의 어떤 값을 맞춰야 하는지 분석(수정 없음). -> 관련: [[110333_alarm_subsystem_analysis]] - ---- - -## 1. 연결 메커니즘 (소스 기준) - -- 드라이버: `AuroraDevices/AuroraMXComponent/MXComponent.cs` — Mitsubishi **MX Component** .NET 컨트롤 `ActUtlType64Class` 사용. -- PTOP 등록(`EquipmentSetup.cs:158`): - `new PLCControllerPTOP(PLCDevice, ePLCNetType.MXComponent, IsSimulation, PLCMMapFileType.CSV, AuroraEnv)` -- 연결 흐름: - ``` - PLCBase.DoActivate (PLCBase.cs:80) - → NetworkType == MXComponent - → PLCNet.SetInitParams(connConfig.StationNumber) // = ActLogicalStationNumber - → Open() → _actUtlType.ActLogicalStationNumber = N; _actUtlType.Open() - ``` -- **핵심 파라미터 = `StationNumber` (논리국번 / Logical Station Number)**. - `MXComponent.SetInitParams(int logicalStationNo)` → `_actUtlType.ActLogicalStationNumber = logicalStationNo` (MXComponent.cs:26~29). -- Open 결과: `0x00000000` 성공, 그 외 Hex 에러. `0x0180840B`(타임아웃) 시 1회 재시도(MXComponent.cs:48). - -## 2. 맞춰야 하는 값 — PC측 - -### (A) MX Component "Communication Setup Utility" (별도 PC 도구) -- MX Component 설치 시 함께 깔리는 **통신 설정 유틸리티**에서 **Logical Station Number** 를 등록. -- 등록 시 입력: PLC **CPU 기종**, **접속 경로**(CPU 내장 이더넷 / 이더넷 모듈 / CC-Link IE / USB / 시리얼), **PLC IP/포트**, 네트워크/국번. -- 이 유틸리티에 등록된 논리국번이 실제 통신 경로를 정의함. (소스는 "번호"만 들고, 실 경로는 유틸리티가 보유) - -### (B) 소스/설정 파일 — `StationNumber` 일치 -- 정의: `AuroraDevices/AuroraPLC/PLCBase.Config.cs:37` - `PLCMXComponentConnConfig.StationNumber` 기본값 **1** (주석: "Logical station No. (By PC Tool: Communication Setting Utility)"). -- 실제 값은 **디바이스 config 파일에 저장**: `_config_device/PLC.json` - ```json - { "ConnConfig": { "StationNumber": 22, "UpdateRateMS": 50 } } - ``` - (위는 Gun 예시 = 22. PTOP는 현장 값으로 별도 설정 필요) -- **편집 경로**: 프로그램 내 PLC 디바이스 설정 화면(`UCPLCDevice`)에서도 수정 가능, 또는 위 JSON 직접 편집. -- ⚠️ **(A)의 유틸리티 논리국번 == (B)의 PLC.json StationNumber** 이어야 연결됨. (현장 PLC.json은 repo에 없음 = 현장 전용) - -## 3. PLC측에 요청해야 할 것 - -| 항목 | 내용 | -|------|------| -| CPU 기종 | Q / L / iQ-R / iQ-F 등 정확한 모델 | -| 접속 방식 | CPU 내장 이더넷 / 이더넷 모듈 / CC-Link IE / USB / 시리얼 중 무엇 | -| 네트워크 정보 | PLC **IP 주소 / 포트**, 서브넷, (CC-Link IE면) 네트워크No·국번 | -| MELSOFT 접속 허용 | 이더넷 "MELSOFT 접속" 오픈 설정, 포트 개방(보통 5007/5006 등), 방화벽 | -| 국번 구성 | 멀티 CPU/증설 시 대상 station | - -→ 위 정보를 받아 **(A) Communication Setup Utility에 논리국번으로 등록**하고, 그 번호를 **(B) PLC.json StationNumber** 에 동일하게 넣으면 연결. - -## 4. 메모리맵 CSV (연결과 별개, 데이터 정합용) - -- `PLCMMapFileType.CSV` → `PLCBase.MemMapParser.cs`가 아래 8종 CSV를 읽어 디바이스 주소 매핑: - - `External/PLC/PLC_DeviceMap_Bit_Input_B_Device.csv` / `..._Output_B_Device` - - `..._Bit_Input_M_Device` / `..._Output_M_Device` - - `..._Word_Input_W_Device` / `..._Output_W_Device` - - `..._Word_Input_D_Device` / `..._Output_D_Device` - - 경로: recipePath 기준 `External\PLC\...` (현장 config 폴더). -- 연결(Open)과는 무관하지만, 연결 후 **읽고/쓰는 주소가 PLC 실제 디바이스와 맞아야** 알람/인터페이스가 정상. PTOP는 repo에 맵 CSV 없음(현장 전용) → [[plc-devicemap-260617-pending]] 참고. - -## 5. 점검 순서 (현장) - -1. PLC측에서 **CPU기종·접속방식·IP/포트·MELSOFT 접속 허용** 확보 -2. PC에서 **MX Component Communication Setup Utility** 실행 → 위 정보로 **논리국번**(예: 1) 등록 → 유틸리티 내 "통신 테스트" 통과 확인 -3. `_config_device/PLC.json` 의 `StationNumber` 를 그 논리국번과 동일하게 설정 (또는 PLC 디바이스 설정 화면) -4. 프로그램 재기동 → PLC Activate → 로그 `PLC Open success` 확인 (`Open: 0x00000000`) -5. 연결되면 SeqAlarmMonitor의 PLC Deactivated/Disconnect 알람 조건 해소 → (단, latch라 **ClearAll 1회** 필요할 수 있음, [[110333_alarm_subsystem_analysis]] 참고) - -## 6. 참고 — 에러코드 / 매뉴얼 - -- Open/Close 반환값은 MX Component 표준 Hex 에러코드. `0x0180840B` = 타임아웃(소스에서 1회 재시도). -- 상세 절차·에러표: `Doc/메뉴얼/공통/PLC/21. MX_Component_V4_프로그래밍_매뉴얼.pdf` (Communication Setup Utility / ActUtlType / 에러코드 장 참조). - -## 7. 요약 - -- **연결 핵심 = 논리국번 1개**. PC의 MX Component 유틸리티 등록값 ↔ 소스 `PLC.json StationNumber` 일치 + PLC측 이더넷/접속 허용. -- PLC.json·맵 CSV는 **현장 전용**(repo 없음) → 현장에서 값 세팅 필요. -- 연결되면 PLC 미연결 중알람 해소 → TeachingRun 인터락 정상화 (latch면 ClearAll 1회). diff --git a/docs/분석/2026/06/19/122201_ajinio_readdobit_analysis.md b/docs/분석/2026/06/19/122201_ajinio_readdobit_analysis.md deleted file mode 100644 index 5b9dfc3..0000000 --- a/docs/분석/2026/06/19/122201_ajinio_readdobit_analysis.md +++ /dev/null @@ -1,68 +0,0 @@ -# AjinIO.ReadDoBit(int) 미구현 분석 — 2026-06-19 - -> 계기: `AjinIO.cs`의 `ReadDoBit(int index)`(bool 반환)가 미구현 스텁(`return true`). -> DI 패턴 대비 분석 후, 현 상태는 **스텁 유지 + 구현 주석 보존**으로 결정(현장 반영 예정). -> 관련: [[110333_alarm_subsystem_analysis]] - ---- - -## 1. 현황 - -- 파일: `AuroraDevices/AuroraIO/Vendor/AJINEXTEK/AjinIO.cs` (보호 영역, 신중 수정) -- DI/DO 오버로드 4종(`IOUnit` 추상): - - `int ReadDoBit(int, ref bool)` (469줄) — `CAXD.AxdoReadOutport` 사용, **구현됨** - - `int ReadDiBit(int, ref bool)` (483줄) — `CAXD.AxdiReadInport`, 구현됨 - - `bool ReadDoBit(int)` (496줄) — **미구현 스텁(`return true`)** ← 대상 - - `bool ReadDiBit(int)` (501줄) — `CAXD.AxdiReadInport`, 구현됨(실패 시 throw) -- 즉 **DO 읽기 API(`AxdoReadOutport`)는 같은 파일에서 이미 검증**됨(469줄·`GetDoArray` 399줄). bool 반환 오버로드만 DO쪽이 비어 있었음. - -## 2. 구현안 (DI 패턴 그대로) - -```csharp -public override bool ReadDoBit(int index) -{ - uint data = 0; - int err = (int)CAXD.AxdoReadOutport(index, ref data); - if (err != (int)AXT_FUNC_RESULT.AXT_RT_SUCCESS) - throw new Exception(string.Format("ERR_IO_FAIL_TO_GET_BIT;ERROR CODE:{0}", err)); - return Convert.ToBoolean(data); -} -``` -- `ReadDiBit(int)`(501줄)와 완전 동일 구조, API만 `AxdiReadInport → AxdoReadOutport`. - -## 3. 스텁 의존 호출처 분석 (영향 범위) - -`bool ReadDoBit(int)` 호출처 전수: - -| 호출처 | 위치 | 용도 | -|--------|------|------| -| `UCInformationPanelViewModel.cs` | 46, 53, 82, 115, 132 (5곳) | ModeKeySolOn / SafetyDoorRelease **상태 표시** | - -- 5곳 모두 **`ePDOAddress`(PAIX 주소)** 사용 → 실제 **PAIX 보드에서 실행**. -- **PAIX의 `ReadDoBit(int)`는 정상 구현**(PAIXIO.cs:288, `GetDoArray`로 읽음) → 이 호출처는 이미 정상 동작. -- `(int, ref bool)` 오버로드 호출(`UCTestDeviceViewModel.cs:264`)은 Ajin도 구현돼 있어 무관. -- **결론: Ajin 스텁(`return true`)에 실제 의존하는 코드는 사실상 없음.** 영향은 "Ajin 보드 장비에서 위 5개 호출이 도는 경우"로 한정되며, 그 경우 오히려 항상 true가 오표시였음(상태 표시용, 안전제어 아님 → 위험 낮음). - -## 4. 현재 결정 상태 (스텁 유지 + 구현 주석 보존) - -```csharp -public override bool ReadDoBit(int index) -{ - //현재 사용하지 않아서 추후 구현 - return true; - - // [추후 구현] return true 스텁 의존 코드 확인 후 아래로 교체 (ReadDiBit(int) 패턴) - //uint data = 0; - //int err = (int)CAXD.AxdoReadOutport(index, ref data); - //if (err != (int)AXT_FUNC_RESULT.AXT_RT_SUCCESS) - // throw new Exception(...); - //return Convert.ToBoolean(data); -} -``` -- 동작 변화 없음(`return true` 유지) → 현장 안전. 구현은 주석으로 보존해 추후 활성화 용이. -- 빌드 산출물: **AuroraIO.dll**. - -## 5. 후속 (활성화 조건) - -- Ajin 보드 장비에서 DO 상태 표시 정확성이 필요해질 때, 주석 해제 + 위 5개 호출처 회귀 확인. -- 활성화 시 실패 처리(throw) 영향 검토: `ReadDiBit(int)`와 동일 정책이라 일관성은 확보됨. diff --git a/docs/분석/2026/06/19/124752_guidebeam_trupulse_analysis.md b/docs/분석/2026/06/19/124752_guidebeam_trupulse_analysis.md deleted file mode 100644 index 8e01d2d..0000000 --- a/docs/분석/2026/06/19/124752_guidebeam_trupulse_analysis.md +++ /dev/null @@ -1,59 +0,0 @@ -# 가이드빔(Guide Beam) 제어 경로 분석 — TruPulse — 2026-06-19 - -> 계기: 레시피 Guide Beam 버튼/상태가 현장에서 동작하지 않음. IO(DO)→Laser 순으로 추적. -> 결론: 가이드빔은 **Laser(TruPulse) 디바이스 기능**이며, 제어 콜백 **`ExtPilotOnOff`가 미구현(미할당)**. -> 관련: [[110333_alarm_subsystem_analysis]] - ---- - -## 1. 잘못된 경로였던 IO(DO 17) 시도 요약 - -- 레시피 ViewModel(`UCScannerObjectTeachingControlBoxViewModel.cs`)에서 `_IODevice`(메인 IO_Board=PAIX_PTOP)의 - **DO 17(EX_Red_Control)** 을 read/write 하도록 수정 → 상태 표시(읽기)는 동작 확인. -- 그러나 **물리적 가이드빔(적색광)은 출력 안 됨**. -- 현장 확인: 번들(DBW-MC Laser SW)의 `RedLight Opened/Closed` 이벤트는 **번들의 RedClosed 버튼 수동 조작** 결과로, - 우리 DO 17과 무관. `Mod1_IO:000100↔000000` 비트만 DO 17과 일치. -- → **DO 17(IO) 경로는 가이드빔 제어가 아님.** (메인 IO_Board와 레이저 가이드빔 입력은 별개) -- 현 상태: 원복하지 않고 **주석 토글로 필요시 전환**(도연님 운용). 기존 `_Laser` 코드는 주석 보존됨. - -## 2. 실제 경로 = Laser(TruPulse) 디바이스 - -레이저 타입 = **TruPulse** (`eLaserType.TruPulse`, `Laser.cs:70`). - -``` -_Laser.PilotLaserOn() (베이스 Laser, Laser.cs) - └ IsSimul 이면 return E1.OK (no-op) ← 현재 시뮬이라 여기서 멈춤 - └ 실모드: TruPulse.PilotLaserOn() (TruPulse.cs:817) - └ return PilotOnOffFunc(true) - └ (Laser.cs:74) core.PilotOnOffFunc = (on) => ExtPilotOnOff(on) - └ ExtPilotOnOff(true) ← ★ 레이저 IO 보드 write 의도 콜백 -``` - -상태 읽기: -``` -GetPilotLaserStatus() → PilotLaserEnable ← (TruPulse.cs:174) Di2 & (1<<9) - (QueryStatusHwInterface 로 TruPulse 컨트롤러에서 직접 읽음) -``` - -## 3. ★ 핵심 문제: `ExtPilotOnOff` 미구현(미할당) - -- `Laser.cs` 27~29줄에 `ExtLaserEnable` / `ExtLaserDisable` / `ExtPilotOnOff` **선언만** 존재. -- 전체 소스에 **`ExtPilotOnOff = ...` 할당부 없음**(검색 0건). `updateCore` 구독(`UCTruPulseManualViewModel`)도 core 참조만 받고 콜백 미주입. -- 결과: - - **시뮬(현재)**: 베이스에서 no-op → 빔 안 나옴, 상태 항상 true. (정상) - - **실모드**: `ExtPilotOnOff(true)` 호출 시 **null → NullReferenceException** 위험. -- → "레이저 IO 보드로 가이드빔 제어"는 **설계 의도이나 구현 미완(TODO)**. - -## 4. 결론 / TODO - -| 구간 | 상태 | -|------|------| -| 가이드빔 제어 의도 | TruPulse → `ExtPilotOnOff` → 레이저 IO 보드 DO | -| 실제 구현 | ❌ 미구현 (`ExtPilotOnOff` 미할당) | -| 현재 운영 | 시뮬 → no-op (물리 빔 안 나오는 게 정상) | -| 레시피 DO 17(`_IODevice`) 변경 | 잘못된 경로(메인 IO_Board), 원복 안 함 — 주석 토글로 운용 | - -추후 실제 가이드빔 구현 시: -- `_Laser.ExtPilotOnOff = (on) => { 레이저IO보드._iOUnit.WriteDoBit(<가이드빔 DO>, on); return 0; };` 구현 -- "레이저 IO 보드" 디바이스/DO 주소 확정 (메인 PAIX와 별개 여부) -- Laser 디바이스 **비시뮬 + Activated**