# NRE / 크래시 위험 구간 점검 - 작성 : 2026-09-07 - 계기 : 출사 미지원 객체(Ellipse/Rectangle/Roi) NRE 방어(`ISSUE-260907-01`) 적용 후, 같은 유형이 더 있는지 전수 확인 요청. - 성격 : **점검 목록 전용. 소스 미수정. 현장 검증 미실시.** - 표기 : 🟩 소스 확인 / 🟦 현장 기록 / 🟨 미확보·판정 불가 ## 점검 범위와 한계 | 구분 | 내용 | |---|---| | 포함 | `Machines/ILSOS_PrismaticRD_PTOP/**`, `WeldingComponents/WeldingCommon/Sequences/**` | | 제외 | AuroraVision, LASA GUI(Canvas·DrawTools), AuroraDevices 하위 대부분, Aurora 프레임워크 | | 방법 | 패턴 검색(`FirstOrDefault`/`First()`/`[0]`/`as`/인덱서) 후 해당 라인 전후 확인 | | 한계 | 정적 분석. 런타임 재현·디버깅 없음. 제외 범위에 같은 유형이 더 있을 수 있음 | --- ## 0. 선행 확인 — 예외가 실제로 프로그램을 종료시키는가 🟩 우리 소스에 **전역 예외 핸들러가 없습니다.** `DispatcherUnhandledException` / `AppDomain.CurrentDomain.UnhandledException` / `UnobservedTaskException` / `SetUnhandledExceptionMode` 검색 결과 **0건**. 🟩 `ILSOS_PrismaticRD_PTOP.csproj` → `OutputType=library`. 즉 실행 주체는 **Aurora.exe** 이고 저장소에 소스가 없습니다. 🟨 따라서 아래 항목이 **실제로 프로세스를 종료시키는지는 판정할 수 없습니다.** - Aurora.exe 가 전역 핸들러를 걸어 두었는지 미확인 - Aurora `SequenceBuilder` 가 노드 실행을 try/catch 로 감싸는지 미확인 (`sb.OnForceError(...)` 훅이 존재하므로 **잡을 가능성은 있음**) > **이 문서는 "NRE 가 발생할 수 있는 지점" 목록입니다.** > "여기서 프로그램이 죽는다" 로 읽지 마십시오. 종료 여부는 별도 확인 필요. --- ## A. 널 체크 없이 즉시 역참조 — 위험도 최상 ### A-1. `SeqComponentTeachingRun.cs:456-459` 🟩 ```csharp var power = Status.GetProcessingWeldingData().UploadProcessDatas .Where(data => data.DataName == $"Power_{SlotNo}_{_SequenceSetting.LastPageNo}") .FirstOrDefault(); power.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower1 + ScannerCanvasUserCtrl.ScannerParams[paramIndex].LaserPower2; // ← power == null 이면 NRE var velocity = ...Where(data => data.DataName == $"Velocity_{SlotNo}_{...}").FirstOrDefault(); velocity.Value = ScannerCanvasUserCtrl.ScannerParams[paramIndex].MarkSpeed; // ← 동일 ``` - `DataName` 을 `SlotNo` + `LastPageNo` 로 **문자열 조합**해 찾습니다. - 그 조합이 `UploadProcessDatas` 에 없으면 `FirstOrDefault()` 가 `null` 을 반환합니다. - **페이지 수가 늘거나 슬롯 번호 체계가 바뀌면 즉시 발생**합니다. - 실제 운전 경로(Manual 티칭런)라 재현 가능성이 목록 중 가장 높습니다. ### A-2. `SeqEventQTest.cs:142-145` 🟩 — 3중 위험 ```csharp FovInfo _fovInfo = VisionRecipeManager.GetFovInfos().FirstOrDefault(); _PatternAgent = FovPatternAgentManager.GetAgent(_fovInfo.ToSubSysInfo()); // ① _fovInfo null → NRE List patterns = _PatternAgent.GetMainPatterns(_fovInfo); // ② agent null → NRE _PatternInfo = patterns[0]; // ③ 빈 리스트 → IndexOutOfRange ``` ### A-3. `SeqAutoRunPrismatic.cs:1101-1104` 🟩 A-2 와 동일 패턴 + `_Patterns[_InitParam.VisionRecipeNo]` 인덱스 무검증. > 🟩 **참고 — 올바른 패턴이 같은 파일에 이미 있습니다.** > `SeqAutoRunPrismatic.cs:743-745` 는 `_PatternResult?.Results?.FirstOrDefault()` 로 > 널 조건 연산자를 쓰고 이후 `if (baseResult != null)` 까지 확인합니다. > A-2 · A-3 만 이 방어가 빠져 있습니다. --- ## B. `as` 캐스팅 결과를 가드 없이 보관 — 지연 발화형 캐스팅 실패 또는 키 부재 시 조용히 `null` 이 되고, **한참 뒤 그 필드를 사용하는 시점**에 터집니다. 발생 위치와 원인 위치가 달라 추적이 어렵습니다. | 위치 | 대상 | 🟩 | |---|---|---| | `SeqAutoRunPTOP.cs:291` | `_SystemParameter` | 🟩 | | `Views/Main/UCMainAutoRunViewModel.cs:95-97` | `_SystemParameter`, `_Scanner`, `_Plc` | 🟩 | | `Views/Main/UCMainAutoRunViewModel.cs:472, 505` | `_SystemParameter` 재대입 | 🟩 | | `SeqTestRun.cs:472` | `_IOLaser` | 🟩 | | `SeqComponentTeachingRun.cs:179` | `_SequenceSetting` | 🟩 | | `Monitoring/SeqStatusMonitor.cs:60` | `_SequenceSetting` | 🟩 | | `SeqAutoRunPrismatic.cs:1050` | `SystemSetting` | 🟩 | | `EquipmentSetup.cs:90, 199, 240, 342` | `EquipmentInfo` | 🟩 | | `EquipmentSetup.cs:129, 133` | `IODevice`, `PLCControllerPTOP` | 🟩 | | `Recipes/RMSHandlerForPTOP.cs:52-53` | `_plc`, `_RmsSetting` | 🟩 | 대부분 **초기화 구간**이라 정상 구성에서는 성공합니다. 위험은 "설정 키 이름 변경 / 디바이스 등록 누락 / 타입 교체" 시 **즉시 실패하지 않는다**는 점입니다. 🟩 **예외 — 이미 방어된 곳** : `SeqAutoRunPTOP.cs:1140, 1252` 의 `LaserInterlockSetting` 은 `LaserAngleOffsetSelector.Select()` 안에서 `setting != null` 삼항 처리로 방어됩니다. --- ## C. 인덱스 무검증 — NRE 는 아니나 동일하게 종료 요인 | 위치 | 위험 | 🟩 | |---|---|---| | `SeqUploadWeldingList.cs:206` | `SelectedDrawObjectList[0]` — 빈 리스트 | 🟩 | | `SeqUploadWeldingList.cs:207-209` | `ScannerParams[ScannerParamID - 1]` — **ID 가 0 이면 인덱스 -1** | 🟩 | | `SeqUploadWeldingList.cs:474, 617-631, 719, 1006, 1052, 1152, 1204, 1404, 1605` | `ScannerParams[obj.ScannerParamNo]` 범위 무검증 (배열은 128개 고정 생성) | 🟩 | | `SeqWaitWeldOrChangePage.cs:194-195` | `ScannerParams[paramIndex]` | 🟩 | | `SeqTestRun.cs:237-238` | `ScannerParams[paramIndex]` | 🟩 | | `SeqComponentTeachingRun.cs:457-465` | `ScannerParams[paramIndex]` | 🟩 | | `SeqAutoRunPrismatic.cs:1104` | `_Patterns[VisionRecipeNo]` | 🟩 | **`SeqUploadWeldingList.cs:206-209` 주의** — 2026-09-07 적용한 예외처리는 `FuncLoadDrawObjectScannerParamSelected()` 쪽이라 **이 경로는 여전히 무방비**입니다. --- ## D. 예외는 안 나지만 값이 오염되는 것 (참고) double 나눗셈이라 예외 대신 `Infinity`/`NaN` 이 조용히 흘러갑니다. | 위치 | 내용 | |---|---| | `ScannerRTC6.cs:775` · `ScannerRTC5.cs:773` | `32.0 / periodHz` — `periodHz == 0` 이면 `Infinity` → `(uint)` 캐스팅은 정의되지 않은 동작 | | `SeqUploadWeldingList.cs:1313-1316` | `ArcRampingInterval == 0` 시 동일 | 상세 : [`260907_LaserFrequency_단위계통_분석.md`](260907_LaserFrequency_단위계통_분석.md) --- ## 우선순위 제안 | 순위 | 항목 | 근거 | |---|---|---| | 1 | **A-1** | 실제 운전 경로(Manual 티칭런) + 조합 문자열 탐색이라 재현 가능성 최상 | | 2 | **C 의 `SeqUploadWeldingList.cs:206-209`** | 이번 예외처리가 커버하지 못한 인접 경로 | | 3 | **A-2 / A-3** | 비전 경로. Vision 미사용이면 후순위 | | 4 | **B** | 개별 수정보다 초기화 구간 일괄 널 가드 + 알람이 적절 | | — | **선행** | Aurora.exe / SequenceBuilder 의 예외 처리 정책 확인. 이게 정해져야 "가드 후 어떻게 할지"(스킵 / 알람 / 시퀀스 중단)가 결정됨 | ## 조치 시 주의 - **조용히 스킵하면 안 되는 경우가 있습니다.** A-1 은 공정 데이터 상신 실패를 뜻하므로, 널이면 로그만 남기고 넘어가면 **데이터 누락이 은폐**됩니다. 알람 여부를 함께 정해야 합니다. - 반대로 이번 `ISSUE-260907-01` 처럼 **원래도 동작하지 않던 경로**는 스킵이 맞습니다. - 즉 "널이면 무조건 return" 을 일괄 적용하지 마십시오. 항목마다 판단이 다릅니다. --- ## 변경 이력 | 일자 | 내용 | 비고 | |---|---|---| | 2026-09-07 | 최초 작성. PTOP + WeldingCommon Sequences 범위 점검 | 소스 미수정 |