# 지그 압력 및 누출 검사 애플리케이션 (Jig Pressure & Leak Test App) ## 1. 프로젝트 개요 본 프로젝트는 특정 지그(Jig)에 제품을 고정하고 압력을 인가한 뒤, 유지 시간 동안 내부 압력의 변화를 측정하여 누출(Leak) 여부를 **자동으로 검사**하는 데스크톱 전용 모니터링 애플리케이션입니다. | 항목 | 내용 | |------|------| | **프레임워크** | C# WPF (.NET Framework 4.7.2) | | **아키텍처** | MVVM (Model-View-ViewModel) + Service Layer | | **솔루션 파일** | `jig_test.slnx` | | **NuGet 패키지** | `System.IO.Ports` (10.0.4), `NModbus` / `NModbus.Serial` (3.0.81) | ### 주요 기능 * **수동 검사**: 물리 스위치(i1, i2) 조작에 의해 자동으로 누출 검사 시퀀스가 트리거됨 * **자동 검사**: 버튼 한 번으로 인가 → 목표 압력 대기 → 고정 → 유지 → 배기까지 전 공정을 자동 수행 * **실시간 모니터링**: 100ms 폴링으로 I/O 상태 및 압력값을 실시간 표시 * **통신 분리 및 안정성**: `ConnectionManager`를 통한 통신 인프라 캡슐화 및 `bool` 기반 상태 관리로 안정성 확보 * **자가 복구**: 통신 끊김 감지 시 독립적 자동 재연결 * **설정 관리**: 통신 포트, 기기 ID, 검사 파라미터를 XML 파일(`config.xml`)로 저장/불러오기 --- ## 2. 폴더 구조 ``` jig_test/ ← 솔루션 루트 ├── jig_test.slnx ← 솔루션 파일 ├── README.md ← 본 문서 ├── KN-2000W_..._MANUAL_W.pdf ← 압력 센서 매뉴얼 ├── SemiIOLite_Manual.pdf ← I/O 보드 매뉴얼 │ └── jig_test/ ← 프로젝트 폴더 ├── jig_test.csproj ← 프로젝트 설정 (.NET 4.7.2, WPF) ├── App.xaml / App.xaml.cs ← 앱 진입점, 글로벌 예외 처리 │ ├── Models/ ← 데이터 모델 │ └── AppConfig.cs ← 모든 설정값 (포트, 속도, 검사파라미터) │ ├── Services/ ← 핵심 서비스 서비스 (Infrastructure 계층) │ ├── ConfigService.cs ← XML 직렬화 기반 설정 저장/로드 │ ├── ConnectionManager.cs ← ★ 통신 인프라 (포트 수명, 폴링, 재연결 관리) │ ├── SemiIOLiteController.cs ← I/O 보드 제어 (LS산전 ASCII 프로토콜) │ └── PressureSensorController.cs ← 압력 센서 통신 (Modbus RTU) │ ├── ViewModels/ ← MVVM ViewModel 계층 │ ├── Base/ │ │ ├── ObservableObject.cs ← INotifyPropertyChanged 구현 기반 클래스 │ │ └── RelayCommand.cs ← ICommand 구현 (View 이벤트→ViewModel 바인딩) │ ├── Converters/ │ │ └── InverseBooleanConverter.cs ← bool 반전 변환기 │ ├── MainViewModel.cs ← ★ 핵심 비즈니스 로직 (검사 시퀀스, UI 바인딩) │ ├── SettingsViewModel.cs ← 통신 설정 창 로직 │ └── ParameterViewModel.cs ← 검사 파라미터 창 로직 │ └── Views/ ← MVVM View 계층 (XAML UI) ├── MainWindow.xaml / .cs ← 메인 대시보드 UI ├── SettingsWindow.xaml / .cs ← 통신 설정 다이얼로그 └── ParameterWindow.xaml / .cs ← 검사 파라미터 다이얼로그 ``` --- ## 3. 아키텍처 (MVVM 패턴) ``` ┌──────────────────────────────────────────────────────────────┐ │ App.xaml.cs │ │ (글로벌 예외 처리: UI / AppDomain / Task) │ └──────────────────────┬───────────────────────────────────────┘ │ StartupUri ┌──────────────────────▼───────────────────────────────────────┐ │ VIEW (XAML) │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ MainWindow.xaml ← DataBinding → MainViewModel.cs │ │ │ │ SettingsWindow ← DataBinding → SettingsViewModel │ │ │ │ ParameterWindow ← DataBinding → ParameterViewModel │ │ │ └───────────────────┬─────────────────────────────────────┘ │ └──────────────────────┼───────────────────────────────────────┘ │ Event / Method ┌──────────────────────▼───────────────────────────────────────┐ │ INFRASTRUCTURE / SERVICES │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ ConnectionManager │ │ │ │ (Timer, SerialPort LifeCycle, Reconnect, Event Dispatch)│ │ │ └───────┬────────────────────────────┬────────────────────┘ │ │ │ Composition │ Composition │ │ ┌───────▼──────────┐ ┌───────▼──────────┐ │ │ │ SemiIOLite │ │ PressureSensor │ │ │ │ Controller │ │ Controller │ │ │ └───────┬──────────┘ └───────┬──────────┘ │ │ │ COM Port │ COM Port │ └──────────┼────────────────────────────┼──────────────────────┘ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ Semi IO Lite │ │ KN-2240W │ │ I/O Board │ │ Pressure │ │ (SIO-0201A) │ │ Sensor │ └──────────────┘ └──────────────┘ ``` ### 데이터 흐름 요약 1. **ConnectionManager → Service**: 100ms 타이머로 각 컨트롤러의 `RequestXxx` 메서드 호출 및 송신 HEX 로그 발행 2. **Service → Hardware**: 시리얼 포트를 통해 실제 물리 레이어로 데이터 송신 3. **Hardware → ConnectionManager**: `DataReceived` 이벤트 발생 시 바이트 버퍼링 및 패킷 완성 검사 4. **ConnectionManager → ViewModel**: 패킷이 완성되면 이벤트를 통해 ViewModel에 전달 (`IOPacketReceived` 등) 5. **ViewModel → View**: 수신 패킷을 파싱하여 `lock` 기반으로 압력값 갱신 및 UI 상태 프로퍼티 변경 → 바인딩 자동 반영 --- ## 4. 핵심 파일 상세 ### 4.1. `Models/AppConfig.cs` 모든 애플리케이션 설정을 담는 POCO 모델 클래스입니다. | 프로퍼티 | 타입 | 기본값 | 용도 | |----------|------|--------|------| | `PortName` | `string` | `"COM1"` | I/O 보드 COM 포트 | | `BaudRate` | `int` | `9600` | I/O 보드 통신 속도 | | `PressurePortName` | `string` | `"COM2"` | 압력 센서 COM 포트 | | `PressureBaudRate` | `int` | `9600` | 압력 센서 통신 속도 | | `IOEnabled` | `bool` | `true` | I/O 보드 사용 여부 | | `IOStationId` | `int` | `2` | I/O 보드 국번 ID | | `PressureEnabled` | `bool` | `true` | 압력 센서 사용 여부 | | `PressureSlaveId` | `int` | `1` | Modbus 슬레이브 주소 | | `HoldTime` | `int` | `30` | 누출 검사 유지 시간 (초) | | `AllowedErrorRange` | `double` | `0.3` | 허용 압력 오차 (bar) | | `AutoTestTargetPressure` | `double` | `4.0` | 자동 검사 목표 압력 (bar) | | `IsLogVisible` | `bool` | `false` | 하단 로그 창 표시 여부 | ### 4.2. `Services/ConfigService.cs` `AppConfig`를 XML 직렬화 방식으로 저장/로드합니다. * `Load()`: `config.xml` 파일이 없거나 손상 시 기본 `AppConfig` 반환 (안전한 폴백) * `Save()`: 실행 파일 위치의 `config.xml`에 현재 설정을 즉시 저장 ### 4.3. `Services/ConnectionManager.cs` — ★ 통신 인프라 관리자 `MainViewModel`에서 통신 관련 저수준 책임을 분리하여 캡슐화한 클래스입니다. | 기능 | 상세 내용 | |------|-----------| | **포트 관리** | IO 및 압력 포트의 생성, 오픈, 클로즈, Dispose 주기 관리 | | **폴링 루프** | 100ms `DispatcherTimer`를 구동하여 장비 상태 체크 명령 주기적 송신 | | **버퍼 관리** | `DataReceived` 이벤트로 들어오는 단편화된 바이트를 패킷 단위로 조립 | | **연결 상태** | 응답 타임아웃(1.5초) 감지 시 `bool` 기반 상태 갱신 및 이벤트 발행 | | **자가 복구** | 포트 닫힘이나 연속 실패(10회) 감지 시 3초 간격 자동 재연결 시도 | | **이벤트 발행** | 패킷 수신, 로그 발생, 연결 상태 변경 등을 이벤트를 통해 외부에 알림 | ### 4.4. `Services/SemiIOLiteController.cs` — I/O 보드 제어 LS산전 ASCII 프로토콜로 릴레이 출력을 제어하고 입력(i1, i2)을 읽습니다. #### 릴레이 상태 (`RelayState` enum) | 값 | 이름 | 동작 | |----|------|------| | `0` | `None` | 모든 릴레이 OFF (고정/밀폐 상태) | | `1` | `Exhaust` | 릴레이1 ON — 배기 밸브 작동 | | `2` | `Pressurize` | 릴레이2 ON — 인가 밸브 작동 | | `3` | `Clamp` | 릴레이1+2 ON — 지그 물리 고정 | #### 주요 메서드 | 메서드 | 역할 | |--------|------| | `SetStateAsync(RelayState)` | 릴레이 상태 변경 쓰기 명령 전송 + ACK 응답 대기 (최대 500ms) | | `RequestInputState(onTx)` | 주소 `0A07`의 입력 상태 읽기 요청 전송 (비동기) | | `ParseInputBuffer(buffer, out i1, out i2)` | 수신 버퍼를 파싱하여 i1, i2 디지털 입력 감지 | | `CheckConnectionAsync()` | 주소 `0001` 읽기로 통신 유효성 확인 (핸드셰이크) | ### 4.5. `Services/PressureSensorController.cs` — 압력 센서 통신 KN-2240W 디지털 센서와 **Modbus RTU** 프로토콜로 통신합니다. #### Modbus RTU 프레임 구조 | 구분 | 바이트 구성 | |------|-------------| | **요청 (8바이트)** | `[Slave Addr]` `[Func 0x04]` `[Start Addr Hi]` `[Start Addr Lo]` `[Count Hi]` `[Count Lo]` `[CRC Lo]` `[CRC Hi]` | | **응답 (7바이트)** | `[Slave Addr]` `[Func 0x04]` `[Byte Count]` `[Data Hi]` `[Data Lo]` `[CRC Lo]` `[CRC Hi]` | #### 주요 메서드 | 메서드 | 역할 | |--------|------| | `RequestCurrentPressure(onTx)` | 레지스터 `0x0000` 1개 읽기 요청 전송 | | `ParsePressureResponse(buffer, out pressureValue)` | CRC 검증 + 데이터 파싱 후 원시 압력값 반환 | | `BuildReadRequest(startAddress, count)` | Modbus RTU Read Input Registers 프레임 동적 생성 | ### 4.6. `ViewModels/MainViewModel.cs` — ★ 비즈니스 로직 및 UI 바인딩 통신 인프라를 제외한 순수 검사 시퀀스와 사용자 인터페이스 로직을 담당합니다. (~800줄) #### 4.6.1. 이벤트 기반 데이터 처리 * `_connManager.IOPacketReceived` 구독: IO 보드 응답 시 `UpdateJigLamps` 호출 * `_connManager.PressurePacketReceived` 구독: 압력 센서 응답 시 압력값 갱신 및 UI 반영 #### 4.6.2. 자가 복구 (Self-Healing) 알고리즘 (ConnectionManager 내 구현) ``` [정상 동작] → 100ms 폴링 중 응답 수신 ↓ 1.5초 무응답 [경고] → 램프 Red + 실패 카운터 증가 ↓ 10회 연속 무응답 [단절 선언] → 포트 닫기 및 리소스 정리 ↓ 즉시 [재연결 시도] → 3초 주기로 포트 재오픈 시도 ``` #### 4.6.3. 수동 검사 시퀀스 (StartAutoHoldSequence) ``` [스위치 → 인가 위치] _wasPressurized = true ↓ [스위치 → 고정 위치] && _wasPressurized ↓ StartAutoHoldSequence() 트리거 ↓ (1) 현재 압력을 "검사 시작 압력"으로 저장 (2) HoldTime 초 동안 1초 간격 대기 (3) |최종 압력 - 시작 압력| vs AllowedErrorRange 비교 ├── 이내 → PASS (LimeGreen) └── 초과 → FAIL (Red) ``` #### 4.6.4. 자동 검사 시퀀스 (StartAutoCycleSequence) ``` [시작 버튼 클릭] (고정 상태에서만 가능) ↓ 단계 1: 인가(Pressurize) → 목표 압력 도달 대기 ↓ 단계 2: 고정(None) → 밸브 폐쇄 ↓ 단계 3: HoldTime 초 동안 압력 유지 검사 ↓ 단계 4: 배기(Exhaust) → 0.0bar 도달 대기 (60초 타임아웃) ↓ 단계 5: 다시 고정(None) → 종료 ``` #### 4.6.5. 주요 바인딩 프로퍼티 요약 | 카테고리 | 프로퍼티 | 용도 | |----------|----------|------| | **장비 램프** | `LampIOStatus`, `LampPressureStatus` | I/O / 압력 센서 연결 표시등 | | **압력** | `CurrentPressureText`, `CurrentPressureColor` | 실시간 압력값 표시 | | **수동 결과** | `ResultText`, `ResultColor` | PASS/FAIL 결과 표시 | | **자동 결과** | `AutoResultText`, `AutoResultColor` | 자동 검사 PASS/FAIL | | **로그** | `IOLogText`, `PrLogText` | I/O / 압력 센서 HEX 통신 로그 | ### 4.7. `ViewModels/SettingsViewModel` / `ParameterViewModel` * 설정 복사본(`Clone`)을 사용한 원본 보호 및 취소 기능 * 시스템의 COM 포트 자동 감지 및 폴백 처리 * 입력 유효성 검증 (정규식 필터링 및 범위 체크) ### 4.8. `Views/MainWindow.xaml` — 메인 UI 구조 * `Viewbox Stretch="Uniform"`: 창 크기 변화에도 UI 비율 유지 * 커스텀 타이틀 바 + 탭 컨트롤 기반 화면 구성 * 실시간 로그 창 (I/O 녹색, 압력 파란색) 접이식 구현 및 자동 스크롤 --- ## 5. 안정성 메커니즘 ### 5.1. `bool` 기반 상태 관리 (Refactoring #11) 기존에는 UI 램프의 `Brush` 색상을 비교하여 통신 상태를 판단했으나, 리액터링을 통해 `_isIOConnected`, `_isPressureConnected` 명시적 필드를 도입하여 로직의 안정성과 가독성을 높였습니다. ### 5.2. 책임 분리 (Refactoring #10) `ConnectionManager`를 통해 통신 인프라를 캡슐화했습니다. 메인 로직은 장치와의 직접적인 포트 관리에서 자유로워졌으며, 이벤트 기반으로 응답을 처리하여 UI 스레드 정지 현상을 방지합니다. ### 5.3. 글로벌 예외 처리 (`App.xaml.cs`) `DispatcherUnhandledException`, `AppDomain.UnhandledException`, `TaskScheduler.UnobservedTaskException`을 모두 처리하여 예기치 못한 에러 시에도 프로그램이 강제 종료되지 않도록 보호합니다. --- ## 6. 설정 파일 (`config.xml`) 실행 파일 위치에 XML 형식으로 저장되며, 손상 시 자동으로 기본값으로 복원됩니다. ```xml COM3 9600 COM4 9600 true 2 true 30 0.3 4.0 false ``` --- ## 7. 유지보수 및 확장 가이드 ### 새 검사 파라미터 추가 시 1. `Models/AppConfig.cs` 데이터 추가 및 `Clone()` 메서드 업데이트 2. `Views/ParameterWindow.xaml` 입력 UI 추가 3. `ViewModels/MainViewModel.UpdateParamDisplay()`에 반영 ### 새 기기 추가 시 1. `Services/` 컨트롤러 구현 → `ConnectionManager.cs`에 인스턴스/폴링 추가 2. `MainViewModel.cs`에서 이벤트 구독 및 파싱 로직 연결