commit
9fc060c14b
967 changed files with 14127 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,83 @@ |
|||
{ |
|||
"Version": 1, |
|||
"WorkspaceRootPath": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\", |
|||
"Documents": [ |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\viewmodels\\parameterviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\viewmodels\\parameterviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\bin\\release\\net472\\jig_test.exe||{177559E0-D141-11D0-92DF-00A0C9138C45}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\bin\\release\\net472\\jig_test.exe||{177559E0-D141-11D0-92DF-00A0C9138C45}" |
|||
} |
|||
], |
|||
"DocumentGroupContainers": [ |
|||
{ |
|||
"Orientation": 0, |
|||
"VerticalTabListWidth": 256, |
|||
"DocumentGroups": [ |
|||
{ |
|||
"DockedWidth": 200, |
|||
"SelectedChildIndex": 3, |
|||
"Children": [ |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 3, |
|||
"Title": "jig_test.exe", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"RelativeDocumentMoniker": "jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"RelativeToolTip": "jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000165|", |
|||
"WhenOpened": "2026-03-19T02:31:29.291Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 2, |
|||
"Title": "ParameterViewModel.cs", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"RelativeDocumentMoniker": "jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"RelativeToolTip": "jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-03-18T01:19:24.151Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 1, |
|||
"Title": "MainViewModel.cs", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\MainViewModel.cs", |
|||
"RelativeDocumentMoniker": "jig_test\\ViewModels\\MainViewModel.cs", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\MainViewModel.cs", |
|||
"RelativeToolTip": "jig_test\\ViewModels\\MainViewModel.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-03-18T01:19:22.881Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 0, |
|||
"Title": "MainWindow.xaml", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\Views\\MainWindow.xaml", |
|||
"RelativeDocumentMoniker": "jig_test\\Views\\MainWindow.xaml", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\Views\\MainWindow.xaml", |
|||
"RelativeToolTip": "jig_test\\Views\\MainWindow.xaml", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|", |
|||
"WhenOpened": "2026-03-18T01:19:19.351Z", |
|||
"EditorCaption": "" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
{ |
|||
"Version": 1, |
|||
"WorkspaceRootPath": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\", |
|||
"Documents": [ |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\views\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\viewmodels\\mainviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\viewmodels\\parameterviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\viewmodels\\parameterviewmodel.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|c:\\users\\computer1\\desktop\\mobi\\jig_test\\jig_test\\bin\\release\\net472\\jig_test.exe||{177559E0-D141-11D0-92DF-00A0C9138C45}", |
|||
"RelativeMoniker": "D:0:0:{B6E92BCB-5515-4599-A710-A56894B63761}|jig_test\\jig_test.csproj|solutionrelative:jig_test\\bin\\release\\net472\\jig_test.exe||{177559E0-D141-11D0-92DF-00A0C9138C45}" |
|||
} |
|||
], |
|||
"DocumentGroupContainers": [ |
|||
{ |
|||
"Orientation": 0, |
|||
"VerticalTabListWidth": 256, |
|||
"DocumentGroups": [ |
|||
{ |
|||
"DockedWidth": 200, |
|||
"SelectedChildIndex": 3, |
|||
"Children": [ |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 3, |
|||
"Title": "jig_test.exe", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"RelativeDocumentMoniker": "jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"RelativeToolTip": "jig_test\\bin\\Release\\net472\\jig_test.exe", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000165|", |
|||
"WhenOpened": "2026-03-19T02:31:29.291Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 2, |
|||
"Title": "ParameterViewModel.cs", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"RelativeDocumentMoniker": "jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"RelativeToolTip": "jig_test\\ViewModels\\ParameterViewModel.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-03-18T01:19:24.151Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 1, |
|||
"Title": "MainViewModel.cs", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\MainViewModel.cs", |
|||
"RelativeDocumentMoniker": "jig_test\\ViewModels\\MainViewModel.cs", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\ViewModels\\MainViewModel.cs", |
|||
"RelativeToolTip": "jig_test\\ViewModels\\MainViewModel.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-03-18T01:19:22.881Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 0, |
|||
"Title": "MainWindow.xaml", |
|||
"DocumentMoniker": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\Views\\MainWindow.xaml", |
|||
"RelativeDocumentMoniker": "jig_test\\Views\\MainWindow.xaml", |
|||
"ToolTip": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\jig_test\\jig_test\\Views\\MainWindow.xaml", |
|||
"RelativeToolTip": "jig_test\\Views\\MainWindow.xaml", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|", |
|||
"WhenOpened": "2026-03-18T01:19:19.351Z", |
|||
"EditorCaption": "" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,320 @@ |
|||
# 지그 압력 및 누출 검사 애플리케이션 (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 |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<AppConfig> |
|||
<PortName>COM3</PortName> |
|||
<BaudRate>9600</BaudRate> |
|||
<PressurePortName>COM4</PressurePortName> |
|||
<PressureBaudRate>9600</PressureBaudRate> |
|||
<IOEnabled>true</IOEnabled> |
|||
<IOStationId>2</IOStationId> |
|||
<PressureEnabled>true</PressureEnabled> |
|||
<HoldTime>30</HoldTime> |
|||
<AllowedErrorRange>0.3</AllowedErrorRange> |
|||
<AutoTestTargetPressure>4.0</AutoTestTargetPressure> |
|||
<IsLogVisible>false</IsLogVisible> |
|||
</AppConfig> |
|||
``` |
|||
|
|||
--- |
|||
|
|||
## 7. 유지보수 및 확장 가이드 |
|||
|
|||
### 새 검사 파라미터 추가 시 |
|||
1. `Models/AppConfig.cs` 데이터 추가 및 `Clone()` 메서드 업데이트 |
|||
2. `Views/ParameterWindow.xaml` 입력 UI 추가 |
|||
3. `ViewModels/MainViewModel.UpdateParamDisplay()`에 반영 |
|||
|
|||
### 새 기기 추가 시 |
|||
1. `Services/` 컨트롤러 구현 → `ConnectionManager.cs`에 인스턴스/폴링 추가 |
|||
2. `MainViewModel.cs`에서 이벤트 구독 및 파싱 로직 연결 |
|||
Binary file not shown.
@ -0,0 +1,3 @@ |
|||
<Solution> |
|||
<Project Path="jig_test/jig_test.csproj" Id="b6e92bcb-5515-4599-a710-a56894b63761" /> |
|||
</Solution> |
|||
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<configuration> |
|||
<startup> |
|||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> |
|||
</startup> |
|||
</configuration> |
|||
@ -0,0 +1,9 @@ |
|||
<Application x:Class="jig_test.App" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:local="clr-namespace:jig_test" |
|||
StartupUri="Views/MainWindow.xaml"> |
|||
<Application.Resources> |
|||
|
|||
</Application.Resources> |
|||
</Application> |
|||
@ -0,0 +1,49 @@ |
|||
using System; |
|||
using System.Windows; |
|||
|
|||
namespace jig_test |
|||
{ |
|||
/// <summary>
|
|||
/// App.xaml에 대한 상호 작용 논리
|
|||
/// </summary>
|
|||
public partial class App : Application |
|||
{ |
|||
protected override void OnStartup(StartupEventArgs e) |
|||
{ |
|||
base.OnStartup(e); |
|||
|
|||
// 1. UI 스레드 예외 처리
|
|||
this.DispatcherUnhandledException += (s, ex) => |
|||
{ |
|||
HandleException(ex.Exception, "UI 스레드"); |
|||
ex.Handled = true; // 프로그램이 즉시 종료되지 않도록 함
|
|||
}; |
|||
|
|||
// 2. 백그라운드 스레드 예외 처리
|
|||
AppDomain.CurrentDomain.UnhandledException += (s, ex) => |
|||
{ |
|||
HandleException(ex.ExceptionObject as Exception, "AppDomain"); |
|||
}; |
|||
|
|||
// 3. 비동기 Task 예외 처리
|
|||
System.Threading.Tasks.TaskScheduler.UnobservedTaskException += (s, ex) => |
|||
{ |
|||
HandleException(ex.Exception, "TaskScheduler"); |
|||
ex.SetObserved(); |
|||
}; |
|||
} |
|||
|
|||
private void HandleException(Exception ex, string source) |
|||
{ |
|||
if (ex == null) return; |
|||
|
|||
string errorMsg = $"[치명적 오류 - {source}] {ex.Message}\n\n{ex.StackTrace}"; |
|||
System.Diagnostics.Debug.WriteLine(errorMsg); |
|||
|
|||
// 중요: 사용자에게 알림을 표시하되 프로그램을 멈추지 않음
|
|||
MessageBox.Show("프로그램 실행 중 문제가 발생했습니다. 로그를 확인해 주세요.\n\n" + ex.Message, |
|||
"안정성 경고", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,54 @@ |
|||
using System; |
|||
|
|||
namespace jig_test.Models |
|||
{ |
|||
/// <summary>
|
|||
/// 애플리케이션의 모든 설정 정보를 담는 모델 클래스
|
|||
/// </summary>
|
|||
public class AppConfig |
|||
{ |
|||
// 통신 관련
|
|||
public string PortName { get; set; } = "COM1"; // IO 보드 전용 포트
|
|||
public int BaudRate { get; set; } = 9600; |
|||
|
|||
public string PressurePortName { get; set; } = "COM2"; // 압력 센서 전용 포트
|
|||
public int PressureBaudRate { get; set; } = 9600; |
|||
|
|||
// 기기 활성화 및 ID (정석적인 int 방식)
|
|||
public bool IOEnabled { get; set; } = true; |
|||
public int IOStationId { get; set; } = 2; |
|||
public bool PressureEnabled { get; set; } = true; |
|||
public int PressureSlaveId { get; set; } = 1; |
|||
|
|||
// 테스트 파라미터
|
|||
public int HoldTime { get; set; } = 30; // 유지 시간 (초)
|
|||
public double AllowedErrorRange { get; set; } = 0.3; // 허용 압력 오차 (bar)
|
|||
public double AutoTestTargetPressure { get; set; } = 4.0; // 자동 검사 목표 압력 (bar)
|
|||
|
|||
// UI 상태
|
|||
public bool IsLogVisible { get; set; } = false; // 로그 창 표시 여부
|
|||
|
|||
/// <summary>
|
|||
/// 현재 설정의 깊은 복사본을 생성합니다.
|
|||
/// 설정 창에 전달하여 '취소' 시 원본 보호에 사용합니다.
|
|||
/// </summary>
|
|||
public AppConfig Clone() |
|||
{ |
|||
return new AppConfig |
|||
{ |
|||
PortName = this.PortName, |
|||
BaudRate = this.BaudRate, |
|||
PressurePortName = this.PressurePortName, |
|||
PressureBaudRate = this.PressureBaudRate, |
|||
IOEnabled = this.IOEnabled, |
|||
IOStationId = this.IOStationId, |
|||
PressureEnabled = this.PressureEnabled, |
|||
PressureSlaveId = this.PressureSlaveId, |
|||
HoldTime = this.HoldTime, |
|||
AllowedErrorRange = this.AllowedErrorRange, |
|||
AutoTestTargetPressure = this.AutoTestTargetPressure, |
|||
IsLogVisible = this.IsLogVisible, |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
|
|||
namespace jig_test.Models |
|||
{ |
|||
/// <summary>
|
|||
/// 검사 결과 로그 데이터를 담는 클래스
|
|||
/// </summary>
|
|||
public class LogData |
|||
{ |
|||
public int Index { get; set; } |
|||
public DateTime StartTime { get; set; } |
|||
public DateTime EndTime { get; set; } |
|||
public double TargetPressure { get; set; } |
|||
public double ErrorValue { get; set; } |
|||
public string Result { get; set; } // "PASS" or "FAIL"
|
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. --> |
|||
<Project> |
|||
<PropertyGroup> |
|||
<Configuration>Release</Configuration> |
|||
<Platform>Any CPU</Platform> |
|||
<PublishDir>C:\Users\COMPUTER1\Desktop\mobi\jig_exe</PublishDir> |
|||
<PublishProtocol>FileSystem</PublishProtocol> |
|||
<_TargetId>Folder</_TargetId> |
|||
<TargetFramework>net472</TargetFramework> |
|||
<RuntimeIdentifier>win-x86</RuntimeIdentifier> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. --> |
|||
<Project> |
|||
<PropertyGroup> |
|||
<History>True|2026-03-19T08:41:16.2563200Z||;True|2026-03-19T17:27:36.4622370+09:00||;True|2026-03-19T13:43:30.1971093+09:00||;True|2026-03-19T12:19:34.5998380+09:00||;True|2026-03-19T12:13:28.5236966+09:00||;</History> |
|||
<LastFailureDetails /> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -0,0 +1,71 @@ |
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
namespace jig_test.Properties |
|||
{ |
|||
|
|||
|
|||
/// <summary>
|
|||
/// 지역화된 문자열 등을 찾기 위한 강력한 형식의 리소스 클래스입니다.
|
|||
/// </summary>
|
|||
// 이 클래스는 ResGen 또는 Visual Studio와 같은 도구를 통해 StronglyTypedResourceBuilder
|
|||
// 클래스에서 자동으로 생성되었습니다.
|
|||
// 멤버를 추가하거나 제거하려면 .ResX 파일을 편집한 다음 /str 옵션을 사용하여
|
|||
// ResGen을 다시 실행하거나 VS 프로젝트를 다시 빌드하십시오.
|
|||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] |
|||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] |
|||
internal class Resources |
|||
{ |
|||
|
|||
private static global::System.Resources.ResourceManager resourceMan; |
|||
|
|||
private static global::System.Globalization.CultureInfo resourceCulture; |
|||
|
|||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] |
|||
internal Resources() |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 이 클래스에서 사용하는 캐시된 ResourceManager 인스턴스를 반환합니다.
|
|||
/// </summary>
|
|||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] |
|||
internal static global::System.Resources.ResourceManager ResourceManager |
|||
{ |
|||
get |
|||
{ |
|||
if ((resourceMan == null)) |
|||
{ |
|||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("jig_test.Properties.Resources", typeof(Resources).Assembly); |
|||
resourceMan = temp; |
|||
} |
|||
return resourceMan; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 이 강력한 형식의 리소스 클래스를 사용하여 모든 리소스 조회에 대해 현재 스레드의 CurrentUICulture 속성을
|
|||
/// 재정의합니다.
|
|||
/// </summary>
|
|||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] |
|||
internal static global::System.Globalization.CultureInfo Culture |
|||
{ |
|||
get |
|||
{ |
|||
return resourceCulture; |
|||
} |
|||
set |
|||
{ |
|||
resourceCulture = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<root> |
|||
<!-- |
|||
Microsoft ResX Schema |
|||
|
|||
Version 2.0 |
|||
|
|||
The primary goals of this format is to allow a simple XML format |
|||
that is mostly human readable. The generation and parsing of the |
|||
various data types are done through the TypeConverter classes |
|||
associated with the data types. |
|||
|
|||
Example: |
|||
|
|||
... ado.net/XML headers & schema ... |
|||
<resheader name="resmimetype">text/microsoft-resx</resheader> |
|||
<resheader name="version">2.0</resheader> |
|||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> |
|||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> |
|||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> |
|||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> |
|||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> |
|||
<value>[base64 mime encoded serialized .NET Framework object]</value> |
|||
</data> |
|||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> |
|||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> |
|||
<comment>This is a comment</comment> |
|||
</data> |
|||
|
|||
There are any number of "resheader" rows that contain simple |
|||
name/value pairs. |
|||
|
|||
Each data row contains a name, and value. The row also contains a |
|||
type or mimetype. Type corresponds to a .NET class that support |
|||
text/value conversion through the TypeConverter architecture. |
|||
Classes that don't support this are serialized and stored with the |
|||
mimetype set. |
|||
|
|||
The mimetype is used for serialized objects, and tells the |
|||
ResXResourceReader how to depersist the object. This is currently not |
|||
extensible. For a given mimetype the value must be set accordingly: |
|||
|
|||
Note - application/x-microsoft.net.object.binary.base64 is the format |
|||
that the ResXResourceWriter will generate, however the reader can |
|||
read any of the formats listed below. |
|||
|
|||
mimetype: application/x-microsoft.net.object.binary.base64 |
|||
value : The object must be serialized with |
|||
: System.Serialization.Formatters.Binary.BinaryFormatter |
|||
: and then encoded with base64 encoding. |
|||
|
|||
mimetype: application/x-microsoft.net.object.soap.base64 |
|||
value : The object must be serialized with |
|||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter |
|||
: and then encoded with base64 encoding. |
|||
|
|||
mimetype: application/x-microsoft.net.object.bytearray.base64 |
|||
value : The object must be serialized into a byte array |
|||
: using a System.ComponentModel.TypeConverter |
|||
: and then encoded with base64 encoding. |
|||
--> |
|||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> |
|||
<xsd:element name="root" msdata:IsDataSet="true"> |
|||
<xsd:complexType> |
|||
<xsd:choice maxOccurs="unbounded"> |
|||
<xsd:element name="metadata"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" /> |
|||
<xsd:attribute name="type" type="xsd:string" /> |
|||
<xsd:attribute name="mimetype" type="xsd:string" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="assembly"> |
|||
<xsd:complexType> |
|||
<xsd:attribute name="alias" type="xsd:string" /> |
|||
<xsd:attribute name="name" type="xsd:string" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="data"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
|||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> |
|||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> |
|||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
<xsd:element name="resheader"> |
|||
<xsd:complexType> |
|||
<xsd:sequence> |
|||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> |
|||
</xsd:sequence> |
|||
<xsd:attribute name="name" type="xsd:string" use="required" /> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
</xsd:choice> |
|||
</xsd:complexType> |
|||
</xsd:element> |
|||
</xsd:schema> |
|||
<resheader name="resmimetype"> |
|||
<value>text/microsoft-resx</value> |
|||
</resheader> |
|||
<resheader name="version"> |
|||
<value>2.0</value> |
|||
</resheader> |
|||
<resheader name="reader"> |
|||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
|||
</resheader> |
|||
<resheader name="writer"> |
|||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> |
|||
</resheader> |
|||
</root> |
|||
@ -0,0 +1,30 @@ |
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// This code was generated by a tool.
|
|||
// Runtime Version:4.0.30319.42000
|
|||
//
|
|||
// Changes to this file may cause incorrect behavior and will be lost if
|
|||
// the code is regenerated.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
namespace jig_test.Properties |
|||
{ |
|||
|
|||
|
|||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] |
|||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] |
|||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase |
|||
{ |
|||
|
|||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); |
|||
|
|||
public static Settings Default |
|||
{ |
|||
get |
|||
{ |
|||
return defaultInstance; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version='1.0' encoding='utf-8'?> |
|||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)"> |
|||
<Profiles> |
|||
<Profile Name="(Default)" /> |
|||
</Profiles> |
|||
<Settings /> |
|||
</SettingsFile> |
|||
@ -0,0 +1,66 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Xml.Serialization; |
|||
using jig_test.Models; |
|||
|
|||
namespace jig_test.Services |
|||
{ |
|||
/// <summary>
|
|||
/// 프로그램 설정(AppConfig)을 파일로 저장하고 불러오는 기능을 담당하는 서비스 클래스
|
|||
/// XML 직렬화(Serialization) 방식을 사용하여 실행 파일 폴더의 config.xml 파일에 저장합니다.
|
|||
/// </summary>
|
|||
public class ConfigService |
|||
{ |
|||
// 설정 파일이 저장될 경로 (실행 파일과 동일한 위치의 config.xml)
|
|||
private static readonly string ConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.xml"); |
|||
|
|||
/// <summary>
|
|||
/// 설정 파일을 읽어와 AppConfig 객체로 변환합니다.
|
|||
/// 파일이 없거나 읽기 오류가 발생하면 기본값을 가진 새 객체를 반환합니다.
|
|||
/// </summary>
|
|||
/// <returns>불러온 설정 객체</returns>
|
|||
public static AppConfig Load() |
|||
{ |
|||
// 파일이 존재하지 않으면 기본 설정으로 시작
|
|||
if (!File.Exists(ConfigPath)) return new AppConfig(); |
|||
|
|||
try |
|||
{ |
|||
var serializer = new XmlSerializer(typeof(AppConfig)); |
|||
using (var reader = new StreamReader(ConfigPath)) |
|||
{ |
|||
// XML 파일을 읽어 C# 객체(AppConfig)로 변환(역직렬화)
|
|||
return (AppConfig)serializer.Deserialize(reader); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
// 파일 손상 등 예외 발생 시 기본값 반환
|
|||
return new AppConfig(); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 현재 설정(AppConfig) 객체를 XML 파일로 저장합니다.
|
|||
/// 프로그램 종료 시나 설정 변경 시 호출됩니다.
|
|||
/// </summary>
|
|||
/// <param name="config">저장할 설정 객체</param>
|
|||
public static void Save(AppConfig config) |
|||
{ |
|||
try |
|||
{ |
|||
var serializer = new XmlSerializer(typeof(AppConfig)); |
|||
using (var writer = new StreamWriter(ConfigPath)) |
|||
{ |
|||
// C# 객체를 XML 형식의 텍스트 파일로 저장(직렬화)
|
|||
serializer.Serialize(writer, config); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
// 저장 실패 시 디버그 출력
|
|||
System.Diagnostics.Debug.WriteLine($"[Config] 설정 저장 실패: {ex.Message}"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,500 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.IO.Ports; |
|||
using System.Threading.Tasks; |
|||
using System.Windows.Threading; |
|||
using jig_test.Models; |
|||
|
|||
namespace jig_test.Services |
|||
{ |
|||
/// <summary>
|
|||
/// 시리얼 포트 수명 관리, 폴링, 재연결, DataReceived 이벤트 처리를 담당하는 서비스.
|
|||
/// MainViewModel에서 통신 인프라 책임을 분리하여 유지보수성을 향상시킵니다.
|
|||
/// </summary>
|
|||
public class ConnectionManager : IDisposable |
|||
{ |
|||
// ==========================================
|
|||
// 외부 의존성
|
|||
// ==========================================
|
|||
private AppConfig _config; |
|||
private readonly Dispatcher _uiDispatcher; |
|||
|
|||
// ==========================================
|
|||
// 시리얼 포트 및 컨트롤러
|
|||
// ==========================================
|
|||
private SerialPort _ioPort; |
|||
private SerialPort _pressurePort; |
|||
private SemiIOLiteController _ioController; |
|||
private PressureSensorController _pressureController; |
|||
|
|||
// ==========================================
|
|||
// 폴링 타이머 및 상태 추적
|
|||
// ==========================================
|
|||
private DispatcherTimer _pollingTimer; |
|||
private bool _isPolling = false; |
|||
|
|||
private List<byte> _serialBuffer = new List<byte>(); |
|||
private List<byte> _prSerialBuffer = new List<byte>(); |
|||
|
|||
private DateTime _lastIOResponseTime = DateTime.MinValue; |
|||
private DateTime _lastPrResponseTime = DateTime.MinValue; |
|||
private DateTime _lastIOReconnectAttempt = DateTime.MinValue; |
|||
private DateTime _lastPrReconnectAttempt = DateTime.MinValue; |
|||
|
|||
private int _ioFailCount = 0; |
|||
private int _prFailCount = 0; |
|||
private bool _prErrorLogged = false; |
|||
|
|||
// ==========================================
|
|||
// 공개 상태 프로퍼티
|
|||
// ==========================================
|
|||
public bool IsIOConnected { get; private set; } = false; |
|||
public bool IsPressureConnected { get; private set; } = false; |
|||
public SemiIOLiteController IOController => _ioController; |
|||
public PressureSensorController PressureController => _pressureController; |
|||
|
|||
// ==========================================
|
|||
// ViewModel에 알리는 이벤트/콜백
|
|||
// ==========================================
|
|||
|
|||
/// <summary>I/O 패킷 완성 시 (buffer). UI 스레드에서 호출됨.</summary>
|
|||
public event Action<byte[]> IOPacketReceived; |
|||
/// <summary>압력 패킷 완성 시 (buffer). UI 스레드에서 호출됨.</summary>
|
|||
public event Action<byte[]> PressurePacketReceived; |
|||
/// <summary>IO 연결 상태 변경 시 (isConnected). 폴링/DataReceived 스레드에서 호출될 수 있음.</summary>
|
|||
public event Action<bool> IOConnectionChanged; |
|||
/// <summary>압력 연결 상태 변경 시 (isConnected). 폴링/DataReceived 스레드에서 호출될 수 있음.</summary>
|
|||
public event Action<bool> PressureConnectionChanged; |
|||
/// <summary>IO 시스템 로그 메시지 (msg)</summary>
|
|||
public event Action<string> LogMessage; |
|||
/// <summary>압력 시스템 로그 메시지 (msg)</summary>
|
|||
public event Action<string> PrLogMessage; |
|||
/// <summary>HEX 로그 메시지 (prefix, buffer)</summary>
|
|||
public event Action<string, byte[]> HexLogMessage; |
|||
/// <summary>IO 폴링 틱 상태 (ioFailCount, prFailCount, prErrorLogged)</summary>
|
|||
public event Action<string> PrTimeoutLogMessage; |
|||
|
|||
|
|||
// ==========================================
|
|||
// 생성자
|
|||
// ==========================================
|
|||
public ConnectionManager(AppConfig config, Dispatcher uiDispatcher) |
|||
{ |
|||
_config = config; |
|||
_uiDispatcher = uiDispatcher; |
|||
InitPollingTimer(); |
|||
} |
|||
|
|||
/// <summary>설정이 변경되었을 때 호출하여 내부 config 참조를 갱신합니다.</summary>
|
|||
public void UpdateConfig(AppConfig config) |
|||
{ |
|||
_config = config; |
|||
} |
|||
|
|||
// ==========================================
|
|||
// 폴링 타이머
|
|||
// ==========================================
|
|||
private void InitPollingTimer() |
|||
{ |
|||
_pollingTimer = new DispatcherTimer(); |
|||
_pollingTimer.Interval = TimeSpan.FromMilliseconds(100); |
|||
_pollingTimer.Tick += async (s, e) => |
|||
{ |
|||
if (_isPolling) return; |
|||
_isPolling = true; |
|||
try |
|||
{ |
|||
await OnPollingTickAsync(); |
|||
} |
|||
finally |
|||
{ |
|||
_isPolling = false; |
|||
} |
|||
}; |
|||
} |
|||
|
|||
// ==========================================
|
|||
// 연결 / 재연결 / 해제
|
|||
// ==========================================
|
|||
public async Task TryConnectAsync() |
|||
{ |
|||
_ioFailCount = 0; |
|||
_prFailCount = 0; |
|||
_lastIOReconnectAttempt = DateTime.Now; |
|||
_lastPrReconnectAttempt = DateTime.Now; |
|||
_lastIOResponseTime = DateTime.MinValue; |
|||
_lastPrResponseTime = DateTime.MinValue; |
|||
|
|||
try |
|||
{ |
|||
DisconnectIO(); |
|||
DisconnectPressure(); |
|||
await Task.Delay(200); |
|||
|
|||
bool anyOpened = false; |
|||
|
|||
if (_config.IOEnabled) |
|||
{ |
|||
anyOpened |= await ReconnectIOAsync(false); |
|||
} |
|||
|
|||
if (_config.PressureEnabled) |
|||
{ |
|||
anyOpened |= await ReconnectPressureAsync(false); |
|||
} |
|||
|
|||
// 연결 결과를 ViewModel에서 처리하도록 이벤트만 발행
|
|||
if (anyOpened) |
|||
{ |
|||
if (_config.IOEnabled) LogMessage?.Invoke(">>> 기기 응답 대기 중... (폴링으로 자동 감지)"); |
|||
if (_config.PressureEnabled) PrLogMessage?.Invoke(">>> 기기 응답 대기 중... (폴링으로 자동 감지)"); |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
LogMessage?.Invoke($"연결 프로세스 에러: {ex.Message}"); |
|||
throw; // ViewModel에서 처리
|
|||
} |
|||
finally |
|||
{ |
|||
_pollingTimer.Start(); |
|||
} |
|||
} |
|||
|
|||
public async Task<bool> ReconnectIOAsync(bool isRetry = true) |
|||
{ |
|||
DisconnectIO(); |
|||
if (isRetry) await Task.Delay(200); |
|||
|
|||
try |
|||
{ |
|||
_ioPort = new SerialPort(_config.PortName, _config.BaudRate, Parity.None, 8, StopBits.One); |
|||
_ioPort.DataReceived += IOPort_DataReceived; |
|||
_ioPort.Open(); |
|||
_ioController = new SemiIOLiteController(_ioPort, _config.IOStationId); |
|||
LogMessage?.Invoke($">>> IO 포트 {_config.PortName} 열기 성공 (응답 대기)"); |
|||
return true; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
if (isRetry) LogMessage?.Invoke($"IO 포트 재연결 시도 실패: {ex.Message}"); |
|||
else LogMessage?.Invoke($"IO 포트 열기 실패: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public async Task<bool> ReconnectPressureAsync(bool isRetry = true) |
|||
{ |
|||
DisconnectPressure(); |
|||
if (isRetry) await Task.Delay(200); |
|||
|
|||
try |
|||
{ |
|||
_pressurePort = new SerialPort(_config.PressurePortName, _config.PressureBaudRate, Parity.None, 8, StopBits.One); |
|||
_pressurePort.DataReceived += PressurePort_DataReceived; |
|||
_pressurePort.Open(); |
|||
_pressureController = new PressureSensorController(_pressurePort, _config.PressureSlaveId); |
|||
PrLogMessage?.Invoke($">>> 압력 포트 {_config.PressurePortName} 열기 성공 (응답 대기)"); |
|||
return true; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
if (isRetry) PrLogMessage?.Invoke($"압력 포트 재연결 시도 실패: {ex.Message}"); |
|||
else PrLogMessage?.Invoke($"압력 포트 열기 실패: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public void DisconnectIO() |
|||
{ |
|||
_ioController?.Dispose(); |
|||
_ioController = null; |
|||
if (_ioPort != null) |
|||
{ |
|||
try |
|||
{ |
|||
_ioPort.DataReceived -= IOPort_DataReceived; |
|||
if (_ioPort.IsOpen) _ioPort.Close(); |
|||
} |
|||
catch { } |
|||
_ioPort.Dispose(); |
|||
_ioPort = null; |
|||
} |
|||
SetIOConnected(false); |
|||
_serialBuffer.Clear(); |
|||
} |
|||
|
|||
public void DisconnectPressure() |
|||
{ |
|||
_pressureController?.Dispose(); |
|||
_pressureController = null; |
|||
if (_pressurePort != null) |
|||
{ |
|||
try |
|||
{ |
|||
_pressurePort.DataReceived -= PressurePort_DataReceived; |
|||
if (_pressurePort.IsOpen) _pressurePort.Close(); |
|||
} |
|||
catch { } |
|||
_pressurePort.Dispose(); |
|||
_pressurePort = null; |
|||
} |
|||
SetPressureConnected(false); |
|||
_prSerialBuffer.Clear(); |
|||
} |
|||
|
|||
public void DisconnectAll() |
|||
{ |
|||
_pollingTimer?.Stop(); |
|||
DisconnectIO(); |
|||
DisconnectPressure(); |
|||
} |
|||
|
|||
// ==========================================
|
|||
// DataReceived 이벤트 핸들러
|
|||
// ==========================================
|
|||
private void IOPort_DataReceived(object sender, SerialDataReceivedEventArgs e) |
|||
{ |
|||
if (_ioPort == null || !_ioPort.IsOpen) return; |
|||
try |
|||
{ |
|||
int bytesToRead = _ioPort.BytesToRead; |
|||
byte[] tempBuffer = new byte[bytesToRead]; |
|||
_ioPort.Read(tempBuffer, 0, bytesToRead); |
|||
|
|||
lock (_serialBuffer) |
|||
{ |
|||
_serialBuffer.AddRange(tempBuffer); |
|||
|
|||
if (_serialBuffer.Contains(0x04)) |
|||
{ |
|||
byte[] fullPacket = _serialBuffer.ToArray(); |
|||
_serialBuffer.Clear(); |
|||
|
|||
_uiDispatcher.BeginInvoke(new Action(() => |
|||
{ |
|||
IOPacketReceived?.Invoke(fullPacket); |
|||
})); |
|||
} |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[ConnectionManager] IO DataReceived 에러: {ex.Message}"); |
|||
} |
|||
} |
|||
|
|||
private void PressurePort_DataReceived(object sender, SerialDataReceivedEventArgs e) |
|||
{ |
|||
if (_pressurePort == null || !_pressurePort.IsOpen) return; |
|||
try |
|||
{ |
|||
int bytesToRead = _pressurePort.BytesToRead; |
|||
byte[] tempBuffer = new byte[bytesToRead]; |
|||
_pressurePort.Read(tempBuffer, 0, bytesToRead); |
|||
|
|||
lock (_prSerialBuffer) |
|||
{ |
|||
_prSerialBuffer.AddRange(tempBuffer); |
|||
|
|||
if (_prSerialBuffer.Count >= 7) |
|||
{ |
|||
if (_prSerialBuffer[0] == _config.PressureSlaveId && _prSerialBuffer[1] == 0x04) |
|||
{ |
|||
byte[] fullPacket = _prSerialBuffer.ToArray(); |
|||
_prSerialBuffer.Clear(); |
|||
|
|||
_uiDispatcher.BeginInvoke(new Action(() => |
|||
{ |
|||
PressurePacketReceived?.Invoke(fullPacket); |
|||
})); |
|||
} |
|||
else |
|||
{ |
|||
_prSerialBuffer.RemoveAt(0); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[ConnectionManager] PR DataReceived 에러: {ex.Message}"); |
|||
} |
|||
} |
|||
|
|||
// ==========================================
|
|||
// IO 파싱 결과 수신 (ViewModel에서 호출)
|
|||
// ==========================================
|
|||
/// <summary>IO 패킷 파싱 성공 시 ViewModel에서 호출하여 응답 시간과 연결 상태를 갱신합니다.</summary>
|
|||
public void NotifyIOResponseReceived() |
|||
{ |
|||
_lastIOResponseTime = DateTime.Now; |
|||
SetIOConnected(true); |
|||
} |
|||
|
|||
/// <summary>압력 패킷 파싱 성공 시 ViewModel에서 호출하여 응답 시간과 연결 상태를 갱신합니다.</summary>
|
|||
public void NotifyPressureResponseReceived() |
|||
{ |
|||
_lastPrResponseTime = DateTime.Now; |
|||
_prErrorLogged = false; |
|||
_prFailCount = 0; |
|||
SetPressureConnected(true); |
|||
} |
|||
|
|||
// ==========================================
|
|||
// 폴링 틱 (100ms마다 호출)
|
|||
// ==========================================
|
|||
private async Task OnPollingTickAsync() |
|||
{ |
|||
try |
|||
{ |
|||
bool hasIo = !_config.IOEnabled || (_ioPort != null && _ioPort.IsOpen); |
|||
bool hasPr = !_config.PressureEnabled || (_pressurePort != null && _pressurePort.IsOpen); |
|||
|
|||
// 포트 재연결 시도
|
|||
if (!hasIo) |
|||
{ |
|||
if ((DateTime.Now - _lastIOReconnectAttempt).TotalSeconds >= 3) |
|||
{ |
|||
_lastIOReconnectAttempt = DateTime.Now; |
|||
_ = ReconnectIOAsync(); |
|||
} |
|||
} |
|||
|
|||
if (!hasPr) |
|||
{ |
|||
if ((DateTime.Now - _lastPrReconnectAttempt).TotalSeconds >= 3) |
|||
{ |
|||
_lastPrReconnectAttempt = DateTime.Now; |
|||
_ = ReconnectPressureAsync(); |
|||
} |
|||
} |
|||
|
|||
if (!hasIo && !hasPr) return; |
|||
|
|||
// IO 폴링
|
|||
if (_ioController != null && _ioPort != null && _ioPort.IsOpen) |
|||
{ |
|||
_ioController.RequestInputState((txBytes) => |
|||
{ |
|||
HexLogMessage?.Invoke("[IO TX]", txBytes); |
|||
}); |
|||
|
|||
double elapsedIo = (DateTime.Now - _lastIOResponseTime).TotalMilliseconds; |
|||
if (elapsedIo > 1500) |
|||
{ |
|||
_ioFailCount++; |
|||
SetIOConnected(false); |
|||
} |
|||
else |
|||
{ |
|||
_ioFailCount = 0; |
|||
SetIOConnected(true); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
SetIOConnected(false); |
|||
} |
|||
|
|||
// 압력 폴링
|
|||
if (_pressureController != null && _pressurePort != null && _pressurePort.IsOpen) |
|||
{ |
|||
_pressureController.RequestCurrentPressure((txBytes) => |
|||
{ |
|||
HexLogMessage?.Invoke("[PR TX]", txBytes); |
|||
}); |
|||
|
|||
double elapsedPr = (DateTime.Now - _lastPrResponseTime).TotalMilliseconds; |
|||
if (elapsedPr > 1500) |
|||
{ |
|||
_prFailCount++; |
|||
if (!_prErrorLogged && _lastPrResponseTime != DateTime.MinValue) |
|||
{ |
|||
PrTimeoutLogMessage?.Invoke("[압력 센서] 무응답 타임아웃"); |
|||
_prErrorLogged = true; |
|||
} |
|||
SetPressureConnected(false); |
|||
} |
|||
else |
|||
{ |
|||
_prFailCount = 0; |
|||
_prErrorLogged = false; |
|||
SetPressureConnected(true); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
SetPressureConnected(false); |
|||
} |
|||
|
|||
// 임계치 초과 재연결
|
|||
int threshold = 10; |
|||
|
|||
if (_config.IOEnabled && _ioFailCount >= threshold) |
|||
{ |
|||
if (_ioPort != null) |
|||
{ |
|||
LogMessage?.Invoke("!!! [시스템] I/O 포트 응답 없음 (10회). 자동 재연결 시도 중."); |
|||
_ = _uiDispatcher.BeginInvoke(new Action(async () => |
|||
{ |
|||
DisconnectIO(); |
|||
_ioFailCount = 0; |
|||
_lastIOReconnectAttempt = DateTime.Now; |
|||
await ReconnectIOAsync(); |
|||
})); |
|||
} |
|||
} |
|||
|
|||
if (_config.PressureEnabled && _prFailCount >= threshold) |
|||
{ |
|||
if (_pressurePort != null) |
|||
{ |
|||
PrLogMessage?.Invoke("!!! [시스템] 압력 센서 응답 없음 (10회). 자동 재연결 시도 중."); |
|||
_ = _uiDispatcher.BeginInvoke(new Action(async () => |
|||
{ |
|||
DisconnectPressure(); |
|||
_prFailCount = 0; |
|||
_lastPrReconnectAttempt = DateTime.Now; |
|||
await ReconnectPressureAsync(); |
|||
})); |
|||
} |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
LogMessage?.Invoke($"[시스템 장애] 폴링 루프 치명적 에러: {ex.Message}"); |
|||
} |
|||
} |
|||
|
|||
// ==========================================
|
|||
// 내부 헬퍼
|
|||
// ==========================================
|
|||
private void SetIOConnected(bool value) |
|||
{ |
|||
if (IsIOConnected != value) |
|||
{ |
|||
IsIOConnected = value; |
|||
IOConnectionChanged?.Invoke(value); |
|||
} |
|||
} |
|||
|
|||
private void SetPressureConnected(bool value) |
|||
{ |
|||
if (IsPressureConnected != value) |
|||
{ |
|||
IsPressureConnected = value; |
|||
PressureConnectionChanged?.Invoke(value); |
|||
} |
|||
} |
|||
|
|||
// ==========================================
|
|||
// IDisposable
|
|||
// ==========================================
|
|||
public void Dispose() |
|||
{ |
|||
DisconnectAll(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Text; |
|||
using jig_test.Models; |
|||
|
|||
namespace jig_test.Services |
|||
{ |
|||
/// <summary>
|
|||
/// 검사 결과를 CSV 파일로 저장하는 서비스
|
|||
/// </summary>
|
|||
public class LogService |
|||
{ |
|||
private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); |
|||
|
|||
/// <summary>
|
|||
/// 검사 데이터를 CSV 파일에 추가합니다.
|
|||
/// 파일명은 yyyy-MM-dd.csv 형식을 따릅니다.
|
|||
/// </summary>
|
|||
/// <returns>저장 성공 여부</returns>
|
|||
public static bool AppendLog(LogData data) |
|||
{ |
|||
try |
|||
{ |
|||
// Logs 폴더가 없으면 생성
|
|||
if (!Directory.Exists(LogDirectory)) |
|||
{ |
|||
Directory.CreateDirectory(LogDirectory); |
|||
} |
|||
|
|||
string fileName = DateTime.Now.ToString("yyyy-MM-dd") + ".csv"; |
|||
string filePath = Path.Combine(LogDirectory, fileName); |
|||
|
|||
bool isNewFile = !File.Exists(filePath); |
|||
|
|||
using (var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) |
|||
using (var writer = new StreamWriter(stream, new UTF8Encoding(true))) |
|||
{ |
|||
// 새 파일이면 헤더 추가
|
|||
if (isNewFile) |
|||
{ |
|||
writer.WriteLine("순번,검사 시작 시간,검사 완료 시간,검사 기준 압력(bar),오차(bar),최종 결과"); |
|||
} |
|||
|
|||
string line = string.Format("{0},{1:yyyy-MM-dd HH:mm:ss},{2:yyyy-MM-dd HH:mm:ss},{3:F1},{4:F1},{5}", |
|||
data.Index, |
|||
data.StartTime, |
|||
data.EndTime, |
|||
data.TargetPressure, |
|||
data.ErrorValue, |
|||
data.Result); |
|||
|
|||
writer.WriteLine(line); |
|||
} |
|||
return true; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
System.Diagnostics.Debug.WriteLine($"[LogService] 로그 저장 실패: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 오늘 날짜의 로그 파일에서 PASS/FAIL 개수를 분석하여 반환합니다.
|
|||
/// </summary>
|
|||
public static (int pass, int fail) GetTodaySummary() |
|||
{ |
|||
int pass = 0; |
|||
int fail = 0; |
|||
|
|||
try |
|||
{ |
|||
string fileName = DateTime.Now.ToString("yyyy-MM-dd") + ".csv"; |
|||
string filePath = Path.Combine(LogDirectory, fileName); |
|||
|
|||
if (!File.Exists(filePath)) return (0, 0); |
|||
|
|||
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) |
|||
using (var reader = new StreamReader(stream, new UTF8Encoding(true))) |
|||
{ |
|||
string header = reader.ReadLine(); // 헤더 건너뛰기
|
|||
while (!reader.EndOfStream) |
|||
{ |
|||
string line = reader.ReadLine(); |
|||
if (string.IsNullOrWhiteSpace(line)) continue; |
|||
|
|||
string[] parts = line.Split(','); |
|||
if (parts.Length < 6) continue; |
|||
|
|||
string result = parts[5].Trim().ToUpper(); |
|||
if (result == "PASS") pass++; |
|||
else if (result == "FAIL") fail++; |
|||
} |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
System.Diagnostics.Debug.WriteLine($"[LogService] 로그 요약 읽기 실패: {ex.Message}"); |
|||
} |
|||
|
|||
return (pass, fail); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,150 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.IO.Ports; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace jig_test.Services |
|||
{ |
|||
/// <summary>
|
|||
/// KN-2000W 시리즈 압력 디스플레이와 Modbus RTU 통신을 통해 실시간 압력값을 읽어오는 서비스
|
|||
/// I/O 보드 컨트롤러와 동일한 Raw Serial 통신 방식으로 구현됨
|
|||
/// </summary>
|
|||
public class PressureSensorController : IDisposable |
|||
{ |
|||
private SerialPort _serialPort; |
|||
private readonly byte _slaveAddress; |
|||
|
|||
public PressureSensorController(SerialPort sharedPort, int slaveAddress = 1) |
|||
{ |
|||
_serialPort = sharedPort; |
|||
_slaveAddress = (byte)slaveAddress; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 시리얼 포트가 닫혀있을 경우 오픈을 시도합니다.
|
|||
/// </summary>
|
|||
public bool Connect() |
|||
{ |
|||
try |
|||
{ |
|||
if (!_serialPort.IsOpen) _serialPort.Open(); |
|||
return true; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[Pressure Sensor] 연결 실패: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 비동기 폴링을 위해 압력값 읽기(Tx)를 요청합니다. (이후 수신은 DataReceived 이벤트에서 처리)
|
|||
/// </summary>
|
|||
public void RequestCurrentPressure(Action<byte[]> onTx = null) |
|||
{ |
|||
if (!_serialPort.IsOpen) return; |
|||
try |
|||
{ |
|||
byte[] request = BuildReadRequest(0x0000, 1); |
|||
onTx?.Invoke(request); |
|||
_serialPort.Write(request, 0, request.Length); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[Pressure Sensor] Request Error: {ex.Message}"); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 수신된 바이트 버퍼를 해석하여 현재 압력값을 반환합니다.
|
|||
/// </summary>
|
|||
public bool ParsePressureResponse(byte[] buffer, out int pressureValue) |
|||
{ |
|||
pressureValue = 0; |
|||
if (buffer == null || buffer.Length < 7) return false; |
|||
|
|||
try |
|||
{ |
|||
// CRC 검증 (최소 7바이트: Slave(1) + Func(1) + Count(1) + Data(2) + CRC(2))
|
|||
// 실제 Modbus RTU Read Input Registers 응답 길이는 3 + (N*2) + 2
|
|||
// N=1인 경우 7바이트
|
|||
if (!ValidateCRC(buffer, buffer.Length)) return false; |
|||
|
|||
// 0: Slave ID, 1: Function Code (0x04), 2: Byte Count (0x02)
|
|||
if (buffer[0] == _slaveAddress && buffer[1] == 0x04 && buffer.Length >= 5) |
|||
{ |
|||
byte bCount = buffer[2]; |
|||
if (buffer.Length >= bCount + 5) |
|||
{ |
|||
pressureValue = (short)((buffer[3] << 8) | buffer[4]); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[Pressure Sensor] Parse Error: {ex.Message}"); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Modbus RTU 읽기 요청 프레임을 동적으로 생성합니다.
|
|||
/// #2: 슬레이브 주소, 시작 주소, 레지스터 수를 파라미터로 사용하며 CRC를 자동 계산합니다.
|
|||
/// </summary>
|
|||
private byte[] BuildReadRequest(ushort startAddress, ushort count) |
|||
{ |
|||
byte[] frame = new byte[8]; |
|||
frame[0] = _slaveAddress; // 슬레이브 주소 (동적)
|
|||
frame[1] = 0x04; // Function Code: Read Input Registers
|
|||
frame[2] = (byte)(startAddress >> 8); // 시작 주소 High
|
|||
frame[3] = (byte)(startAddress & 0xFF); // 시작 주소 Low
|
|||
frame[4] = (byte)(count >> 8); // 레지스터 수 High
|
|||
frame[5] = (byte)(count & 0xFF); // 레지스터 수 Low
|
|||
|
|||
ushort crc = CalculateCRC(frame, 6); |
|||
frame[6] = (byte)(crc & 0xFF); // CRC Low
|
|||
frame[7] = (byte)(crc >> 8); // CRC High
|
|||
|
|||
return frame; |
|||
} |
|||
|
|||
private bool ValidateCRC(byte[] frame, int length) |
|||
{ |
|||
if (length < 4) return false; |
|||
ushort expected = CalculateCRC(frame, length - 2); |
|||
ushort actual = (ushort)(frame[length - 2] | (frame[length - 1] << 8)); |
|||
return expected == actual; |
|||
} |
|||
|
|||
private ushort CalculateCRC(byte[] buffer, int length) |
|||
{ |
|||
ushort crc = 0xFFFF; |
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
crc ^= buffer[i]; |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
if ((crc & 0x0001) != 0) |
|||
{ |
|||
crc >>= 1; |
|||
crc ^= 0xA001; |
|||
} |
|||
else |
|||
{ |
|||
crc >>= 1; |
|||
} |
|||
} |
|||
} |
|||
return crc; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// #3: 포트 수명은 호출자(MainWindow)에서 관리하므로, 여기서는 참조만 정리합니다.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
_serialPort = null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,320 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.IO; |
|||
using System.IO.Ports; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace jig_test.Services |
|||
{ |
|||
/// <summary>
|
|||
/// Semi IO Lite 보드 (SIO-0201A) 제어 서비스 클래스
|
|||
/// 이 클래스는 LS산전 SV-iG5 인버터 호환 프로토콜(ASCII)을 사용하여 릴레이 출력을 제어하고 입력을 읽습니다.
|
|||
/// </summary>
|
|||
public class SemiIOLiteController : IDisposable |
|||
{ |
|||
private SerialPort _serialPort; // MainWindow에서 생성한 공유 시리얼 포트 인스턴스
|
|||
private readonly string _stationId; // 기기의 고유 국번 (기본값 "04")
|
|||
|
|||
// 프로토콜 특수 문자 정의 (ASCII 제어 문자)
|
|||
private const char ENQ = (char)0x05; // 시작 문자 (Enquiry) - 요청용
|
|||
private const char EOT = (char)0x04; // 종료 문자 (End of Transmission)
|
|||
private const char ACK = (char)0x06; // 응답 문자 (Acknowledge) - 응답용
|
|||
|
|||
/// <summary>
|
|||
/// 릴레이 상태 정의 (비트 조합 방식)
|
|||
/// 릴레이 1과 2의 조합으로 공정 상태(고정, 인가, 배기)를 결정합니다.
|
|||
/// </summary>
|
|||
public enum RelayState |
|||
{ |
|||
None = 0, // 00: 모든 릴레이 OFF
|
|||
Exhaust = 1, // 01: 릴레이 1번 ON (배기 밸브 작동)
|
|||
Pressurize = 2, // 10: 릴레이 2번 ON (인가 밸브 작동)
|
|||
Clamp = 3 // 11: 릴레이 1, 2번 모두 ON (지그 고정 및 폐쇄)
|
|||
} |
|||
|
|||
public SemiIOLiteController(SerialPort sharedPort, int stationId = 2) |
|||
{ |
|||
_serialPort = sharedPort; |
|||
_stationId = stationId.ToString("D2"); // 정수 ID를 2자리 문자열로 변환 (02 등)
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 시리얼 포트가 닫혀있을 경우 오픈을 시도합니다.
|
|||
/// </summary>
|
|||
public bool Connect() |
|||
{ |
|||
try |
|||
{ |
|||
if (!_serialPort.IsOpen) _serialPort.Open(); |
|||
return true; |
|||
} |
|||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException || ex is InvalidOperationException) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] 연결 실패 (포트 접근/상태 오류): {ex.Message}"); |
|||
return false; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] 연결 실패 (기타): {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 릴레이 상태를 물리적으로 변경하는 명령을 보냅니다.
|
|||
/// 형식: ENQ + 국번 + W(Write) + 주소(0001) + 갯수(1) + 데이터(000X) + SUM + EOT
|
|||
/// </summary>
|
|||
public async Task<bool> SetStateAsync(RelayState state) |
|||
{ |
|||
if (_serialPort == null || !_serialPort.IsOpen) return false; |
|||
|
|||
string commandStr = BuildCommand(state); |
|||
|
|||
try |
|||
{ |
|||
_serialPort.DiscardInBuffer(); // 기존 버퍼 비우기
|
|||
_serialPort.Write(commandStr); |
|||
|
|||
// 최대 500ms 동안 응답 대기 및 ACK 확인
|
|||
byte[] buffer = new byte[1024]; |
|||
int totalRead = 0; |
|||
var sw = Stopwatch.StartNew(); |
|||
|
|||
while (sw.ElapsedMilliseconds < 500) |
|||
{ |
|||
if (_serialPort.BytesToRead > 0) |
|||
{ |
|||
int count = _serialPort.Read(buffer, totalRead, buffer.Length - totalRead); |
|||
totalRead += count; |
|||
if (Array.IndexOf(buffer, (byte)0x04, 0, totalRead) != -1) break; // EOT 감지
|
|||
} |
|||
await Task.Delay(10); |
|||
} |
|||
|
|||
if (totalRead == 0) return false; |
|||
|
|||
string response = Encoding.ASCII.GetString(buffer, 0, totalRead); |
|||
return ValidateResponse(response); |
|||
} |
|||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException || ex is InvalidOperationException) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] 쓰기 명령 전송 실패 (포트 오류): {ex.Message}"); |
|||
return false; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] 쓰기 명령 전송 에러: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 통신 유효성을 확인하기 위해 기기의 현재 상태를 읽어봅니다. (핸드셰이크)
|
|||
/// </summary>
|
|||
public async Task<bool> CheckConnectionAsync() |
|||
{ |
|||
if (!_serialPort.IsOpen) return false; |
|||
|
|||
try |
|||
{ |
|||
// 주소 0001의 데이터를 1개 읽어오는 명령 생성
|
|||
string readCmd = BuildReadCommand(); |
|||
_serialPort.DiscardInBuffer(); // 기존 버퍼 비우기
|
|||
_serialPort.Write(readCmd); |
|||
|
|||
// 최대 500ms 동안 응답 대기
|
|||
byte[] buffer = new byte[1024]; |
|||
int totalRead = 0; |
|||
var sw = Stopwatch.StartNew(); // #13: DateTime.Now 대신 Stopwatch 사용
|
|||
|
|||
while (sw.ElapsedMilliseconds < 500) |
|||
{ |
|||
if (_serialPort.BytesToRead > 0) |
|||
{ |
|||
int count = _serialPort.Read(buffer, totalRead, buffer.Length - totalRead); |
|||
totalRead += count; |
|||
// EOT 문자가 들어오면 한 프레임 완성으로 간주
|
|||
if (Array.IndexOf(buffer, (byte)0x04, 0, totalRead) != -1) break; |
|||
} |
|||
await Task.Delay(10); |
|||
} |
|||
|
|||
if (totalRead == 0) return false; |
|||
|
|||
string response = Encoding.ASCII.GetString(buffer, 0, totalRead); |
|||
return ValidateResponse(response); |
|||
} |
|||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException || ex is InvalidOperationException) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] CheckConnection 포트 접속 에러: {ex.Message}"); |
|||
return false; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] CheckConnection 에러: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 읽기용 프로토콜 패킷을 조립합니다.
|
|||
/// </summary>
|
|||
private string BuildReadCommand() |
|||
{ |
|||
string body = $"{_stationId}R00011"; // 국번 + R + 주소 + 갯수
|
|||
int sum = 0; |
|||
foreach (char c in body) sum += (int)c; |
|||
return $"{ENQ}{body}{(sum & 0xFF).ToString("X2")}{EOT}"; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 응답 패킷의 무결성을 검증합니다.
|
|||
/// IO 보드 응답은 ACK(0x06)로 시작하고 EOT(0x04)로 끝납니다.
|
|||
/// </summary>
|
|||
private bool ValidateResponse(string response) |
|||
{ |
|||
if (string.IsNullOrEmpty(response) || response.Length < 7) return false; |
|||
|
|||
try |
|||
{ |
|||
// 응답은 ACK 또는 ENQ로 시작할 수 있음 (기기에 따라 다름)
|
|||
int startIdx = response.IndexOf(ACK); |
|||
if (startIdx == -1) startIdx = response.IndexOf(ENQ); |
|||
|
|||
int eotIdx = response.LastIndexOf(EOT); // 마지막 EOT를 찾음
|
|||
|
|||
return (startIdx != -1 && eotIdx != -1 && eotIdx > startIdx); |
|||
} |
|||
catch |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 쓰기용 프로토콜 패킷을 조립합니다. (국번 + W + 주소 + 갯수 + 데이터)
|
|||
/// </summary>
|
|||
private string BuildCommand(RelayState state) |
|||
{ |
|||
string data = ((int)state).ToString("X1"); |
|||
string body = $"{_stationId}W00011000{data}"; |
|||
int sum = 0; |
|||
foreach (char c in body) sum += (int)c; |
|||
return $"{ENQ}{body}{(sum & 0xFF).ToString("X2")}{EOT}"; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 지그 앞의 물리 버튼(i1 단자)이 눌렸는지 확인하는 요청을 보냅니다.
|
|||
/// 주소 0A07번의 데이터를 1개 읽어옵니다.
|
|||
/// </summary>
|
|||
public bool RequestInputState(Action<byte[]> onTx = null) |
|||
{ |
|||
if (_serialPort == null || !_serialPort.IsOpen) return false; |
|||
try |
|||
{ |
|||
// 입력 상태 읽기 (주소 0A07)
|
|||
string body = $"{_stationId}R0A071"; |
|||
int sum = 0; |
|||
foreach (char c in body) sum += (int)c; |
|||
// [ENQ] + body + [SUM(2자리)] + [EOT]
|
|||
string command = $"{ENQ}{body}{(sum & 0xFF).ToString("X2")}{EOT}"; |
|||
byte[] cmdBytes = Encoding.ASCII.GetBytes(command); |
|||
|
|||
onTx?.Invoke(cmdBytes); // 송신 로그 콜백 호출
|
|||
|
|||
_serialPort.Write(command); |
|||
return true; |
|||
} |
|||
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException || ex is InvalidOperationException) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] RequestInputState 포트 에러: {ex.Message}"); |
|||
return false; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] RequestInputState 기타 에러: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 수신된 바이트 버퍼를 해석하여 디지털 입력(i1, i2) 상태를 반환합니다.
|
|||
/// </summary>
|
|||
public bool ParseInputBuffer(byte[] buffer, out bool i1, out bool i2) |
|||
{ |
|||
i1 = i2 = false; |
|||
if (buffer == null || buffer.Length == 0) return false; |
|||
|
|||
string res = Encoding.ASCII.GetString(buffer); |
|||
|
|||
// 정규식을 활용하여 응답 포맷 검증
|
|||
// 'R' 이후에 오는 3자리 Hex 다음 1자리 값만 추출
|
|||
var match = Regex.Match(res, @"R[0-9A-Fa-f]{3}([0-9A-Fa-f])"); |
|||
if (match.Success) |
|||
{ |
|||
char val = match.Groups[1].Value[0]; |
|||
if (val == '1' || val == '3') i1 = true; |
|||
if (val == '2' || val == '3') i2 = true; |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 16진수 문자열 형태(예: 05303257...)의 Raw 커맨드들을 직접 바이트 배열로 변환하여 전송합니다.
|
|||
/// </summary>
|
|||
public async Task<bool> SendRawHexCommandAsync(string hexString, Action<byte[]> onTx = null) |
|||
{ |
|||
if (_serialPort == null || !_serialPort.IsOpen) return false; |
|||
try |
|||
{ |
|||
// Hex 문자열을 바이트 배열로 변환
|
|||
byte[] buffer = new byte[hexString.Length / 2]; |
|||
for (int i = 0; i < buffer.Length; i++) |
|||
buffer[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); |
|||
|
|||
onTx?.Invoke(buffer); |
|||
|
|||
_serialPort.DiscardInBuffer(); |
|||
_serialPort.Write(buffer, 0, buffer.Length); |
|||
|
|||
// EOT까지 응답을 대기 (간단한 형태)
|
|||
byte[] rxBuffer = new byte[1024]; |
|||
int totalRead = 0; |
|||
var sw = Stopwatch.StartNew(); |
|||
|
|||
while (sw.ElapsedMilliseconds < 500) |
|||
{ |
|||
if (_serialPort.BytesToRead > 0) |
|||
{ |
|||
int count = _serialPort.Read(rxBuffer, totalRead, rxBuffer.Length - totalRead); |
|||
totalRead += count; |
|||
if (Array.IndexOf(rxBuffer, (byte)0x04, 0, totalRead) != -1) break; // EOT 감지
|
|||
} |
|||
await Task.Delay(10); |
|||
} |
|||
|
|||
if (totalRead == 0) return false; |
|||
|
|||
string response = Encoding.ASCII.GetString(rxBuffer, 0, totalRead); |
|||
return ValidateResponse(response); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.WriteLine($"[IO Board] SendRawHexCommand 에러: {ex.Message}"); |
|||
return false; |
|||
} |
|||
} |
|||
/// <summary>
|
|||
/// 포트 수명은 호출자(MainViewModel)에서 관리하므로, 여기서는 참조만 정리합니다.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
_serialPort = null; |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.ComponentModel; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace jig_test.ViewModels.Base |
|||
{ |
|||
/// <summary>
|
|||
/// MVVM의 ViewModel을 위한 기본 클래스 (INotifyPropertyChanged 구현)
|
|||
/// 프로퍼티 변경 시 UI(View)에 자동으로 변경 사항을 알립니다.
|
|||
/// </summary>
|
|||
public abstract class ObservableObject : INotifyPropertyChanged |
|||
{ |
|||
public event PropertyChangedEventHandler PropertyChanged; |
|||
|
|||
/// <summary>
|
|||
/// 프로퍼티의 변경을 통지합니다.
|
|||
/// </summary>
|
|||
/// <param name="propertyName">메서드를 호출한 프로퍼티 이름 (CallerMemberName으로 자동 삽입)</param>
|
|||
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// 필드의 값이 변경될 경우에만 값을 업데이트하고 이벤트를 발생시킵니다.
|
|||
/// </summary>
|
|||
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) |
|||
{ |
|||
if (Equals(storage, value)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
storage = value; |
|||
OnPropertyChanged(propertyName); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using System.Windows.Input; |
|||
|
|||
namespace jig_test.ViewModels.Base |
|||
{ |
|||
/// <summary>
|
|||
/// ICommand를 구현하여 View의 Event(Click 등)를 ViewModel의 메서드(동작)에 바인딩하는 클래스
|
|||
/// </summary>
|
|||
public class RelayCommand : ICommand |
|||
{ |
|||
private readonly Action<object> _execute; |
|||
private readonly Predicate<object> _canExecute; |
|||
|
|||
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null) |
|||
{ |
|||
_execute = execute ?? throw new ArgumentNullException(nameof(execute)); |
|||
_canExecute = canExecute; |
|||
} |
|||
|
|||
public RelayCommand(Action execute, Func<bool> canExecute = null) |
|||
{ |
|||
if (execute == null) throw new ArgumentNullException(nameof(execute)); |
|||
_execute = _ => execute(); |
|||
if (canExecute != null) |
|||
{ |
|||
_canExecute = _ => canExecute(); |
|||
} |
|||
} |
|||
|
|||
public event EventHandler CanExecuteChanged |
|||
{ |
|||
add => CommandManager.RequerySuggested += value; |
|||
remove => CommandManager.RequerySuggested -= value; |
|||
} |
|||
|
|||
public bool CanExecute(object parameter) |
|||
{ |
|||
return _canExecute == null || _canExecute(parameter); |
|||
} |
|||
|
|||
public void Execute(object parameter) |
|||
{ |
|||
_execute(parameter); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Windows.Data; |
|||
using System.Windows.Media; |
|||
|
|||
namespace jig_test.ViewModels.Converters |
|||
{ |
|||
public class BooleanToBrushConverter : IValueConverter |
|||
{ |
|||
public Brush TrueBrush { get; set; } = Brushes.LimeGreen; |
|||
public Brush FalseBrush { get; set; } = Brushes.Gray; |
|||
|
|||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
if (value is bool b) |
|||
{ |
|||
return b ? TrueBrush : FalseBrush; |
|||
} |
|||
return FalseBrush; |
|||
} |
|||
|
|||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using System.Windows.Data; |
|||
|
|||
namespace jig_test.ViewModels.Converters |
|||
{ |
|||
public class InverseBooleanConverter : IValueConverter |
|||
{ |
|||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
if (value is bool b) |
|||
{ |
|||
return !b; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
if (value is bool b) |
|||
{ |
|||
return !b; |
|||
} |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using System.Windows; |
|||
using System.Windows.Input; |
|||
using jig_test.Models; |
|||
using jig_test.Services; |
|||
using jig_test.ViewModels.Base; |
|||
|
|||
namespace jig_test.ViewModels |
|||
{ |
|||
public class ParameterViewModel : ObservableObject |
|||
{ |
|||
private AppConfig _config; |
|||
|
|||
public AppConfig Config |
|||
{ |
|||
get => _config; |
|||
set => SetProperty(ref _config, value); |
|||
} |
|||
|
|||
public ICommand SaveCommand { get; } |
|||
public ICommand CancelCommand { get; } |
|||
|
|||
public Action<bool> RequestClose; |
|||
|
|||
public ParameterViewModel(AppConfig currentConfig) |
|||
{ |
|||
Config = currentConfig.Clone(); |
|||
|
|||
SaveCommand = new RelayCommand(SaveParameters); |
|||
CancelCommand = new RelayCommand(CancelParameters); |
|||
} |
|||
|
|||
private void SaveParameters() |
|||
{ |
|||
if (Config.HoldTime <= 0 || Config.HoldTime >= 3600) |
|||
{ |
|||
MessageBox.Show("유지 시간은 1초 이상, 3600초 미만이어야 합니다.", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
return; |
|||
} |
|||
|
|||
if (Config.AllowedErrorRange < 0.0 || Config.AllowedErrorRange > 5.0) |
|||
{ |
|||
MessageBox.Show("허용 오차는 0.0 이상 5.0 이하의 값이어야 합니다.", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
return; |
|||
} |
|||
|
|||
if (Config.AutoTestTargetPressure <= 0.0 || Config.AutoTestTargetPressure > 10.0) |
|||
{ |
|||
MessageBox.Show("자동검사 목표 압력은 0.1 이상 10.0 이하의 값이어야 합니다.", "입력 오류", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
return; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
ConfigService.Save(Config); |
|||
RequestClose?.Invoke(true); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
MessageBox.Show($"파라미터 저장 중 오류가 발생했습니다: {ex.Message}"); |
|||
} |
|||
} |
|||
|
|||
private void CancelParameters() |
|||
{ |
|||
RequestClose?.Invoke(false); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO.Ports; |
|||
using System.Linq; |
|||
using System.Windows.Input; |
|||
using jig_test.Models; |
|||
using jig_test.Services; |
|||
using jig_test.ViewModels.Base; |
|||
using System.Windows; |
|||
|
|||
namespace jig_test.ViewModels |
|||
{ |
|||
public class SettingsViewModel : ObservableObject |
|||
{ |
|||
private AppConfig _config; |
|||
|
|||
public AppConfig Config |
|||
{ |
|||
get => _config; |
|||
set => SetProperty(ref _config, value); |
|||
} |
|||
|
|||
public List<string> AvailablePorts { get; } |
|||
public List<int> AvailableBaudRates { get; } = new List<int> { 9600, 19200, 38400, 57600, 115200 }; |
|||
|
|||
public ICommand SaveCommand { get; } |
|||
public ICommand CancelCommand { get; } |
|||
|
|||
// 창 닫기를 위한 델리게이트 이벤트 (View에서 구독하여 창 닫기)
|
|||
public Action<bool> RequestClose; |
|||
|
|||
public SettingsViewModel(AppConfig currentConfig) |
|||
{ |
|||
// 취소를 위해 복사본 사용
|
|||
Config = currentConfig.Clone(); |
|||
AvailablePorts = SerialPort.GetPortNames().ToList(); |
|||
|
|||
// 시스템에 COM 포트가 전혀 없는 경우 현재 설정값을 폴백으로 추가
|
|||
if (AvailablePorts.Count == 0) |
|||
{ |
|||
if (!string.IsNullOrEmpty(Config.PortName)) AvailablePorts.Add(Config.PortName); |
|||
if (!string.IsNullOrEmpty(Config.PressurePortName) && !AvailablePorts.Contains(Config.PressurePortName)) |
|||
AvailablePorts.Add(Config.PressurePortName); |
|||
} |
|||
|
|||
SaveCommand = new RelayCommand(SaveSettings); |
|||
CancelCommand = new RelayCommand(CancelSettings); |
|||
|
|||
// 초기 포트 값이 리스트에 없을 수도 있으므로 (연결 해제 등), 안전하게 설정
|
|||
if (!AvailablePorts.Contains(Config.PortName)) Config.PortName = AvailablePorts.FirstOrDefault() ?? Config.PortName; |
|||
if (!AvailablePorts.Contains(Config.PressurePortName)) Config.PressurePortName = AvailablePorts.FirstOrDefault() ?? Config.PressurePortName; |
|||
} |
|||
|
|||
private void SaveSettings() |
|||
{ |
|||
try |
|||
{ |
|||
ConfigService.Save(Config); |
|||
RequestClose?.Invoke(true); // 저장성공
|
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
MessageBox.Show($"저장 중 오류가 발생했습니다: {ex.Message}"); |
|||
} |
|||
} |
|||
|
|||
private void CancelSettings() |
|||
{ |
|||
RequestClose?.Invoke(false); // 취소
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,294 @@ |
|||
<Window x:Class="jig_test.Views.MainWindow" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
xmlns:local="clr-namespace:jig_test.Views" |
|||
xmlns:vm="clr-namespace:jig_test.ViewModels" |
|||
mc:Ignorable="d" |
|||
d:DataContext="{d:DesignInstance vm:MainViewModel, IsDesignTimeCreatable=True}" |
|||
Title="지그 체결 누출 테스트" Height="768" Width="1024" |
|||
MinWidth="1000" MinHeight="700" |
|||
Background="#F4F4F9" |
|||
WindowStyle="None" |
|||
AllowsTransparency="True" |
|||
ResizeMode="CanResizeWithGrip" |
|||
WindowState="Maximized" |
|||
WindowStartupLocation="CenterScreen" |
|||
MouseLeftButtonDown="Window_MouseLeftButtonDown"> |
|||
<Grid> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="40"/> <!-- 0: 타이틀 바 --> |
|||
<RowDefinition Height="30"/> <!-- 1: 날짜/시간 바 --> |
|||
<RowDefinition Height="34"/> <!-- 2: 요약 및 설정 바 --> |
|||
<RowDefinition Height="*"/> <!-- 3: 메인 콘텐츠 --> |
|||
<RowDefinition Height="Auto"/> <!-- 4: 통신 로그 (가변) --> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<!-- [0] 타이틀 바 --> |
|||
<Border Grid.Row="0" Background="#333333"> |
|||
<Grid> |
|||
<TextBlock Text="지그 체결 누출 테스트" Foreground="White" FontWeight="Bold" VerticalAlignment="Center" Margin="15,0,0,0"/> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,5,0"> |
|||
<Button Content="—" Width="30" Height="30" Background="Transparent" Foreground="White" BorderThickness="0" FontSize="14" Click="BtnMinimize_Click" ToolTip="최소화"/> |
|||
<Button Content="🗖" Width="30" Height="30" Background="Transparent" Foreground="White" BorderThickness="0" FontSize="14" Click="BtnMaximizeRestore_Click" ToolTip="최대화"/> |
|||
<Button Content="✕" Width="30" Height="30" Background="Transparent" Foreground="White" BorderThickness="0" FontSize="14" Click="BtnClose_Click" ToolTip="닫기"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
</Border> |
|||
|
|||
<!-- [1] 날짜 및 시간 표시 바 --> |
|||
<Border Grid.Row="1" Background="#E0E0E0" BorderBrush="#CCCCCC" BorderThickness="0,0,0,1"> |
|||
<Grid> |
|||
<Button Content="{Binding ToggleLogButtonText}" Command="{Binding ToggleLogCommand}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0" Padding="8,2" FontSize="11" Background="#D0D0D0" BorderBrush="#BBBBBB"/> |
|||
<TextBlock Text="{Binding DateTimeText}" Foreground="#444444" FontWeight="SemiBold" FontSize="13" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0"/> |
|||
</Grid> |
|||
</Border> |
|||
|
|||
<!-- [2] 상단 요약 정보 및 설정 버튼 영역 (날짜 바 스타일) --> |
|||
<Border Grid.Row="2" Background="#E0E0E0" BorderBrush="#CCCCCC" BorderThickness="0,0,0,1"> |
|||
<Grid> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,0,0,0"> |
|||
<TextBlock Text="통신 포트:" Foreground="#444444" FontSize="12" VerticalAlignment="Center"/> |
|||
<TextBlock Text="{Binding SummaryPortText}" FontWeight="Bold" Foreground="#222222" FontSize="12" Margin="5,0,20,0" VerticalAlignment="Center"/> |
|||
<Button Content="🔌 통신 설정" Command="{Binding SettingsCommand}" Padding="8,2" Margin="0,0,8,0" Background="#D0D0D0" BorderBrush="#BBBBBB" FontSize="11"/> |
|||
<Button Content="📊 파라미터" Command="{Binding ParameterCommand}" Padding="8,2" Margin="0,0,8,0" Background="#D0D0D0" BorderBrush="#BBBBBB" FontSize="11"/> |
|||
<Button Content="📋 로그 확인" Command="{Binding OpenLogCommand}" Padding="8,2" Background="#D0D0D0" BorderBrush="#BBBBBB" FontSize="11"/> |
|||
</StackPanel> |
|||
|
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,15,0"> |
|||
<TextBlock Text="오늘:" Foreground="#444444" FontSize="12" VerticalAlignment="Center" Margin="0,0,5,0"/> |
|||
<TextBlock Foreground="#2E7D32" FontWeight="Bold" FontSize="12" Margin="0,0,5,0" VerticalAlignment="Center"> |
|||
<Run Text="PASS ["/><Run Text="{Binding TodayPassCount, Mode=OneWay}"/><Run Text="]"/> |
|||
</TextBlock> |
|||
<TextBlock Foreground="#C62828" FontWeight="Bold" FontSize="12" Margin="0,0,20,0" VerticalAlignment="Center"> |
|||
<Run Text="FAIL ["/><Run Text="{Binding TodayFailCount, Mode=OneWay}"/><Run Text="]"/> |
|||
</TextBlock> |
|||
|
|||
<TextBlock Text="상태:" Foreground="#444444" FontSize="12" VerticalAlignment="Center" Margin="0,0,5,0"/> |
|||
<TextBlock Text="{Binding ConnStatusText}" Foreground="{Binding ConnStatusColor}" FontWeight="Bold" FontSize="12" Margin="0,0,20,0" VerticalAlignment="Center"/> |
|||
|
|||
<Ellipse Width="10" Height="10" Fill="{Binding LampIOStatus}" Margin="0,0,5,0" VerticalAlignment="Center" ToolTip="I/O 보드"/> |
|||
<TextBlock Text="I/O" Margin="0,0,15,0" VerticalAlignment="Center" Foreground="#444444" FontSize="11"/> |
|||
|
|||
<Ellipse Width="10" Height="10" Fill="{Binding LampPressureStatus}" Margin="0,0,5,0" VerticalAlignment="Center" ToolTip="압력 센서"/> |
|||
<TextBlock Text="Pressure" VerticalAlignment="Center" Foreground="#444444" FontSize="11"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
</Border> |
|||
|
|||
<!-- [3] 메인 콘텐츠 영역 --> |
|||
<TabControl Grid.Row="3" Margin="10" Background="Transparent" BorderThickness="0" |
|||
SelectedIndex="{Binding AutoTestSelectedIndex}"> |
|||
<!-- 1. 수동 검사 탭 --> |
|||
<TabItem Header="수동 검사" FontSize="16" FontWeight="SemiBold"> |
|||
<Viewbox Stretch="Uniform" Margin="10"> |
|||
<Grid Width="980"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<!-- 타이틀 --> |
|||
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Margin="0,20,0,10"> |
|||
<TextBlock Text="기존 수동 테스트 진행 상태" FontSize="26" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center"/> |
|||
</StackPanel> |
|||
|
|||
<!-- 상태 표시 램프 및 압력 표시 --> |
|||
<StackPanel Grid.Row="1" VerticalAlignment="Center" Margin="0,10"> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> |
|||
<!-- 배기 --> |
|||
<StackPanel VerticalAlignment="Top" Margin="20"> |
|||
<Grid> |
|||
<Ellipse Width="130" Height="130" Fill="{Binding LampExhaust}" Stroke="#BDBDBD" StrokeThickness="3"/> |
|||
<TextBlock Text="배기" FontSize="20" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center"/> |
|||
</Grid> |
|||
</StackPanel> |
|||
|
|||
<TextBlock Text="↔" FontSize="36" Foreground="#BDBDBD" VerticalAlignment="Top" Margin="10,45,10,0"/> |
|||
|
|||
<!-- 고정/유지 --> |
|||
<StackPanel VerticalAlignment="Top" Margin="20"> |
|||
<Grid> |
|||
<Ellipse Width="130" Height="130" Fill="{Binding LampClampHold}" Stroke="#BDBDBD" StrokeThickness="3"/> |
|||
<TextBlock Text="고정/유지" FontSize="20" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center"/> |
|||
</Grid> |
|||
<StackPanel Margin="0,15,0,0"> |
|||
<TextBlock Text="{Binding HoldTimeParamText}" HorizontalAlignment="Center" Foreground="#555555" FontSize="14" FontWeight="SemiBold"/> |
|||
<TextBlock Text="{Binding HoldErrorParamText}" HorizontalAlignment="Center" Foreground="#555555" FontSize="14" FontWeight="SemiBold"/> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
|
|||
<TextBlock Text="↔" FontSize="36" Foreground="#BDBDBD" VerticalAlignment="Top" Margin="10,45,10,0"/> |
|||
|
|||
<!-- 인가 --> |
|||
<StackPanel VerticalAlignment="Top" Margin="20"> |
|||
<Grid> |
|||
<Ellipse Width="130" Height="130" Fill="{Binding LampPressurize}" Stroke="#BDBDBD" StrokeThickness="3"/> |
|||
<TextBlock Text="인가" FontSize="20" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center"/> |
|||
</Grid> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
|
|||
<!-- 압력 표시 영역 --> |
|||
<Border Background="#E1F5FE" CornerRadius="10" Padding="30,15" HorizontalAlignment="Center" Margin="0,20,0,0" BorderBrush="#03A9F4" BorderThickness="2"> |
|||
<StackPanel> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,10"> |
|||
<TextBlock Text="검사 시작 압력 :" FontSize="16" Foreground="#546E7A" VerticalAlignment="Center" Margin="0,0,10,0"/> |
|||
<TextBlock Text="{Binding BasePressureText}" FontSize="18" FontWeight="Bold" Foreground="#546E7A" VerticalAlignment="Center"/> |
|||
<TextBlock Text=" bar" FontSize="14" Foreground="#546E7A" VerticalAlignment="Bottom" Margin="3,0,0,2"/> |
|||
</StackPanel> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> |
|||
<TextBlock Text="현재 압력 :" FontSize="20" Foreground="#01579B" VerticalAlignment="Bottom" Margin="0,0,15,5"/> |
|||
<TextBlock Text="{Binding CurrentPressureText}" Foreground="{Binding CurrentPressureColor}" FontSize="30" FontWeight="Black" VerticalAlignment="Center"/> |
|||
<TextBlock Text=" bar" FontSize="18" Foreground="#01579B" VerticalAlignment="Bottom" Margin="8,0,0,8"/> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
</Border> |
|||
</StackPanel> |
|||
|
|||
<!-- 최종 결과 영역 --> |
|||
<Border Grid.Row="2" Background="White" BorderBrush="#DDDDDD" BorderThickness="2" Margin="40,10,40,20" Padding="20" CornerRadius="10"> |
|||
<StackPanel HorizontalAlignment="Center"> |
|||
<TextBlock Text="{Binding CurrentStatusText}" FontSize="20" FontWeight="SemiBold" Foreground="#005A9E" HorizontalAlignment="Center" Margin="0,0,0,10"/> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> |
|||
<TextBlock Text="최종 결과 :" FontSize="32" FontWeight="Bold" Foreground="#333333" VerticalAlignment="Center"/> |
|||
<TextBlock Text="{Binding ResultText}" Foreground="{Binding ResultColor}" FontSize="36" FontWeight="Black" VerticalAlignment="Center" Margin="20,0,0,0"/> |
|||
</StackPanel> |
|||
<TextBlock Text="{Binding StartInspectionTimeText}" FontSize="14" Foreground="#777777" HorizontalAlignment="Center" Margin="0,10,0,0"/> |
|||
<TextBlock Text="{Binding LastInspectionTimeText}" FontSize="14" Foreground="#777777" HorizontalAlignment="Center" Margin="0,5,0,0"/> |
|||
</StackPanel> |
|||
</Border> |
|||
</Grid> |
|||
</Viewbox> |
|||
</TabItem> |
|||
|
|||
<!-- 2. 자동 검사 탭 --> |
|||
<TabItem Header="자동 검사" FontSize="16" FontWeight="SemiBold"> |
|||
<Viewbox Stretch="Uniform" Margin="10"> |
|||
<Grid Width="980"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<!-- 상단 헤더 영역: 시작 버튼 --> |
|||
<StackPanel Grid.Row="0" HorizontalAlignment="Center" Margin="0,15,0,5"> |
|||
<TextBlock Text="자동 검사 사이클 (고정 상태에서 시작)" FontSize="22" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" Margin="0,0,0,15"/> |
|||
<Button Command="{Binding AutoStartCommand}" Width="250" Height="45" FontSize="18" FontWeight="Bold" Foreground="White" BorderThickness="0"> |
|||
<Button.Template> |
|||
<ControlTemplate TargetType="Button"> |
|||
<Border x:Name="border" Background="{Binding AutoStartButtonColor}" CornerRadius="8"> |
|||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Content="{Binding AutoStartButtonText}"/> |
|||
</Border> |
|||
<ControlTemplate.Triggers> |
|||
<Trigger Property="IsPressed" Value="True"> |
|||
<Setter TargetName="border" Property="Opacity" Value="0.8"/> |
|||
</Trigger> |
|||
</ControlTemplate.Triggers> |
|||
</ControlTemplate> |
|||
</Button.Template> |
|||
</Button> |
|||
</StackPanel> |
|||
|
|||
<!-- 자동 검사 프로세스 램프 --> |
|||
<StackPanel Grid.Row="1" VerticalAlignment="Center" Margin="0,10"> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> |
|||
<!-- 배기 (완료) --> |
|||
<StackPanel VerticalAlignment="Top" Margin="20"> |
|||
<Grid> |
|||
<Ellipse Width="130" Height="130" Fill="{Binding AutoLampExhaust}" Stroke="#BDBDBD" StrokeThickness="3"/> |
|||
<TextBlock Text="배기(완료)" FontSize="20" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center"/> |
|||
</Grid> |
|||
</StackPanel> |
|||
|
|||
<TextBlock Text="↔" FontSize="36" Foreground="#BDBDBD" VerticalAlignment="Top" Margin="10,45,10,0"/> |
|||
|
|||
<!-- 고정/유지 및 검사 --> |
|||
<StackPanel VerticalAlignment="Top" Margin="20"> |
|||
<Grid> |
|||
<Ellipse Width="130" Height="130" Fill="{Binding AutoLampClampHold}" Stroke="#BDBDBD" StrokeThickness="3"/> |
|||
<TextBlock Text="고정 검사" FontSize="20" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center"/> |
|||
</Grid> |
|||
<StackPanel Margin="0,15,0,0"> |
|||
<TextBlock Text="{Binding HoldTimeParamText}" HorizontalAlignment="Center" Foreground="#555555" FontSize="14" FontWeight="SemiBold"/> |
|||
<TextBlock Text="{Binding HoldErrorParamText}" HorizontalAlignment="Center" Foreground="#555555" FontSize="14" FontWeight="SemiBold"/> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
|
|||
<TextBlock Text="↔" FontSize="36" Foreground="#BDBDBD" VerticalAlignment="Top" Margin="10,45,10,0"/> |
|||
|
|||
<!-- 인가 (시작) --> |
|||
<StackPanel VerticalAlignment="Top" Margin="20"> |
|||
<Grid> |
|||
<Ellipse Width="130" Height="130" Fill="{Binding AutoLampPressurize}" Stroke="#BDBDBD" StrokeThickness="3"/> |
|||
<TextBlock Text="인가" FontSize="20" FontWeight="Bold" Foreground="#333333" HorizontalAlignment="Center" VerticalAlignment="Center"/> |
|||
</Grid> |
|||
<StackPanel Margin="0,15,0,0"> |
|||
<TextBlock Text="{Binding AutoTargetParamText}" HorizontalAlignment="Center" Foreground="#555555" FontSize="14" FontWeight="SemiBold"/> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
|
|||
<!-- 압력 표시 영역 --> |
|||
<Border Background="#E1F5FE" CornerRadius="10" Padding="30,15" HorizontalAlignment="Center" Margin="0,20,0,0" BorderBrush="#03A9F4" BorderThickness="2"> |
|||
<StackPanel> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,10"> |
|||
<TextBlock Text="검사 시작 압력 :" FontSize="16" Foreground="#546E7A" VerticalAlignment="Center" Margin="0,0,10,0"/> |
|||
<TextBlock Text="{Binding BasePressureText}" FontSize="18" FontWeight="Bold" Foreground="#546E7A" VerticalAlignment="Center"/> |
|||
<TextBlock Text=" bar" FontSize="14" Foreground="#546E7A" VerticalAlignment="Bottom" Margin="3,0,0,2"/> |
|||
</StackPanel> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> |
|||
<TextBlock Text="현재 압력 :" FontSize="20" Foreground="#01579B" VerticalAlignment="Bottom" Margin="0,0,15,5"/> |
|||
<TextBlock Text="{Binding CurrentPressureText}" Foreground="{Binding CurrentPressureColor}" FontSize="30" FontWeight="Black" VerticalAlignment="Center"/> |
|||
<TextBlock Text=" bar" FontSize="18" Foreground="#01579B" VerticalAlignment="Bottom" Margin="8,0,0,8"/> |
|||
</StackPanel> |
|||
</StackPanel> |
|||
</Border> |
|||
</StackPanel> |
|||
|
|||
<!-- 최종 결과 영역 --> |
|||
<Border Grid.Row="2" Background="White" BorderBrush="#DDDDDD" BorderThickness="2" Margin="40,10,40,20" Padding="20" CornerRadius="10"> |
|||
<StackPanel HorizontalAlignment="Center"> |
|||
<TextBlock Text="{Binding AutoTestStatusText}" FontSize="20" FontWeight="SemiBold" Foreground="#005A9E" HorizontalAlignment="Center" Margin="0,0,0,10"/> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> |
|||
<TextBlock Text="최종 결과 :" FontSize="32" FontWeight="Bold" Foreground="#333333" VerticalAlignment="Center"/> |
|||
<TextBlock Text="{Binding AutoResultText}" Foreground="{Binding AutoResultColor}" FontSize="36" FontWeight="Black" VerticalAlignment="Center" Margin="20,0,0,0"/> |
|||
</StackPanel> |
|||
<TextBlock Text="{Binding AutoStartInspectionTimeText}" FontSize="14" Foreground="#777777" HorizontalAlignment="Center" Margin="0,10,0,0"/> |
|||
<TextBlock Text="{Binding AutoLastInspectionTimeText}" FontSize="14" Foreground="#777777" HorizontalAlignment="Center" Margin="0,5,0,0"/> |
|||
</StackPanel> |
|||
</Border> |
|||
</Grid> |
|||
</Viewbox> |
|||
</TabItem> |
|||
</TabControl> |
|||
|
|||
<!-- [4] 실시간 통신 로그 창 (하단 가변) --> |
|||
<GroupBox Grid.Row="4" Header="실시간 통신 로그 (HEX)" Margin="10,0,10,10" Height="200" |
|||
Visibility="{Binding LogGroupVisibility}"> |
|||
<Grid> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<!-- I/O 및 시스템 로그 --> |
|||
<GroupBox Grid.Column="0" Header="I/O 보드 및 시스템 로그" Margin="0,0,5,0"> |
|||
<ScrollViewer VerticalScrollBarVisibility="Auto"> |
|||
<TextBox Text="{Binding IOLogText, Mode=OneWay}" Background="#1E1E1E" Foreground="#4CAF50" FontFamily="Consolas" IsReadOnly="True" BorderThickness="0" TextChanged="TextBox_TextChanged"/> |
|||
</ScrollViewer> |
|||
</GroupBox> |
|||
|
|||
<!-- 압력 센서 로그 --> |
|||
<GroupBox Grid.Column="1" Header="압력 센서 로그" Margin="5,0,0,0"> |
|||
<ScrollViewer VerticalScrollBarVisibility="Auto"> |
|||
<TextBox Text="{Binding PrLogText, Mode=OneWay}" Background="#1E1E1E" Foreground="#03A9F4" FontFamily="Consolas" IsReadOnly="True" BorderThickness="0" TextChanged="TextBox_TextChanged"/> |
|||
</ScrollViewer> |
|||
</GroupBox> |
|||
</Grid> |
|||
</GroupBox> |
|||
</Grid> |
|||
</Window> |
|||
@ -0,0 +1,66 @@ |
|||
using System.Windows; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Input; |
|||
using jig_test.ViewModels; |
|||
|
|||
namespace jig_test.Views |
|||
{ |
|||
public partial class MainWindow : Window |
|||
{ |
|||
private MainViewModel _viewModel; |
|||
|
|||
public MainWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
_viewModel = new MainViewModel(); |
|||
this.DataContext = _viewModel; |
|||
|
|||
// 윈도우가 닫힐 때 자원 해제 보장
|
|||
this.Closed += (s, e) => _viewModel?.Dispose(); |
|||
} |
|||
|
|||
private void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) |
|||
{ |
|||
if (e.ChangedButton == MouseButton.Left) |
|||
this.DragMove(); |
|||
} |
|||
|
|||
private void BtnMinimize_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
this.WindowState = WindowState.Minimized; |
|||
} |
|||
|
|||
private void BtnMaximizeRestore_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
if (this.WindowState == WindowState.Maximized) |
|||
this.WindowState = WindowState.Normal; |
|||
else |
|||
this.WindowState = WindowState.Maximized; |
|||
} |
|||
|
|||
private void BtnClose_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
_viewModel?.Dispose(); |
|||
Application.Current.Shutdown(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// TextBox의 Text가 바인딩으로 변경될 때 항상 가장 아래로 스크롤되도록 처리 (UI전용 편의기능)
|
|||
/// </summary>
|
|||
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) |
|||
{ |
|||
if (sender is TextBox tb && tb.Parent is ScrollViewer sv) |
|||
{ |
|||
bool isAtBottom = sv.VerticalOffset >= sv.ScrollableHeight - 5.0; |
|||
if (isAtBottom) |
|||
{ |
|||
sv.ScrollToEnd(); |
|||
} |
|||
} |
|||
else if (sender is TextBox tbx) |
|||
{ |
|||
tbx.ScrollToEnd(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
<Window x:Class="jig_test.Views.ParameterWindow" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
Title="테스트 파라미터" Height="320" Width="300" |
|||
WindowStartupLocation="CenterOwner" ResizeMode="NoResize" |
|||
Background="#F4F4F9"> |
|||
<Grid Margin="20"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="*"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<TextBlock Text="테스트 파라미터 설정" FontSize="18" FontWeight="Bold" Margin="0,0,0,15"/> |
|||
|
|||
<StackPanel Grid.Row="1"> |
|||
<GroupBox Header="공정 및 판정 기준" Padding="10"> |
|||
<StackPanel> |
|||
<!-- 유지 시간 설정 --> |
|||
<Grid Margin="0,5"> |
|||
<TextBlock Text="유지 시간 (초):" VerticalAlignment="Center"/> |
|||
<TextBox Text="{Binding Config.HoldTime}" Width="80" HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="NumberValidationTextBox" InputMethod.IsInputMethodEnabled="False"/> |
|||
</Grid> |
|||
<!-- 허용 오차 설정 --> |
|||
<Grid Margin="0,5"> |
|||
<TextBlock Text="허용 오차 (bar):" VerticalAlignment="Center"/> |
|||
<TextBox Text="{Binding Config.AllowedErrorRange, StringFormat=F1}" Width="80" HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="DecimalValidationTextBox" InputMethod.IsInputMethodEnabled="False"/> |
|||
</Grid> |
|||
<!-- 자동검사 목표 압력 설정 --> |
|||
<Grid Margin="0,5"> |
|||
<TextBlock Text="자동 검사 압력 (bar):" VerticalAlignment="Center"/> |
|||
<TextBox Text="{Binding Config.AutoTestTargetPressure, StringFormat=F1}" Width="80" HorizontalAlignment="Right" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="DecimalValidationTextBox" InputMethod.IsInputMethodEnabled="False"/> |
|||
</Grid> |
|||
</StackPanel> |
|||
</GroupBox> |
|||
</StackPanel> |
|||
|
|||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,15,0,0"> |
|||
<Button Content="저장" Command="{Binding SaveCommand}" Width="80" Height="35" Background="#0078D7" Foreground="White" BorderThickness="0" Margin="0,0,10,0"/> |
|||
<Button Content="취소" Command="{Binding CancelCommand}" Width="60" Height="35"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
</Window> |
|||
@ -0,0 +1,50 @@ |
|||
using System.Text.RegularExpressions; |
|||
using System.Windows; |
|||
using System.Windows.Input; |
|||
|
|||
namespace jig_test.Views |
|||
{ |
|||
public partial class ParameterWindow : Window |
|||
{ |
|||
public ParameterWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e) |
|||
{ |
|||
Regex regex = new Regex("[^0-9]+"); |
|||
e.Handled = regex.IsMatch(e.Text); |
|||
} |
|||
|
|||
private void DecimalValidationTextBox(object sender, TextCompositionEventArgs e) |
|||
{ |
|||
Regex regex = new Regex("^[0-9.]+$"); |
|||
if (!regex.IsMatch(e.Text)) |
|||
{ |
|||
e.Handled = true; |
|||
return; |
|||
} |
|||
|
|||
if (sender is System.Windows.Controls.TextBox textBox) |
|||
{ |
|||
if (e.Text == "." && textBox.Text.Contains(".")) |
|||
{ |
|||
e.Handled = true; |
|||
return; |
|||
} |
|||
|
|||
// Check decimal places
|
|||
string newText = textBox.Text.Insert(textBox.CaretIndex, e.Text); |
|||
if (newText.Contains(".")) |
|||
{ |
|||
int decimalIndex = newText.IndexOf("."); |
|||
if (newText.Length - decimalIndex - 1 > 1) |
|||
{ |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
<Window x:Class="jig_test.Views.SettingsWindow" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
Title="통신 설정" Height="480" Width="480" |
|||
WindowStartupLocation="CenterOwner" ResizeMode="NoResize" |
|||
Background="#F9F9F9"> |
|||
|
|||
<Grid Margin="20"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="*"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<TextBlock Text="시스템 통신 상세 설정" FontSize="20" FontWeight="Bold" Margin="0,0,0,20" Foreground="#333333"/> |
|||
|
|||
<StackPanel Grid.Row="1"> |
|||
<!-- 1. IO 보드 개별 설정 --> |
|||
<GroupBox Padding="10" Margin="0,0,0,15" BorderBrush="#999999"> |
|||
<GroupBox.Header> |
|||
<CheckBox Content="1. I/O 보드 통신 사용" IsChecked="{Binding Config.IOEnabled}" FontWeight="Bold" Foreground="#333333"/> |
|||
</GroupBox.Header> |
|||
<Grid> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="60"/> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="60"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Text="포트:" VerticalAlignment="Center"/> |
|||
<ComboBox Grid.Column="1" Margin="5,0" ItemsSource="{Binding AvailablePorts}" SelectedItem="{Binding Config.PortName}"/> |
|||
<TextBlock Text="속도:" Grid.Column="2" VerticalAlignment="Center" Margin="10,0,0,0"/> |
|||
<ComboBox Grid.Column="3" Margin="5,0" ItemsSource="{Binding AvailableBaudRates}" SelectedItem="{Binding Config.BaudRate}"/> |
|||
</Grid> |
|||
</GroupBox> |
|||
|
|||
<!-- 2. 압력 센서 개별 설정 --> |
|||
<GroupBox Padding="10" Margin="0,0,0,15" BorderBrush="#999999"> |
|||
<GroupBox.Header> |
|||
<CheckBox Content="2. 압력 센서 통신 사용" IsChecked="{Binding Config.PressureEnabled}" FontWeight="Bold" Foreground="#333333"/> |
|||
</GroupBox.Header> |
|||
<Grid> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="60"/> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="60"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Text="포트:" VerticalAlignment="Center"/> |
|||
<ComboBox Grid.Column="1" Margin="5,0" ItemsSource="{Binding AvailablePorts}" SelectedItem="{Binding Config.PressurePortName}"/> |
|||
<TextBlock Text="속도:" Grid.Column="2" VerticalAlignment="Center" Margin="10,0,0,0"/> |
|||
<ComboBox Grid.Column="3" Margin="5,0" ItemsSource="{Binding AvailableBaudRates}" SelectedItem="{Binding Config.PressureBaudRate}"/> |
|||
</Grid> |
|||
</GroupBox> |
|||
|
|||
<!-- 기기 ID 설정 --> |
|||
<GroupBox Header="기기 식별(ID) 설정" Padding="10" Background="#F0F0F0"> |
|||
<Grid> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="Auto"/> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="Auto"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Text="I/O ID:" VerticalAlignment="Center"/> |
|||
<TextBox Grid.Column="1" Margin="10,0" Text="{Binding Config.IOStationId}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="NumberValidationTextBox" InputMethod.IsInputMethodEnabled="False"/> |
|||
<TextBlock Text="압력 ID:" Grid.Column="2" VerticalAlignment="Center"/> |
|||
<TextBox Grid.Column="3" Margin="10,0" Text="{Binding Config.PressureSlaveId}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" PreviewTextInput="NumberValidationTextBox" InputMethod.IsInputMethodEnabled="False"/> |
|||
</Grid> |
|||
</GroupBox> |
|||
</StackPanel> |
|||
|
|||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0"> |
|||
<Button Content="설정 저장 및 적용" Command="{Binding SaveCommand}" Width="120" Height="40" Background="#0078D7" Foreground="White" BorderThickness="0" Margin="0,0,10,0"/> |
|||
<Button Content="취소" Command="{Binding CancelCommand}" Width="80" Height="40"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
</Window> |
|||
@ -0,0 +1,21 @@ |
|||
using System.Text.RegularExpressions; |
|||
using System.Windows; |
|||
using System.Windows.Input; |
|||
using jig_test.ViewModels; |
|||
|
|||
namespace jig_test.Views |
|||
{ |
|||
public partial class SettingsWindow : Window |
|||
{ |
|||
public SettingsWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
} |
|||
|
|||
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e) |
|||
{ |
|||
Regex regex = new Regex("[^0-9]+"); |
|||
e.Handled = regex.IsMatch(e.Text); |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<AppConfig xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
|||
<PortName>COM9</PortName> |
|||
<BaudRate>9600</BaudRate> |
|||
<PressurePortName>COM10</PressurePortName> |
|||
<PressureBaudRate>9600</PressureBaudRate> |
|||
<IOEnabled>true</IOEnabled> |
|||
<IOStationId>2</IOStationId> |
|||
<PressureEnabled>true</PressureEnabled> |
|||
<PressureSlaveId>1</PressureSlaveId> |
|||
<HoldTime>10</HoldTime> |
|||
<AllowedErrorRange>0.1</AllowedErrorRange> |
|||
<AutoTestTargetPressure>0.5</AutoTestTargetPressure> |
|||
<IsLogVisible>true</IsLogVisible> |
|||
</AppConfig> |
|||
Binary file not shown.
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<configuration> |
|||
<startup> |
|||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> |
|||
</startup> |
|||
</configuration> |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<AppConfig xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> |
|||
<PortName>COM9</PortName> |
|||
<BaudRate>9600</BaudRate> |
|||
<PressurePortName>COM10</PressurePortName> |
|||
<PressureBaudRate>9600</PressureBaudRate> |
|||
<IOEnabled>true</IOEnabled> |
|||
<IOStationId>2</IOStationId> |
|||
<PressureEnabled>true</PressureEnabled> |
|||
<PressureSlaveId>1</PressureSlaveId> |
|||
<HoldTime>5</HoldTime> |
|||
<AllowedErrorRange>0.3</AllowedErrorRange> |
|||
<AutoTestTargetPressure>4</AutoTestTargetPressure> |
|||
<IsLogVisible>true</IsLogVisible> |
|||
</AppConfig> |
|||
Binary file not shown.
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<configuration> |
|||
<startup> |
|||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> |
|||
</startup> |
|||
</configuration> |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,6 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<configuration> |
|||
<startup> |
|||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> |
|||
</startup> |
|||
</configuration> |
|||
Binary file not shown.
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<OutputType>WinExe</OutputType> |
|||
<TargetFramework>net472</TargetFramework> |
|||
<UseWPF>true</UseWPF> |
|||
<RootNamespace>jig_test</RootNamespace> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="NModbus" Version="3.0.81" /> |
|||
<PackageReference Include="NModbus.Serial" Version="3.0.81" /> |
|||
<PackageReference Include="System.IO.Ports" Version="10.0.4" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<ProjectView>ShowAllFiles</ProjectView> |
|||
<_LastSelectedProfileId>C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId> |
|||
</PropertyGroup> |
|||
</Project> |
|||
@ -0,0 +1,4 @@ |
|||
// <autogenerated />
|
|||
using System; |
|||
using System.Reflection; |
|||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] |
|||
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||
cc6129068e0a7223a9d1769f746f81782b0fd99b060fc36bafdc08ca0ce2cd77 |
|||
@ -0,0 +1,4 @@ |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\jig_test.csproj.AssemblyReference.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\jig_test.Properties.Resources.resources |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\jig_test.csproj.GenerateResource.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\jig_test.csproj.CoreCompileInputs.cache |
|||
Binary file not shown.
@ -0,0 +1,4 @@ |
|||
// <autogenerated />
|
|||
using System; |
|||
using System.Reflection; |
|||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] |
|||
@ -0,0 +1,70 @@ |
|||
#pragma checksum "..\..\..\App.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "0AE025BED62770D1ECFC17C1BA45F4498B5E1690"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// This code was generated by a tool.
|
|||
//
|
|||
// Changes to this file may cause incorrect behavior and will be lost if
|
|||
// the code is regenerated.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
using jig_test; |
|||
|
|||
|
|||
namespace jig_test { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// App
|
|||
/// </summary>
|
|||
public partial class App : System.Windows.Application { |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.2.0")] |
|||
public void InitializeComponent() { |
|||
|
|||
#line 5 "..\..\..\App.xaml"
|
|||
this.StartupUri = new System.Uri("Views/MainWindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Application Entry Point.
|
|||
/// </summary>
|
|||
[System.STAThreadAttribute()] |
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.2.0")] |
|||
public static void Main() { |
|||
jig_test.App app = new jig_test.App(); |
|||
app.InitializeComponent(); |
|||
app.Run(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,71 @@ |
|||
#pragma checksum "..\..\..\App.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "0AE025BED62770D1ECFC17C1BA45F4498B5E1690"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
using jig_test; |
|||
|
|||
|
|||
namespace jig_test { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// App
|
|||
/// </summary>
|
|||
public partial class App : System.Windows.Application { |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.2.0")] |
|||
public void InitializeComponent() { |
|||
|
|||
#line 5 "..\..\..\App.xaml"
|
|||
this.StartupUri = new System.Uri("Views/MainWindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Application Entry Point.
|
|||
/// </summary>
|
|||
[System.STAThreadAttribute()] |
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.2.0")] |
|||
public static void Main() { |
|||
jig_test.App app = new jig_test.App(); |
|||
app.InitializeComponent(); |
|||
app.Run(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Binary file not shown.
@ -0,0 +1,62 @@ |
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
namespace XamlGeneratedNamespace { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// GeneratedInternalTypeHelper
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
public sealed class GeneratedInternalTypeHelper : System.Windows.Markup.InternalTypeHelper { |
|||
|
|||
/// <summary>
|
|||
/// CreateInstance
|
|||
/// </summary>
|
|||
protected override object CreateInstance(System.Type type, System.Globalization.CultureInfo culture) { |
|||
return System.Activator.CreateInstance(type, ((System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic) |
|||
| (System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.CreateInstance)), null, null, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// GetPropertyValue
|
|||
/// </summary>
|
|||
protected override object GetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, System.Globalization.CultureInfo culture) { |
|||
return propertyInfo.GetValue(target, System.Reflection.BindingFlags.Default, null, null, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SetPropertyValue
|
|||
/// </summary>
|
|||
protected override void SetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, object value, System.Globalization.CultureInfo culture) { |
|||
propertyInfo.SetValue(target, value, System.Reflection.BindingFlags.Default, null, null, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// CreateDelegate
|
|||
/// </summary>
|
|||
protected override System.Delegate CreateDelegate(System.Type delegateType, object target, string handler) { |
|||
return ((System.Delegate)(target.GetType().InvokeMember("_CreateDelegate", (System.Reflection.BindingFlags.InvokeMethod |
|||
| (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)), null, target, new object[] { |
|||
delegateType, |
|||
handler}, null))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// AddEventHandler
|
|||
/// </summary>
|
|||
protected override void AddEventHandler(System.Reflection.EventInfo eventInfo, object target, System.Delegate handler) { |
|||
eventInfo.AddEventHandler(target, handler); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,62 @@ |
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
namespace XamlGeneratedNamespace { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// GeneratedInternalTypeHelper
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
public sealed class GeneratedInternalTypeHelper : System.Windows.Markup.InternalTypeHelper { |
|||
|
|||
/// <summary>
|
|||
/// CreateInstance
|
|||
/// </summary>
|
|||
protected override object CreateInstance(System.Type type, System.Globalization.CultureInfo culture) { |
|||
return System.Activator.CreateInstance(type, ((System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic) |
|||
| (System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.CreateInstance)), null, null, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// GetPropertyValue
|
|||
/// </summary>
|
|||
protected override object GetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, System.Globalization.CultureInfo culture) { |
|||
return propertyInfo.GetValue(target, System.Reflection.BindingFlags.Default, null, null, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SetPropertyValue
|
|||
/// </summary>
|
|||
protected override void SetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, object value, System.Globalization.CultureInfo culture) { |
|||
propertyInfo.SetValue(target, value, System.Reflection.BindingFlags.Default, null, null, culture); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// CreateDelegate
|
|||
/// </summary>
|
|||
protected override System.Delegate CreateDelegate(System.Type delegateType, object target, string handler) { |
|||
return ((System.Delegate)(target.GetType().InvokeMember("_CreateDelegate", (System.Reflection.BindingFlags.InvokeMethod |
|||
| (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)), null, target, new object[] { |
|||
delegateType, |
|||
handler}, null))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// AddEventHandler
|
|||
/// </summary>
|
|||
protected override void AddEventHandler(System.Reflection.EventInfo eventInfo, object target, System.Delegate handler) { |
|||
eventInfo.AddEventHandler(target, handler); |
|||
} |
|||
} |
|||
} |
|||
|
|||
Binary file not shown.
@ -0,0 +1,119 @@ |
|||
#pragma checksum "..\..\..\..\Views\MainWindow.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "BB66098EC741B9EED8840DC73E391B14C0636A15"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// This code was generated by a tool.
|
|||
//
|
|||
// Changes to this file may cause incorrect behavior and will be lost if
|
|||
// the code is regenerated.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
using jig_test.ViewModels; |
|||
using jig_test.Views; |
|||
|
|||
|
|||
namespace jig_test.Views { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// MainWindow
|
|||
/// </summary>
|
|||
public partial class MainWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector { |
|||
|
|||
private bool _contentLoaded; |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
public void InitializeComponent() { |
|||
if (_contentLoaded) { |
|||
return; |
|||
} |
|||
_contentLoaded = true; |
|||
System.Uri resourceLocater = new System.Uri("/jig_test;component/views/mainwindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line 1 "..\..\..\..\Views\MainWindow.xaml"
|
|||
System.Windows.Application.LoadComponent(this, resourceLocater); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] |
|||
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) { |
|||
switch (connectionId) |
|||
{ |
|||
case 1: |
|||
|
|||
#line 18 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((jig_test.Views.MainWindow)(target)).MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.Window_MouseLeftButtonDown); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 2: |
|||
|
|||
#line 33 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.BtnMaximizeRestore_Click); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 3: |
|||
|
|||
#line 34 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.BtnClose_Click); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 4: |
|||
|
|||
#line 269 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).TextChanged += new System.Windows.Controls.TextChangedEventHandler(this.TextBox_TextChanged); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 5: |
|||
|
|||
#line 276 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).TextChanged += new System.Windows.Controls.TextChangedEventHandler(this.TextBox_TextChanged); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
} |
|||
this._contentLoaded = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,120 @@ |
|||
#pragma checksum "..\..\..\..\Views\MainWindow.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "BB66098EC741B9EED8840DC73E391B14C0636A15"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
using jig_test.ViewModels; |
|||
using jig_test.Views; |
|||
|
|||
|
|||
namespace jig_test.Views { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// MainWindow
|
|||
/// </summary>
|
|||
public partial class MainWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector { |
|||
|
|||
private bool _contentLoaded; |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
public void InitializeComponent() { |
|||
if (_contentLoaded) { |
|||
return; |
|||
} |
|||
_contentLoaded = true; |
|||
System.Uri resourceLocater = new System.Uri("/jig_test;component/views/mainwindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line 1 "..\..\..\..\Views\MainWindow.xaml"
|
|||
System.Windows.Application.LoadComponent(this, resourceLocater); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] |
|||
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) { |
|||
switch (connectionId) |
|||
{ |
|||
case 1: |
|||
|
|||
#line 18 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((jig_test.Views.MainWindow)(target)).MouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(this.Window_MouseLeftButtonDown); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 2: |
|||
|
|||
#line 33 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.BtnMaximizeRestore_Click); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 3: |
|||
|
|||
#line 34 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.BtnClose_Click); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 4: |
|||
|
|||
#line 269 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).TextChanged += new System.Windows.Controls.TextChangedEventHandler(this.TextBox_TextChanged); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 5: |
|||
|
|||
#line 276 "..\..\..\..\Views\MainWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).TextChanged += new System.Windows.Controls.TextChangedEventHandler(this.TextBox_TextChanged); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
} |
|||
this._contentLoaded = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
Binary file not shown.
@ -0,0 +1,101 @@ |
|||
#pragma checksum "..\..\..\..\Views\ParameterWindow.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "E3C6CAECA907BCA8157D7345CF8F594D3E1F783F"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// This code was generated by a tool.
|
|||
//
|
|||
// Changes to this file may cause incorrect behavior and will be lost if
|
|||
// the code is regenerated.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
|
|||
|
|||
namespace jig_test.Views { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// ParameterWindow
|
|||
/// </summary>
|
|||
public partial class ParameterWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector { |
|||
|
|||
private bool _contentLoaded; |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
public void InitializeComponent() { |
|||
if (_contentLoaded) { |
|||
return; |
|||
} |
|||
_contentLoaded = true; |
|||
System.Uri resourceLocater = new System.Uri("/jig_test;component/views/parameterwindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line 1 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
System.Windows.Application.LoadComponent(this, resourceLocater); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] |
|||
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) { |
|||
switch (connectionId) |
|||
{ |
|||
case 1: |
|||
|
|||
#line 22 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.NumberValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 2: |
|||
|
|||
#line 27 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.DecimalValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 3: |
|||
|
|||
#line 32 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.DecimalValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
} |
|||
this._contentLoaded = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,102 @@ |
|||
#pragma checksum "..\..\..\..\Views\ParameterWindow.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "E3C6CAECA907BCA8157D7345CF8F594D3E1F783F"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
|
|||
|
|||
namespace jig_test.Views { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// ParameterWindow
|
|||
/// </summary>
|
|||
public partial class ParameterWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector { |
|||
|
|||
private bool _contentLoaded; |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
public void InitializeComponent() { |
|||
if (_contentLoaded) { |
|||
return; |
|||
} |
|||
_contentLoaded = true; |
|||
System.Uri resourceLocater = new System.Uri("/jig_test;V1.0.0.0;component/views/parameterwindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line 1 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
System.Windows.Application.LoadComponent(this, resourceLocater); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] |
|||
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) { |
|||
switch (connectionId) |
|||
{ |
|||
case 1: |
|||
|
|||
#line 22 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.NumberValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 2: |
|||
|
|||
#line 27 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.DecimalValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 3: |
|||
|
|||
#line 32 "..\..\..\..\Views\ParameterWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.DecimalValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
} |
|||
this._contentLoaded = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
Binary file not shown.
@ -0,0 +1,94 @@ |
|||
#pragma checksum "..\..\..\..\Views\SettingsWindow.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "0174FC1EEC33D4FB7218476DF7162E76B36E45C3"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
|
|||
|
|||
namespace jig_test.Views { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// SettingsWindow
|
|||
/// </summary>
|
|||
public partial class SettingsWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector { |
|||
|
|||
private bool _contentLoaded; |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
public void InitializeComponent() { |
|||
if (_contentLoaded) { |
|||
return; |
|||
} |
|||
_contentLoaded = true; |
|||
System.Uri resourceLocater = new System.Uri("/jig_test;component/views/settingswindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line 1 "..\..\..\..\Views\SettingsWindow.xaml"
|
|||
System.Windows.Application.LoadComponent(this, resourceLocater); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] |
|||
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) { |
|||
switch (connectionId) |
|||
{ |
|||
case 1: |
|||
|
|||
#line 66 "..\..\..\..\Views\SettingsWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.NumberValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 2: |
|||
|
|||
#line 68 "..\..\..\..\Views\SettingsWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.NumberValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
} |
|||
this._contentLoaded = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,94 @@ |
|||
#pragma checksum "..\..\..\..\Views\SettingsWindow.xaml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "0174FC1EEC33D4FB7218476DF7162E76B36E45C3"
|
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|||
// 런타임 버전:4.0.30319.42000
|
|||
//
|
|||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|||
// 이러한 변경 내용이 손실됩니다.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Windows; |
|||
using System.Windows.Automation; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Controls.Primitives; |
|||
using System.Windows.Controls.Ribbon; |
|||
using System.Windows.Data; |
|||
using System.Windows.Documents; |
|||
using System.Windows.Ink; |
|||
using System.Windows.Input; |
|||
using System.Windows.Markup; |
|||
using System.Windows.Media; |
|||
using System.Windows.Media.Animation; |
|||
using System.Windows.Media.Effects; |
|||
using System.Windows.Media.Imaging; |
|||
using System.Windows.Media.Media3D; |
|||
using System.Windows.Media.TextFormatting; |
|||
using System.Windows.Navigation; |
|||
using System.Windows.Shapes; |
|||
using System.Windows.Shell; |
|||
|
|||
|
|||
namespace jig_test.Views { |
|||
|
|||
|
|||
/// <summary>
|
|||
/// SettingsWindow
|
|||
/// </summary>
|
|||
public partial class SettingsWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector { |
|||
|
|||
private bool _contentLoaded; |
|||
|
|||
/// <summary>
|
|||
/// InitializeComponent
|
|||
/// </summary>
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
public void InitializeComponent() { |
|||
if (_contentLoaded) { |
|||
return; |
|||
} |
|||
_contentLoaded = true; |
|||
System.Uri resourceLocater = new System.Uri("/jig_test;V1.0.0.0;component/views/settingswindow.xaml", System.UriKind.Relative); |
|||
|
|||
#line 1 "..\..\..\..\Views\SettingsWindow.xaml"
|
|||
System.Windows.Application.LoadComponent(this, resourceLocater); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
} |
|||
|
|||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "10.0.5.0")] |
|||
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] |
|||
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] |
|||
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) { |
|||
switch (connectionId) |
|||
{ |
|||
case 1: |
|||
|
|||
#line 66 "..\..\..\..\Views\SettingsWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.NumberValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
case 2: |
|||
|
|||
#line 68 "..\..\..\..\Views\SettingsWindow.xaml"
|
|||
((System.Windows.Controls.TextBox)(target)).PreviewTextInput += new System.Windows.Input.TextCompositionEventHandler(this.NumberValidationTextBox); |
|||
|
|||
#line default
|
|||
#line hidden
|
|||
return; |
|||
} |
|||
this._contentLoaded = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,22 @@ |
|||
//------------------------------------------------------------------------------
|
|||
// <auto-generated>
|
|||
// This code was generated by a tool.
|
|||
//
|
|||
// Changes to this file may cause incorrect behavior and will be lost if
|
|||
// the code is regenerated.
|
|||
// </auto-generated>
|
|||
//------------------------------------------------------------------------------
|
|||
|
|||
using System; |
|||
using System.Reflection; |
|||
|
|||
[assembly: System.Reflection.AssemblyCompanyAttribute("jig_test")] |
|||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] |
|||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] |
|||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] |
|||
[assembly: System.Reflection.AssemblyProductAttribute("jig_test")] |
|||
[assembly: System.Reflection.AssemblyTitleAttribute("jig_test")] |
|||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] |
|||
|
|||
// MSBuild WriteCodeFragment 클래스에서 생성되었습니다.
|
|||
|
|||
@ -0,0 +1 @@ |
|||
cf6e0f4558e3ad73f7ef85ec3a319f36dd7e59797e7ad616ee564fab92086615 |
|||
@ -0,0 +1,8 @@ |
|||
is_global = true |
|||
build_property.RootNamespace = jig_test |
|||
build_property.ProjectDir = C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\ |
|||
build_property.EnableComHosting = |
|||
build_property.EnableGeneratedComInterfaceComImportInterop = |
|||
build_property.CsWinRTUseWindowsUIXamlProjections = false |
|||
build_property.EffectiveAnalysisLevelStyle = |
|||
build_property.EnableCodeStyleSeverity = |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||
ebfe5d991935b785837076994f976d8e6dd1ac5c2bdaefa0c9cd54ade779c558 |
|||
@ -0,0 +1,27 @@ |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\bin\Debug\net472\jig_test.exe.config |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\bin\Debug\net472\jig_test.exe |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\bin\Debug\net472\jig_test.pdb |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.csproj.AssemblyReference.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\Views\MainWindow.g.cs |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\App.g.cs |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test_MarkupCompile.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test_MarkupCompile.lref |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\Views\MainWindow.baml |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.g.resources |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.Properties.Resources.resources |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.csproj.GenerateResource.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.GeneratedMSBuildEditorConfig.editorconfig |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.AssemblyInfoInputs.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.AssemblyInfo.cs |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.csproj.CoreCompileInputs.cache |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.exe |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.pdb |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\bin\Debug\net472\NModbus.dll |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\bin\Debug\net472\NModbus.Serial.dll |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\bin\Debug\net472\System.IO.Ports.dll |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\jig_test.csproj.Up2Date |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\Views\SettingsWindow.baml |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\Views\SettingsWindow.g.cs |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\Views\ParameterWindow.baml |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\Views\ParameterWindow.g.cs |
|||
C:\Users\COMPUTER1\Desktop\mobi\jig_test\jig_test\obj\Debug\net472\GeneratedInternalTypeHelper.g.cs |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue