@ -0,0 +1,4 @@ |
|||||
|
# 프로젝트 개발 지침 |
||||
|
|
||||
|
- **개발 전 단계**: 작업이나 구현 계획을 세우기 전, 반드시 `ob/` 폴더 내에 있는 시스템 설계 문서 및 관련 문서들을 상세히 읽어 요구사항과 설계 방향을 확인해야 합니다. |
||||
|
- **개발 후 단계**: 코드 변경 또는 신규 기능 개발 완료 후, 작업 결과와 관련 설계 변경점을 `ob/` 폴더 내의 해당 문서에 성실히 업데이트하고 기록해야 합니다. |
||||
@ -0,0 +1,24 @@ |
|||||
|
; ===================================================================== |
||||
|
; Mobi Leak Test System - 데이터베이스 및 로그인 설정 파일 (Database.ini) |
||||
|
; ===================================================================== |
||||
|
|
||||
|
; [데이터베이스 접속 상세 설정] |
||||
|
; Db_Server : 중앙 MSSQL 데이터베이스 서버의 IP 주소 또는 호스트명 |
||||
|
; Db_Name : 검사 데이터를 저장할 대상 데이터베이스 이름 |
||||
|
; Db_User : 데이터베이스 접속 계정 ID (예: sa) |
||||
|
; Db_Password: 데이터베이스 접속 계정의 비밀번호 |
||||
|
Db_Server=192.168.0.20 |
||||
|
Db_Name=NE1aW_PT_SENSOR |
||||
|
Db_User=sa |
||||
|
Db_Password=sa1234 |
||||
|
|
||||
|
|
||||
|
[Login] |
||||
|
; Procedure: 로그인 검증에 사용할 DB 저장 프로시저명입니다. (공란일 경우 기본 테이블 조회 쿼리 작동) |
||||
|
Procedure=dbo.CheckOperator |
||||
|
|
||||
|
; ProcessName: 로그인 검증 시 전달할 공정 이름입니다. |
||||
|
ProcessName=EOL |
||||
|
|
||||
|
; OfflinePreview: 오프라인 모드 활성화 여부 (true = DB 연결 없이 로그인 통과, false = 실제 DB 인증) |
||||
|
OfflinePreview=false |
||||
@ -0,0 +1,99 @@ |
|||||
|
# 6. 측정 데이터 조회 (Data View) |
||||
|
|
||||
|
[← 메뉴얼 목차로 돌아가기](사용자_메뉴얼.md) |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
하단 메뉴에서 **Data (측정 데이터)** 버튼을 클릭하면 표시됩니다. 다시 클릭하면 메인 화면으로 복귀합니다. |
||||
|
![[Pasted image 20260624104558.png]] |
||||
|
## 6.1 화면 구성 |
||||
|
|
||||
|
``` |
||||
|
┌───────────────────────────────────────────────────────────────────────────────────────────┐ |
||||
|
│ INSPECT DATA LIST (측정 기록 조회) │ ← 타이틀 바 |
||||
|
├───────────────────────────────────────────────────────────────────────────────────────────┤ |
||||
|
│ [조회기간] 2026-05-01 ~ 2026-05-08 [판정 결과] 전체 │ |
||||
|
│ [ID 검색] [작업자] [Lot No] [모델] [Search] [CSV] │ ← 2행 필터 영역 |
||||
|
├──────┬──────┬───────┬────────┬──────┬─────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┼ |
||||
|
│검사일│검사시│ IC SN │ PCB │메이커│모델 │변형1 │변형2 │작업자│라인 │Lot No│지그 │... │ ← 데이터 그리드 |
||||
|
├──────┼──────┼───────┼────────┼──────┼─────┼──────┼──────┼──────┼──────┼──────┼──────┼──────┼ |
||||
|
│ ... │ ... │ABC123 │ ABC123 │ MOBI │ EOL │ A │ B │ admin│ 1 │LOT001│ 1 │... │ |
||||
|
├──────┴──────┴───────┴────────┴──────┴─────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┼ |
||||
|
│ [ Close ] │ |
||||
|
└───────────────────────────────────────────────────────────────────────────────────────────┘ |
||||
|
``` |
||||
|
|
||||
|
## 6.2 필터 조건 상세 |
||||
|
|
||||
|
### 1행: 공통 기본 필터 |
||||
|
|
||||
|
| 항목 | UI 타입 | 기본값 | 설명 | |
||||
|
|------|---------|--------|------| |
||||
|
| **조회 기간** | DatePicker (달력) x 2 | 오늘로부터 7일 전 ~ 오늘 | 조회 대상 날짜 범위 설정 (달력 확대 표시 적용) | |
||||
|
| **판정 결과** | 드롭다운 ComboBox | `[전체]` / `OK` / `NG` | 판정 결과에 따른 필터링 (기본값: 전체) | |
||||
|
|
||||
|
### 2행: 상세 검색 필터 (DB 스키마 필드 교차 검색) |
||||
|
|
||||
|
| 항목 | UI 타입 | 설명 | 기본값 | |
||||
|
|------|---------|------|--------| |
||||
|
| **ID 검색** | TextBox (텍스트 입력) | `IC_SN` 또는 `PCB_Barcode` 컬럼에 대한 부분 일치 (LIKE) 검색 | (빈 값 = 필터 제외) | |
||||
|
| **작업자** | TextBox (텍스트 입력) | 작업자 ID (`Operator`) 컬럼에 대한 부분 일치 검색 | (빈 값 = 필터 제외) | |
||||
|
| **Lot No** | TextBox (텍스트 입력) | 랏 번호 (`Lot_No`) 컬럼에 대한 부분 일치 검색 | (빈 값 = 필터 제외) | |
||||
|
| **모델** | TextBox (텍스트 입력) | 모델명 (`Model`) 컬럼에 대한 부분 일치 검색 | (빈 값 = 필터 제외) | |
||||
|
|
||||
|
## 6.3 조회 및 내보내기 버튼 |
||||
|
|
||||
|
| 버튼 | 색상 | 동작 | |
||||
|
|------|------|------| |
||||
|
| **Search** | 파란색 계열 | 설정된 2행의 모든 필터 조건으로 SQL Server EOL DB(혹은 CSV)를 부분일치 검색하여 그리드에 표시합니다. | |
||||
|
| **CSV** | 녹색 계열 | 검색 결과를 새 CSV 파일(16개 전체 컬럼 사양)로 저장합니다. | |
||||
|
|
||||
|
### Search 버튼 동작 상세 |
||||
|
1. DB 연결 활성화 시 SQL Server `dbo.EOL` 테이블에서 Like 조건 쿼리 실행. |
||||
|
2. DB 연결 실패/주소 미설정 시 로컬 CSV 파일에서 인메모리 부분일치 검색 처리 (구형 8컬럼 파일 하위 호환). |
||||
|
3. 검색 결과가 없으면 "해당 조건의 데이터가 없습니다." 알림 표시. |
||||
|
|
||||
|
### CSV 버튼 동작 상세 |
||||
|
1. 검색 결과가 없으면 "저장할 데이터가 없습니다." 알림 표시. |
||||
|
2. 파일 저장 대화상자 표시 (기본 파일명: `InspectData_yyyyMMdd_HHmmss.csv`). |
||||
|
3. 저장 완료 시 "CSV 파일로 저장되었습니다." 알림 표시. |
||||
|
|
||||
|
## 6.4 데이터 그리드 컬럼 |
||||
|
|
||||
|
| 컬럼 헤더명 | 바인딩 프로퍼티 | 정렬 | 설명 | |
||||
|
|------|------|------|------| |
||||
|
| **검사일자** | `InspectDate` | 좌측 | 검사 일자 (yyyy-MM-dd) | |
||||
|
| **검사시간** | `InspectTime` | 좌측 | 검사 시각 (HH:mm:ss) | |
||||
|
| **IC SN (제품ID)** | `ProductId` (`IcSn`) | 좌측 | 제품 고유 IC SN 시리얼 번호 | |
||||
|
| **PCB 바코드** | `PcbBarcode` | 좌측 | PCB 바코드 식별자 | |
||||
|
| **메이커** | `Maker` | 좌측 | 로그인 시 입력된 제조사명 | |
||||
|
| **모델** | `Model` | 좌측 | 로그인 시 입력된 검사 모델명 | |
||||
|
| **변형 1** | `Variant1` | 좌측 | 로그인 시 입력된 변형 1 사양 | |
||||
|
| **변형 2** | `Variant2` | 좌측 | 로그인 시 입력된 변형 2 사양 | |
||||
|
| **작업자** | `Operator` | 좌측 | 작업자 사용자 ID | |
||||
|
| **작업라인** | `Line` | 좌측 | 로그인 공정 작업 라인 | |
||||
|
| **Lot No** | `LotNo` | 좌측 | 생산 Lot 번호 | |
||||
|
| **지그 번호** | `JigNo` | 좌측 | 사용 지그(Jig) 번호 | |
||||
|
| **검사채널** | `Channel` | 좌측 | 검사 통신 채널 (`LEFT` / `RIGHT`) | |
||||
|
| **SPEC UL** | `SpecUL` | 좌측 | 상한 규격값 | |
||||
|
| **SPEC LL** | `SpecLL` | 좌측 | 하한 규격값 | |
||||
|
| **릭 측정값** | `MeasuredValue` | **우측** | 측정된 리크 유량값 (**볼드 표시**) | |
||||
|
| **최종판정** | `Judgment` | **중앙** | `OK`(녹색) 또는 `NG`(빨간색) 볼드 강조 | |
||||
|
|
||||
|
### 그리드 스타일 |
||||
|
- 격자선: 수평/수직 연회색 격자선 표시 |
||||
|
- 홀수 행 배경: 흰색, 짝수 행 배경: 연회색 배경으로 행 식별 편의 제공 |
||||
|
- 선택된 행: 연파랑 배경에 파란색 텍스트로 강조 |
||||
|
- 컬럼 헤더 클릭 시 해당 열 기준 오름차순/내림차순 정렬 가능 (컬럼 순서 변경 불가) |
||||
|
- 최소 50행이 항상 표시됩니다 (데이터가 적어도 격자 형태 유지를 위해 빈 행으로 채워짐) |
||||
|
|
||||
|
## 6.5 화면 닫기 |
||||
|
|
||||
|
| 방법 | 설명 | |
||||
|
|------|------| |
||||
|
| 하단 **Close** 버튼 클릭 | 메인 화면으로 복귀 | |
||||
|
| 하단 메뉴 **Data** 버튼 재클릭 | 토글 동작으로 메인 화면으로 복귀 | |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
[← 이전: 시험 설정](05_시험_설정.md) | [다음: 자동 시험 프로세스 →](07_시험_프로세스.md) |
||||
@ -1,99 +0,0 @@ |
|||||
# 6. 측정 데이터 조회 (Data View) |
|
||||
|
|
||||
[← 메뉴얼 목차로 돌아가기](사용자_메뉴얼.md) |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
하단 메뉴에서 **Data (측정 데이터)** 버튼을 클릭하면 표시됩니다. 다시 클릭하면 메인 화면으로 복귀합니다. |
|
||||
|
|
||||
## 7.1 화면 구성 |
|
||||
|
|
||||
``` |
|
||||
┌───────────────────────────────────────────────────────────┐ |
|
||||
│ INSPECT DATA LIST (측정 기록 조회) │ ← 타이틀 바 |
|
||||
├───────────────────────────────────────────────────────────┤ |
|
||||
│ [조회기간] 2026-05-01 ~ 2026-05-08 [판정] 전체 [ID검색] │ |
|
||||
│ [Search] [CSV] │ ← 필터 영역 |
|
||||
├──────┬──────┬───────┬────┬──────┬──────┬──────┬────┬─────┤ |
|
||||
│ 날짜 │재시험│ ID │좌우│UL │LL │측정값│판정│비고 │ ← 데이터 그리드 |
|
||||
│ │ │ │ │ │ │ │ │ │ |
|
||||
│ ... │ N │ABC123 │LEFT│ 1.00 │-1.00 │0.5234│ OK │ │ |
|
||||
│ ... │ N │DEF456 │RIGHT│1.00 │-1.00 │1.2000│ NG │ │ |
|
||||
├──────┴──────┴───────┴────┴──────┴──────┴──────┴────┴─────┤ |
|
||||
│ [ Close ] │ |
|
||||
└───────────────────────────────────────────────────────────┘ |
|
||||
``` |
|
||||
|
|
||||
## 6.2 필터 조건 상세 |
|
||||
|
|
||||
### 조회 기간 (폭 320px) |
|
||||
|
|
||||
| 항목 | UI 타입 | 기본값 | 설명 | |
|
||||
|------|---------|--------|------| |
|
||||
| **시작일** | DatePicker (달력) | 오늘로부터 7일 전 | 조회 시작 날짜 | |
|
||||
| **종료일** | DatePicker (달력) | 오늘 | 조회 종료 날짜 | |
|
||||
|
|
||||
> **참고**: 달력 팝업은 1.25배 확대되어 표시됩니다. |
|
||||
|
|
||||
### 판정 결과 (폭 120px) |
|
||||
|
|
||||
| 항목 | UI 타입 | 선택 가능 값 | 기본값 | |
|
||||
|------|---------|-------------|--------| |
|
||||
| **판정 결과** | 드롭다운 | `[전체]` / `OK` / `NG` | `[전체]` | |
|
||||
|
|
||||
### ID 검색 (폭 280px) |
|
||||
|
|
||||
| 항목 | UI 타입 | 설명 | 기본값 | |
|
||||
|------|---------|------|--------| |
|
||||
| **ID 검색** | 텍스트 입력 | 제품 ID에 포함된 텍스트로 부분 일치 검색 | (빈 값 = 전체) | |
|
||||
|
|
||||
## 6.3 조회 및 내보내기 버튼 |
|
||||
|
|
||||
| 버튼 | 색상 | 크기 | 동작 | |
|
||||
|------|------|------|------| |
|
||||
| **Search** | 연파랑 배경, 파랑 테두리 | 110×45px | 설정된 필터 조건으로 Logs 폴더의 CSV 파일을 검색합니다 | |
|
||||
| **CSV** | 연녹색 배경, 녹색 테두리 | 110×45px | 검색 결과를 새 CSV 파일로 저장합니다 (저장 위치 선택 대화상자) | |
|
||||
|
|
||||
### Search 버튼 동작 상세 |
|
||||
1. `[프로그램폴더]\Logs\` 폴더에서 조회 기간에 해당하는 `yyyy-MM-dd.csv` 파일을 탐색 |
|
||||
2. 각 CSV 파일의 데이터를 판정/ID 필터 조건으로 필터링 |
|
||||
3. 결과를 날짜 역순(최신순)으로 정렬하여 데이터 그리드에 표시 |
|
||||
4. 검색 결과가 없으면 "해당 조건의 데이터가 없습니다." 알림 표시 |
|
||||
|
|
||||
### CSV 버튼 동작 상세 |
|
||||
1. 검색 결과가 없으면 "저장할 데이터가 없습니다." 알림 표시 |
|
||||
2. 파일 저장 대화상자 표시 (기본 파일명: `InspectData_yyyyMMdd_HHmmss.csv`) |
|
||||
3. 저장 완료 시 "CSV 파일로 저장되었습니다." 알림 표시 |
|
||||
|
|
||||
## 6.4 데이터 그리드 컬럼 |
|
||||
|
|
||||
| 컬럼 | 폭 | 정렬 | 설명 | |
|
||||
|------|-----|------|------| |
|
||||
| **날짜** | 130px | 좌측 | 검사 일자 (yyyy-MM-dd 형식) | |
|
||||
| **재시험** | 70px | 좌측 | 재시험 여부 (`Y` 또는 `N`) | |
|
||||
| **ID** | 250px | 좌측 | 제품 ID (ZMDI 센서에서 읽은 값) | |
|
||||
| **좌/우** | 70px | 좌측 | 검사 채널 (`LEFT` 또는 `RIGHT`) | |
|
||||
| **SPEC UL** | 80px | 좌측 | 검사 시점의 상한 규격값 | |
|
||||
| **SPEC LL** | 80px | 좌측 | 검사 시점의 하한 규격값 | |
|
||||
| **측정값** | 100px | **우측** | 측정된 결과 sccm (**볼드**) | |
|
||||
| **판정** | 80px | **중앙** | `OK`(녹색, #27AE60) 또는 `NG`(빨간색, #C0392B), **ExtraBold** | |
|
||||
| **비고** | 나머지(300px~) | 좌측 | 추가 메모 (현재 미사용) | |
|
||||
|
|
||||
### 그리드 스타일 |
|
||||
- 행 높이: 35px |
|
||||
- 격자선: 수평/수직 모두 연회색(#ECF0F1) |
|
||||
- 홀수 행 배경: 흰색, 짝수 행 배경: 연회색(#F9FBFC) |
|
||||
- 선택된 행: 연파랑 배경(#D6EAF8), 파란색 텍스트(#2E86C1) |
|
||||
- 컬럼 헤더 정렬 가능, 컬럼 순서 변경 불가 |
|
||||
- 최소 50행이 항상 표시됩니다 (데이터가 적어도 빈 행으로 채워짐) |
|
||||
|
|
||||
## 6.5 화면 닫기 |
|
||||
|
|
||||
| 방법 | 설명 | |
|
||||
|------|------| |
|
||||
| 하단 **Close** 버튼 클릭 | 메인 화면으로 복귀 | |
|
||||
| 하단 메뉴 **Data** 버튼 재클릭 | 토글 동작으로 메인 화면으로 복귀 | |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
[← 이전: 시험 설정](05_시험_설정.md) | [다음: 자동 시험 프로세스 →](08_시험_프로세스.md) |
|
||||
@ -0,0 +1,219 @@ |
|||||
|
# 8. 로그 및 데이터 관리 |
||||
|
|
||||
|
[← 메뉴얼 목차로 돌아가기](사용자_메뉴얼.md) |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 8.1 검사 데이터 로그 (CSV) |
||||
|
|
||||
|
### 기본 정보 |
||||
|
|
||||
|
| 항목 | 내용 | |
||||
|
|------|------| |
||||
|
| **저장 위치** | `[프로그램 폴더]\Logs\yyyy-MM-dd.csv` | |
||||
|
| **파일 형식** | CSV (UTF-8 인코딩) | |
||||
|
| **파일 생성** | 날짜별 자동 생성 (해당 날짜 첫 시험 시) | |
||||
|
| **데이터 추가** | 매 시험 완료 시 자동으로 해당 날짜 파일에 1행 추가 | |
||||
|
|
||||
|
### CSV 헤더 및 컬럼 |
||||
|
|
||||
|
``` |
||||
|
IC_SN,PCB_Barcode,Maker,Model,Variant_1,Variant_2,Operator,Production_Date,Line,Lot_No,Jig_No,Channel,Spec_UL,Spec_LL,Leak_Value,Result |
||||
|
``` |
||||
|
|
||||
|
| 컬럼 | 예시 | 설명 | |
||||
|
|------|------|------| |
||||
|
| `IC_SN` | `260508A00123B01C` | 제품 고유 시리얼 번호 (PK/FK) | |
||||
|
| `PCB_Barcode` | `260508A00123B01C` | PCB 바코드 고유 식별 정보 (UK) | |
||||
|
| `Maker` | `Hyundai` | 메이커 정보 | |
||||
|
| `Model` | `NE1aW` | 모델 정보 | |
||||
|
| `Variant_1` | `PT` | 변형1 정보 | |
||||
|
| `Variant_2` | `SENSOR` | 변형2 정보 | |
||||
|
| `Operator` | `op001` | 작업자 ID | |
||||
|
| `Production_Date` | `2026-05-08 10:30:15` | 검사 수행 일시 | |
||||
|
| `Line` | `L1` | 작업 라인 번호 | |
||||
|
| `Lot_No` | `LOT001` | Lot 번호 | |
||||
|
| `Jig_No` | `JIG01` | 지그 번호 | |
||||
|
| `Channel` | `LEFT` | 검사 채널 (LEFT 또는 RIGHT) | |
||||
|
| `Spec_UL` | `1.00` | 규격 상한값 | |
||||
|
| `Spec_LL` | `-1.00` | 규격 하한값 | |
||||
|
| `Leak_Value` | `0.5234` | 측정된 릭 측정값 (소수점 4자리) | |
||||
|
| `Result` | `OK` | 최종 판정 결과 (OK 또는 NG) | |
||||
|
|
||||
|
### CSV 예시 |
||||
|
```csv |
||||
|
IC_SN,PCB_Barcode,Maker,Model,Variant_1,Variant_2,Operator,Production_Date,Line,Lot_No,Jig_No,Channel,Spec_UL,Spec_LL,Leak_Value,Result |
||||
|
260508A00123B01C,260508A00123B01C,Hyundai,NE1aW,PT,SENSOR,op001,2026-05-08 10:30:15,L1,LOT001,JIG01,LEFT,1.00,-1.00,0.5234,OK |
||||
|
260508A00456D02E,260508A00456D02E,Hyundai,NE1aW,PT,SENSOR,op002,2026-05-08 10:30:45,L1,LOT001,JIG01,RIGHT,1.00,-1.00,1.2000,NG |
||||
|
``` |
||||
|
|
||||
|
> **참고**: 타임아웃 발생 시에도 측정값 `0.0000`, 판정 `NG`로 로그에 기록됩니다. |
||||
|
|
||||
|
> **참고**: 프로그램은 구형 8개 컬럼 포맷(`Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment`)의 로그 파일에 대해서도 컬럼 크기를 기준으로 자동 감지 및 파싱(하위 호환성)을 수행합니다. |
||||
|
|
||||
|
> **참고**: CSV 값에 쉼표, 따옴표, 줄바꿈이 포함된 경우 자동으로 이스케이프 처리됩니다. |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 8.2 시스템 로그 |
||||
|
|
||||
|
### 기본 정보 |
||||
|
|
||||
|
| 항목 | 내용 | |
||||
|
|------|------| |
||||
|
| **저장 위치** | `[프로그램 폴더]\Logs\yyyy-MM-dd_system.log` | |
||||
|
| **파일 형식** | 텍스트 파일 (UTF-8 인코딩) | |
||||
|
| **내용** | 통신 오류, 시스템 이벤트 등 | |
||||
|
|
||||
|
### 로그 형식 |
||||
|
``` |
||||
|
[HH:mm:ss.fff] [TAG] 메시지 내용 |
||||
|
``` |
||||
|
|
||||
|
### 예시 |
||||
|
``` |
||||
|
[10:30:00.123] [SYSTEM] 프로그램 시작 |
||||
|
[10:30:01.456] [COMM] Sentinel C28 포트 연결 성공 (COM1) |
||||
|
[10:35:15.789] [ERROR] Sentinel C28 통신 타임아웃 |
||||
|
``` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 8.3 설정 파일 |
||||
|
|
||||
|
### config.xml |
||||
|
|
||||
|
| 항목 | 내용 | |
||||
|
|------|------| |
||||
|
| **위치** | 프로그램 실행 폴더 | |
||||
|
| **생성** | 최초 실행 시 기본값으로 자동 생성 | |
||||
|
| **수정** | Comm. Settings 또는 Parameters에서 저장 시 갱신 | |
||||
|
|
||||
|
```xml |
||||
|
<AppConfig> |
||||
|
<SensorPort>COM1</SensorPort> |
||||
|
<SensorBaudRate>9600</SensorBaudRate> |
||||
|
<Board4251Port>COM3</Board4251Port> |
||||
|
<Board4251BaudRate>115200</Board4251BaudRate> |
||||
|
<Board4251Timeout>5000</Board4251Timeout> |
||||
|
<SpecUL>1.00</SpecUL> |
||||
|
<SpecLL>-1.00</SpecLL> |
||||
|
</AppConfig> |
||||
|
``` |
||||
|
|
||||
|
| 설정 키 | 기본값 | 설정 위치 | 설명 | |
||||
|
|---------|--------|----------|------| |
||||
|
| `SensorPort` | COM1 | Comm. Settings | C28 센서 COM 포트 | |
||||
|
| `SensorBaudRate` | 9600 | Comm. Settings | C28 센서 통신 속도 | |
||||
|
| `Board4251Port` | COM3 | Comm. Settings | 4251 보드 COM 포트 | |
||||
|
| `Board4251BaudRate` | 115200 | Comm. Settings | 4251 보드 통신 속도 | |
||||
|
| `Board4251Timeout` | 5000 | (코드 내 고정) | 4251 보드 응답 대기 시간(ms) | |
||||
|
| `SpecUL` | 1.00 | Parameters | 누설 상한 규격값 | |
||||
|
| `SpecLL` | -1.00 | Parameters | 누설 하한 규격값 | |
||||
|
|
||||
|
### Database.ini |
||||
|
|
||||
|
| 항목 | 내용 | |
||||
|
|------|------| |
||||
|
| **위치** | 프로그램 실행 폴더 | |
||||
|
| **생성** | 배포 시 기본 포함 및 파일 누락 상태로 기동 시 기본 양식이 자동 생성됨 | |
||||
|
| **수정** | 메모장 등의 텍스트 에디터를 통해 연결 주소 및 프로시저 정보 직접 수정 | |
||||
|
|
||||
|
```ini |
||||
|
; [데이터베이스 접속 상세 설정] |
||||
|
Db_Server=192.168.0.20 |
||||
|
Db_Name=NE1aW_PT_SENSOR |
||||
|
Db_User=sa |
||||
|
Db_Password=sa1234 |
||||
|
|
||||
|
[Login] |
||||
|
Procedure=dbo.CheckOperator |
||||
|
ProcessName=EOL |
||||
|
OfflinePreview=false |
||||
|
``` |
||||
|
|
||||
|
| 설정 키 | 예시 | 설명 | |
||||
|
|---------|--------|------| |
||||
|
| `Db_Server` | 192.168.0.20 | 데이터베이스 서버 IP 주소 또는 호스트명 | |
||||
|
| `Db_Name` | NE1aW_PT_SENSOR | 대상 데이터베이스(DB) 이름 | |
||||
|
| `Db_User` | sa | 데이터베이스 접속 계정 ID | |
||||
|
| `Db_Password` | sa1234 | 데이터베이스 접속 계정 비밀번호 | |
||||
|
| `Procedure` | dbo.CheckOperator | 로그인 계정 검증용 저장 프로시저명 | |
||||
|
| `ProcessName` | EOL | 검증 공정명 | |
||||
|
| `OfflinePreview` | false | true 시 DB 연결 없이 로그인 즉시 성공 (오프라인 모드) | |
||||
|
|
||||
|
### DioConfig.ini |
||||
|
|
||||
|
| 항목 | 내용 | |
||||
|
|------|------| |
||||
|
| **위치** | `[프로그램 폴더]\Settings\DioConfig.ini` | |
||||
|
| **생성** | 최초 실행 시 자동 생성 | |
||||
|
| **수정** | 일반적으로 수정 불필요 (하드웨어 변경 시에만) | |
||||
|
|
||||
|
```ini |
||||
|
[DIO] |
||||
|
CompanyName=ADLink |
||||
|
BoardCnt=1 |
||||
|
|
||||
|
[BOARD_1] |
||||
|
BoardType=PCI_7432 |
||||
|
BoardNumber=0 |
||||
|
|
||||
|
[BOARD_1_IN] |
||||
|
0=LEFT_START,1,좌측 시험 시작 신호 |
||||
|
1=RIGHT_START,1,우측 시험 시작 신호 |
||||
|
|
||||
|
[BOARD_1_OUT] |
||||
|
0=LEFT_OK,1,좌측 합격 출력 |
||||
|
1=RIGHT_OK,1,우측 합격 출력 |
||||
|
2=PC_ON,1,PC 가동 신호 |
||||
|
3=LEFT_NG,1,좌측 불합격 출력 |
||||
|
4=RIGHT_NG,1,우측 불합격 출력 |
||||
|
``` |
||||
|
|
||||
|
**INI 포맷 설명**: `비트인덱스=신호명,활성여부,설명` |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 8.4 파일 관리 주의사항 |
||||
|
|
||||
|
| 주의사항 | 설명 | |
||||
|
|---------|------| |
||||
|
| Logs 폴더 삭제 금지 | 검사 기록이 영구 삭제됩니다 | |
||||
|
| CSV 파일 직접 편집 | Excel 등으로 편집 시 인코딩이 변경될 수 있으므로 주의 | |
||||
|
| config.xml 직접 편집 | 잘못된 값 입력 시 프로그램 오작동 가능. 프로그램 내 설정 화면 사용 권장 | |
||||
|
| 디스크 용량 | 일일 수백 건 검사 기준, 연간 약 50~100MB 정도의 로그가 생성됩니다 | |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
## 8.5 데이터베이스 데이터 관리 (dbo.EOL) |
||||
|
|
||||
|
매 시험 완료 시 검사 결과 및 로그인 정보가 데이터베이스에 실시간으로 저장(MERGE/UPSERT)됩니다. |
||||
|
|
||||
|
### 데이터베이스 테이블 및 컬럼 매핑 |
||||
|
|
||||
|
* **대상 테이블**: `[dbo].[EOL]` |
||||
|
* **매핑 상세**: |
||||
|
|
||||
|
| DB 컬럼명 | 데이터 타입 | 매핑되는 C# 데이터 | 비고 | |
||||
|
| :--- | :--- | :--- | :--- | |
||||
|
| **`IC_SN`** | `NVARCHAR(10)` | 제품의 최종 조합 ID (`inspectData.ProductId`) | **PK / FK** | |
||||
|
| **`PCB_Barcode`** | `NVARCHAR(10)` | 고유 식별용 바코드 정보 (`inspectData.ProductId` 매핑) | **UK** | |
||||
|
| `Maker` | `NVARCHAR(10)` | 로그인 제조사 정보 (`App.LoginResult.Maker`) | | |
||||
|
| `Model` | `NVARCHAR(10)` | 로그인 모델 정보 (`App.LoginResult.Model`) | | |
||||
|
| `Variant_1` | `NVARCHAR(10)` | 로그인 변형1 정보 (`App.LoginResult.Variant1`) | | |
||||
|
| `Variant_2` | `NVARCHAR(10)` | 로그인 변형2 정보 (`App.LoginResult.Variant2`) | | |
||||
|
| `Operator` | `NVARCHAR(10)` | 로그인 검사자 ID (`App.LoginResult.Operator`) | | |
||||
|
| `Production_Date` | `DATETIME2(0)` | 검사 수행 일시 (`DateTime.Now`) | | |
||||
|
| `Line` | `NVARCHAR(5)` | 로그인 작업 라인 (`App.LoginResult.LineNo`) | | |
||||
|
| `Lot_No` | `NVARCHAR(8)` | 로그인 Lot 번호 (`App.LoginResult.LotNo`) | | |
||||
|
| `Jig_No` | `NVARCHAR(10)` | 로그인 지그 번호 (`App.LoginResult.JigNo`) | | |
||||
|
| `Channel` | `NVARCHAR(5)` | 검사 채널 (`LEFT` / `RIGHT`) | | |
||||
|
| `Spec_UL` | `DECIMAL(3,2)` | 규격 상한값 (소수점 2자리) | | |
||||
|
| `Spec_LL` | `DECIMAL(3,2)` | 규격 하한값 (소수점 2자리) | | |
||||
|
| `Leak_Value` | `DECIMAL(5,4)` | 측정된 릭 값 (소수점 4자리) | | |
||||
|
| `Result` | `NVARCHAR(5)` | 최종 판정 결과 (`OK` 또는 `NG`) | | |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
[← 이전: 시험 프로세스](07_시험_프로세스.md) | [다음: 오류 대응 →](09_오류_대응.md) |
||||
@ -1,150 +0,0 @@ |
|||||
# 8. 로그 및 데이터 관리 |
|
||||
|
|
||||
[← 메뉴얼 목차로 돌아가기](사용자_메뉴얼.md) |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 8.1 검사 데이터 로그 (CSV) |
|
||||
|
|
||||
### 기본 정보 |
|
||||
|
|
||||
| 항목 | 내용 | |
|
||||
|------|------| |
|
||||
| **저장 위치** | `[프로그램 폴더]\Logs\yyyy-MM-dd.csv` | |
|
||||
| **파일 형식** | CSV (UTF-8 인코딩) | |
|
||||
| **파일 생성** | 날짜별 자동 생성 (해당 날짜 첫 시험 시) | |
|
||||
| **데이터 추가** | 매 시험 완료 시 자동으로 해당 날짜 파일에 1행 추가 | |
|
||||
|
|
||||
### CSV 헤더 및 컬럼 |
|
||||
|
|
||||
``` |
|
||||
Date,Time,Channel,ID,Value,Judgment,SpecUL,SpecLL,Retest |
|
||||
``` |
|
||||
|
|
||||
| 컬럼 | 예시 | 설명 | |
|
||||
|------|------|------| |
|
||||
| `Date` | `2026-05-08` | 검사 일자 | |
|
||||
| `Time` | `10:30:15` | 검사 시각 | |
|
||||
| `Channel` | `LEFT` | 검사 채널 (LEFT 또는 RIGHT) | |
|
||||
| `ID` | `260508A00123B01C` | 제품 ID | |
|
||||
| `Value` | `0.5234` | 측정값 (sccm, 소수점 4자리) | |
|
||||
| `Judgment` | `OK` | 판정 결과 (OK 또는 NG) | |
|
||||
| `SpecUL` | `1.00` | 검사 시점의 상한 규격값 | |
|
||||
| `SpecLL` | `-1.00` | 검사 시점의 하한 규격값 | |
|
||||
| `Retest` | `N` | 재시험 여부 (Y 또는 N) | |
|
||||
|
|
||||
### CSV 예시 |
|
||||
```csv |
|
||||
Date,Time,Channel,ID,Value,Judgment,SpecUL,SpecLL,Retest |
|
||||
2026-05-08,10:30:15,LEFT,260508A00123B01C,0.5234,OK,1.00,-1.00,N |
|
||||
2026-05-08,10:30:45,RIGHT,260508A00456D02E,1.2000,NG,1.00,-1.00,N |
|
||||
2026-05-08,10:31:20,LEFT,260508A00789F03G,0.0000,NG,1.00,-1.00,N |
|
||||
``` |
|
||||
|
|
||||
> **참고**: 타임아웃 발생 시에도 측정값 `0.0000`, 판정 `NG`로 로그에 기록됩니다. |
|
||||
|
|
||||
> **참고**: CSV 값에 쉼표, 따옴표, 줄바꿈이 포함된 경우 자동으로 이스케이프 처리됩니다. |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 8.2 시스템 로그 |
|
||||
|
|
||||
### 기본 정보 |
|
||||
|
|
||||
| 항목 | 내용 | |
|
||||
|------|------| |
|
||||
| **저장 위치** | `[프로그램 폴더]\Logs\yyyy-MM-dd_system.log` | |
|
||||
| **파일 형식** | 텍스트 파일 (UTF-8 인코딩) | |
|
||||
| **내용** | 통신 오류, 시스템 이벤트 등 | |
|
||||
|
|
||||
### 로그 형식 |
|
||||
``` |
|
||||
[HH:mm:ss.fff] [TAG] 메시지 내용 |
|
||||
``` |
|
||||
|
|
||||
### 예시 |
|
||||
``` |
|
||||
[10:30:00.123] [SYSTEM] 프로그램 시작 |
|
||||
[10:30:01.456] [COMM] Sentinel C28 포트 연결 성공 (COM1) |
|
||||
[10:35:15.789] [ERROR] Sentinel C28 통신 타임아웃 |
|
||||
``` |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 8.3 설정 파일 |
|
||||
|
|
||||
### config.xml |
|
||||
|
|
||||
| 항목 | 내용 | |
|
||||
|------|------| |
|
||||
| **위치** | 프로그램 실행 폴더 | |
|
||||
| **생성** | 최초 실행 시 기본값으로 자동 생성 | |
|
||||
| **수정** | Comm. Settings 또는 Parameters에서 저장 시 갱신 | |
|
||||
|
|
||||
```xml |
|
||||
<AppConfig> |
|
||||
<SensorPort>COM1</SensorPort> |
|
||||
<SensorBaudRate>9600</SensorBaudRate> |
|
||||
<Board4251Port>COM3</Board4251Port> |
|
||||
<Board4251BaudRate>115200</Board4251BaudRate> |
|
||||
<Board4251Timeout>5000</Board4251Timeout> |
|
||||
<SpecUL>1.00</SpecUL> |
|
||||
<SpecLL>-1.00</SpecLL> |
|
||||
</AppConfig> |
|
||||
``` |
|
||||
|
|
||||
| 설정 키 | 기본값 | 설정 위치 | 설명 | |
|
||||
|---------|--------|----------|------| |
|
||||
| `SensorPort` | COM1 | Comm. Settings | C28 센서 COM 포트 | |
|
||||
| `SensorBaudRate` | 9600 | Comm. Settings | C28 센서 통신 속도 | |
|
||||
| `Board4251Port` | COM3 | Comm. Settings | 4251 보드 COM 포트 | |
|
||||
| `Board4251BaudRate` | 115200 | Comm. Settings | 4251 보드 통신 속도 | |
|
||||
| `Board4251Timeout` | 5000 | (코드 내 고정) | 4251 보드 응답 대기 시간(ms) | |
|
||||
| `SpecUL` | 1.00 | Parameters | 누설 상한 규격값 | |
|
||||
| `SpecLL` | -1.00 | Parameters | 누설 하한 규격값 | |
|
||||
|
|
||||
### DioConfig.ini |
|
||||
|
|
||||
| 항목 | 내용 | |
|
||||
|------|------| |
|
||||
| **위치** | `[프로그램 폴더]\Settings\DioConfig.ini` | |
|
||||
| **생성** | 최초 실행 시 자동 생성 | |
|
||||
| **수정** | 일반적으로 수정 불필요 (하드웨어 변경 시에만) | |
|
||||
|
|
||||
```ini |
|
||||
[DIO] |
|
||||
CompanyName=ADLink |
|
||||
BoardCnt=1 |
|
||||
|
|
||||
[BOARD_1] |
|
||||
BoardType=PCI_7432 |
|
||||
BoardNumber=0 |
|
||||
|
|
||||
[BOARD_1_IN] |
|
||||
0=LEFT_START,1,좌측 시험 시작 신호 |
|
||||
1=RIGHT_START,1,우측 시험 시작 신호 |
|
||||
|
|
||||
[BOARD_1_OUT] |
|
||||
0=LEFT_OK,1,좌측 합격 출력 |
|
||||
1=RIGHT_OK,1,우측 합격 출력 |
|
||||
2=PC_ON,1,PC 가동 신호 |
|
||||
3=LEFT_NG,1,좌측 불합격 출력 |
|
||||
4=RIGHT_NG,1,우측 불합격 출력 |
|
||||
``` |
|
||||
|
|
||||
**INI 포맷 설명**: `비트인덱스=신호명,활성여부,설명` |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
## 8.4 파일 관리 주의사항 |
|
||||
|
|
||||
| 주의사항 | 설명 | |
|
||||
|---------|------| |
|
||||
| Logs 폴더 삭제 금지 | 검사 기록이 영구 삭제됩니다 | |
|
||||
| CSV 파일 직접 편집 | Excel 등으로 편집 시 인코딩이 변경될 수 있으므로 주의 | |
|
||||
| config.xml 직접 편집 | 잘못된 값 입력 시 프로그램 오작동 가능. 프로그램 내 설정 화면 사용 권장 | |
|
||||
| 디스크 용량 | 일일 수백 건 검사 기준, 연간 약 50~100MB 정도의 로그가 생성됩니다 | |
|
||||
|
|
||||
--- |
|
||||
|
|
||||
[← 이전: 시험 프로세스](08_시험_프로세스.md) | [다음: 오류 대응 →](10_오류_대응.md) |
|
||||
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 49 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 30 KiB |
@ -0,0 +1,122 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
|
||||
|
namespace Housing.Services |
||||
|
{ |
||||
|
public class DatabaseSettings |
||||
|
{ |
||||
|
public string Db_Server { get; set; } = string.Empty; |
||||
|
public string Db_Name { get; set; } = string.Empty; |
||||
|
public string Db_User { get; set; } = string.Empty; |
||||
|
public string Db_Password { get; set; } = string.Empty; |
||||
|
|
||||
|
private string _rawConnectionString = string.Empty; |
||||
|
|
||||
|
public string ConnectionString |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
// 개별 설정이 모두 지정되어 있는 경우 조합해서 사용
|
||||
|
if (!string.IsNullOrWhiteSpace(Db_Server) && |
||||
|
!string.IsNullOrWhiteSpace(Db_Name) && |
||||
|
!string.IsNullOrWhiteSpace(Db_User) && |
||||
|
!string.IsNullOrWhiteSpace(Db_Password)) |
||||
|
{ |
||||
|
return $"Server={Db_Server};Database={Db_Name};User Id={Db_User};Password={Db_Password};"; |
||||
|
} |
||||
|
// 없으면 레거시 ConnectionString 필드를 그대로 사용
|
||||
|
return _rawConnectionString; |
||||
|
} |
||||
|
set |
||||
|
{ |
||||
|
_rawConnectionString = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static readonly object _dbLock = new object(); |
||||
|
private static DatabaseSettings _cachedSettings; |
||||
|
private static DateTime _lastWriteTime; |
||||
|
private static string _cachedPath; |
||||
|
|
||||
|
public static DatabaseSettings Load(string path) |
||||
|
{ |
||||
|
lock (_dbLock) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (File.Exists(path)) |
||||
|
{ |
||||
|
var currentWriteTime = File.GetLastWriteTime(path); |
||||
|
if (_cachedSettings != null && _cachedPath == path && _lastWriteTime == currentWriteTime) |
||||
|
{ |
||||
|
return _cachedSettings; |
||||
|
} |
||||
|
|
||||
|
var settings = new DatabaseSettings(); |
||||
|
var lines = File.ReadAllLines(path); |
||||
|
foreach (var line in lines) |
||||
|
{ |
||||
|
var trimmed = line.Trim(); |
||||
|
|
||||
|
// 주석 행 스킵
|
||||
|
if (trimmed.StartsWith(";") || trimmed.StartsWith("#")) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
if (trimmed.StartsWith("ConnectionString", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.ConnectionString = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
else if (trimmed.StartsWith("Db_Server", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.Db_Server = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
else if (trimmed.StartsWith("Db_Name", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.Db_Name = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
else if (trimmed.StartsWith("Db_User", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.Db_User = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
else if (trimmed.StartsWith("Db_Password", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.Db_Password = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
_cachedSettings = settings; |
||||
|
_lastWriteTime = currentWriteTime; |
||||
|
_cachedPath = path; |
||||
|
return settings; |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine($"[DatabaseSettings Load Error] {ex.Message}"); |
||||
|
} |
||||
|
return _cachedSettings ?? new DatabaseSettings(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Housing.Services |
||||
|
{ |
||||
|
public class LoginAccountRepository |
||||
|
{ |
||||
|
private readonly DatabaseSettings _dbSettings; |
||||
|
private readonly LoginSettings _loginSettings; |
||||
|
|
||||
|
public LoginAccountRepository(DatabaseSettings dbSettings, LoginSettings loginSettings) |
||||
|
{ |
||||
|
_dbSettings = dbSettings; |
||||
|
_loginSettings = loginSettings; |
||||
|
} |
||||
|
|
||||
|
public async Task<bool> ExistsAsync(string operatorId, string password) |
||||
|
{ |
||||
|
// 1. 데모 계정(test/test)인 경우 DB 조회 없이 즉시 로그인 통과
|
||||
|
if (IsDefaultAccount(operatorId, password)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 2. 오프라인 모드인 경우 기본 성공
|
||||
|
if (_loginSettings.OfflinePreview) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// DB 커넥션 스트링이 비어있으면 실패 처리
|
||||
|
if (string.IsNullOrWhiteSpace(_dbSettings.ConnectionString)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
// 실제 DB에서 계정을 조회해보기 위한 시도
|
||||
|
using (var conn = new System.Data.SqlClient.SqlConnection(_dbSettings.ConnectionString)) |
||||
|
{ |
||||
|
await conn.OpenAsync(); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(_loginSettings.Procedure)) |
||||
|
{ |
||||
|
// 저장 프로시저 호출
|
||||
|
using (var cmd = new System.Data.SqlClient.SqlCommand(_loginSettings.Procedure, conn)) |
||||
|
{ |
||||
|
cmd.CommandType = System.Data.CommandType.StoredProcedure; |
||||
|
cmd.Parameters.AddWithValue("@OperatorId", operatorId); |
||||
|
cmd.Parameters.AddWithValue("@Password", password); |
||||
|
cmd.Parameters.AddWithValue("@ProcessName", _loginSettings.ProcessName ?? string.Empty); |
||||
|
|
||||
|
var result = await cmd.ExecuteScalarAsync(); |
||||
|
if (result != null && result != DBNull.Value) |
||||
|
{ |
||||
|
if (int.TryParse(result.ToString(), out var count)) |
||||
|
{ |
||||
|
return count > 0; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// 일반 쿼리 수행
|
||||
|
const string query = "SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id AND [Password] = @Pw"; |
||||
|
using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn)) |
||||
|
{ |
||||
|
cmd.Parameters.AddWithValue("@Id", operatorId); |
||||
|
cmd.Parameters.AddWithValue("@Pw", password); |
||||
|
|
||||
|
var count = (int)await cmd.ExecuteScalarAsync(); |
||||
|
return count > 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine($"[LoginAccountRepository ExistsAsync Error] {ex.Message}"); |
||||
|
// DB 연결이나 쿼리 오류 발생 시 로그인 실패 처리
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool IsDefaultAccount(string operatorId, string password) |
||||
|
{ |
||||
|
// 기본 개발/테스트용 데모 계정 처리 (test/test만 허용)
|
||||
|
if (string.Equals(operatorId, "test", StringComparison.OrdinalIgnoreCase) && |
||||
|
string.Equals(password, "test", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,147 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Xml.Serialization; |
||||
|
|
||||
|
namespace Housing.Services |
||||
|
{ |
||||
|
// XML 직렬화를 위한 데이터 컨테이너 클래스 정의
|
||||
|
[XmlRoot("LoginHistory")] |
||||
|
public class LoginHistoryData |
||||
|
{ |
||||
|
[XmlElement("Category")] |
||||
|
public List<HistoryCategory> Categories { get; set; } = new List<HistoryCategory>(); |
||||
|
} |
||||
|
|
||||
|
public class HistoryCategory |
||||
|
{ |
||||
|
[XmlAttribute("Name")] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[XmlElement("Value")] |
||||
|
public List<string> Values { get; set; } = new List<string>(); |
||||
|
} |
||||
|
|
||||
|
public class LoginHistoryStore |
||||
|
{ |
||||
|
private readonly string _filePath; |
||||
|
private readonly Dictionary<string, List<string>> _history = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); |
||||
|
|
||||
|
private LoginHistoryStore(string filePath) |
||||
|
{ |
||||
|
_filePath = filePath; |
||||
|
} |
||||
|
|
||||
|
public static LoginHistoryStore Load(string path) |
||||
|
{ |
||||
|
var store = new LoginHistoryStore(path); |
||||
|
store.LoadFromFile(); |
||||
|
return store; |
||||
|
} |
||||
|
|
||||
|
public List<string> GetValues(string fieldName) |
||||
|
{ |
||||
|
if (_history.TryGetValue(fieldName, out var list)) |
||||
|
{ |
||||
|
return new List<string>(list); |
||||
|
} |
||||
|
return new List<string>(); |
||||
|
} |
||||
|
|
||||
|
public void Remember(string fieldName, string value) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(value)) return; |
||||
|
value = value.Trim(); |
||||
|
|
||||
|
if (!_history.TryGetValue(fieldName, out var list)) |
||||
|
{ |
||||
|
list = new List<string>(); |
||||
|
_history[fieldName] = list; |
||||
|
} |
||||
|
|
||||
|
// 중복 제거 및 맨 앞으로 이동
|
||||
|
list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)); |
||||
|
list.Insert(0, value); |
||||
|
|
||||
|
// 최대 5개 이력 유지
|
||||
|
if (list.Count > 5) |
||||
|
{ |
||||
|
list.RemoveRange(5, list.Count - 5); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public bool Forget(string fieldName, string value) |
||||
|
{ |
||||
|
if (string.IsNullOrWhiteSpace(value)) return false; |
||||
|
value = value.Trim(); |
||||
|
|
||||
|
if (_history.TryGetValue(fieldName, out var list)) |
||||
|
{ |
||||
|
bool removed = list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)) > 0; |
||||
|
return removed; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public void Save() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
var dir = Path.GetDirectoryName(_filePath); |
||||
|
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) |
||||
|
{ |
||||
|
Directory.CreateDirectory(dir); |
||||
|
} |
||||
|
|
||||
|
var data = new LoginHistoryData(); |
||||
|
foreach (var kvp in _history) |
||||
|
{ |
||||
|
data.Categories.Add(new HistoryCategory |
||||
|
{ |
||||
|
Name = kvp.Key, |
||||
|
Values = kvp.Value |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
XmlSerializer serializer = new XmlSerializer(typeof(LoginHistoryData)); |
||||
|
using (var sw = new StreamWriter(_filePath)) |
||||
|
{ |
||||
|
serializer.Serialize(sw, data); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine($"[LoginHistoryStore Save Error] {ex.Message}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void LoadFromFile() |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (!File.Exists(_filePath)) return; |
||||
|
|
||||
|
XmlSerializer serializer = new XmlSerializer(typeof(LoginHistoryData)); |
||||
|
using (var sr = new StreamReader(_filePath)) |
||||
|
{ |
||||
|
var data = (LoginHistoryData)serializer.Deserialize(sr); |
||||
|
if (data?.Categories != null) |
||||
|
{ |
||||
|
_history.Clear(); |
||||
|
foreach (var cat in data.Categories) |
||||
|
{ |
||||
|
if (!string.IsNullOrEmpty(cat.Name)) |
||||
|
{ |
||||
|
_history[cat.Name] = cat.Values ?? new List<string>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine($"[LoginHistoryStore Load Error] {ex.Message}"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,295 +0,0 @@ |
|||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.IO; |
|
||||
using System.Text.RegularExpressions; |
|
||||
using System.Threading.Tasks; |
|
||||
|
|
||||
namespace Housing.Services |
|
||||
{ |
|
||||
// 1. DatabaseSettings
|
|
||||
public class DatabaseSettings |
|
||||
{ |
|
||||
public string ConnectionString { get; set; } = string.Empty; |
|
||||
|
|
||||
public static DatabaseSettings Load(string path) |
|
||||
{ |
|
||||
var settings = new DatabaseSettings(); |
|
||||
try |
|
||||
{ |
|
||||
if (File.Exists(path)) |
|
||||
{ |
|
||||
var lines = File.ReadAllLines(path); |
|
||||
foreach (var line in lines) |
|
||||
{ |
|
||||
var trimmed = line.Trim(); |
|
||||
if (trimmed.StartsWith("ConnectionString", StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
var parts = trimmed.Split(new[] { '=' }, 2); |
|
||||
if (parts.Length == 2) |
|
||||
{ |
|
||||
settings.ConnectionString = parts[1].Trim(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
// 로드 실패 시 무시
|
|
||||
} |
|
||||
return settings; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 2. LoginSettings
|
|
||||
public class LoginSettings |
|
||||
{ |
|
||||
public bool OfflinePreview { get; set; } = true; // 기본값은 오프라인 테스트 편의를 위해 true
|
|
||||
|
|
||||
public static LoginSettings Load(string path) |
|
||||
{ |
|
||||
var settings = new LoginSettings(); |
|
||||
try |
|
||||
{ |
|
||||
if (File.Exists(path)) |
|
||||
{ |
|
||||
var lines = File.ReadAllLines(path); |
|
||||
foreach (var line in lines) |
|
||||
{ |
|
||||
var trimmed = line.Trim(); |
|
||||
if (trimmed.StartsWith("OfflinePreview", StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
var parts = trimmed.Split(new[] { '=' }, 2); |
|
||||
if (parts.Length == 2) |
|
||||
{ |
|
||||
if (bool.TryParse(parts[1].Trim(), out var val)) |
|
||||
{ |
|
||||
settings.OfflinePreview = val; |
|
||||
} |
|
||||
else if (int.TryParse(parts[1].Trim(), out var intVal)) |
|
||||
{ |
|
||||
settings.OfflinePreview = intVal != 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
// 로드 실패 시 무시
|
|
||||
} |
|
||||
return settings; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 3. LoginHistoryStore
|
|
||||
public class LoginHistoryStore |
|
||||
{ |
|
||||
private readonly string _filePath; |
|
||||
private readonly Dictionary<string, List<string>> _history = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); |
|
||||
|
|
||||
private LoginHistoryStore(string filePath) |
|
||||
{ |
|
||||
_filePath = filePath; |
|
||||
} |
|
||||
|
|
||||
public static LoginHistoryStore Load(string path) |
|
||||
{ |
|
||||
var store = new LoginHistoryStore(path); |
|
||||
store.LoadFromFile(); |
|
||||
return store; |
|
||||
} |
|
||||
|
|
||||
public List<string> GetValues(string fieldName) |
|
||||
{ |
|
||||
if (_history.TryGetValue(fieldName, out var list)) |
|
||||
{ |
|
||||
return new List<string>(list); |
|
||||
} |
|
||||
return new List<string>(); |
|
||||
} |
|
||||
|
|
||||
public void Remember(string fieldName, string value) |
|
||||
{ |
|
||||
if (string.IsNullOrWhiteSpace(value)) return; |
|
||||
value = value.Trim(); |
|
||||
|
|
||||
if (!_history.TryGetValue(fieldName, out var list)) |
|
||||
{ |
|
||||
list = new List<string>(); |
|
||||
_history[fieldName] = list; |
|
||||
} |
|
||||
|
|
||||
// 중복 제거 및 맨 앞으로 이동
|
|
||||
list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)); |
|
||||
list.Insert(0, value); |
|
||||
|
|
||||
// 최대 5개 이력 유지
|
|
||||
if (list.Count > 5) |
|
||||
{ |
|
||||
list.RemoveRange(5, list.Count - 5); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public bool Forget(string fieldName, string value) |
|
||||
{ |
|
||||
if (string.IsNullOrWhiteSpace(value)) return false; |
|
||||
value = value.Trim(); |
|
||||
|
|
||||
if (_history.TryGetValue(fieldName, out var list)) |
|
||||
{ |
|
||||
bool removed = list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)) > 0; |
|
||||
return removed; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
public void Save() |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
var dir = Path.GetDirectoryName(_filePath); |
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) |
|
||||
{ |
|
||||
Directory.CreateDirectory(dir); |
|
||||
} |
|
||||
|
|
||||
// 초간단 JSON 형식으로 직렬화 (의존성 최소화)
|
|
||||
using (var sw = new StreamWriter(_filePath)) |
|
||||
{ |
|
||||
sw.WriteLine("{"); |
|
||||
var keys = new List<string>(_history.Keys); |
|
||||
for (int i = 0; i < keys.Count; i++) |
|
||||
{ |
|
||||
var key = keys[i]; |
|
||||
var values = _history[key]; |
|
||||
var arrayItems = new List<string>(); |
|
||||
foreach (var val in values) |
|
||||
{ |
|
||||
// 따옴표 escape
|
|
||||
var escaped = val.Replace("\"", "\\\""); |
|
||||
arrayItems.Add($"\"{escaped}\""); |
|
||||
} |
|
||||
var line = $" \"{key}\": [{string.Join(", ", arrayItems)}]"; |
|
||||
if (i < keys.Count - 1) |
|
||||
{ |
|
||||
line += ","; |
|
||||
} |
|
||||
sw.WriteLine(line); |
|
||||
} |
|
||||
sw.WriteLine("}"); |
|
||||
} |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
// 저장 오류 시 무시
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void LoadFromFile() |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
if (!File.Exists(_filePath)) return; |
|
||||
|
|
||||
var text = File.ReadAllText(_filePath); |
|
||||
// 정규식이나 아주 심플한 파싱으로 키와 배열 파싱
|
|
||||
// 형식 예: "Maker": ["MOBI", "TEST"]
|
|
||||
var matches = Regex.Matches(text, @"""([^""]+)""\s*:\s*\[([^\]]*)\]"); |
|
||||
foreach (Match match in matches) |
|
||||
{ |
|
||||
var key = match.Groups[1].Value; |
|
||||
var arrayContent = match.Groups[2].Value; |
|
||||
var valueMatches = Regex.Matches(arrayContent, @"""([^""]*)"""); |
|
||||
|
|
||||
var list = new List<string>(); |
|
||||
foreach (Match valMatch in valueMatches) |
|
||||
{ |
|
||||
list.Add(valMatch.Groups[1].Value); |
|
||||
} |
|
||||
_history[key] = list; |
|
||||
} |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
// 로드 오류 시 무시
|
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 4. LoginAccountRepository
|
|
||||
public class LoginAccountRepository |
|
||||
{ |
|
||||
private readonly DatabaseSettings _dbSettings; |
|
||||
private readonly LoginSettings _loginSettings; |
|
||||
|
|
||||
public LoginAccountRepository(DatabaseSettings dbSettings, LoginSettings loginSettings) |
|
||||
{ |
|
||||
_dbSettings = dbSettings; |
|
||||
_loginSettings = loginSettings; |
|
||||
} |
|
||||
|
|
||||
public async Task<bool> ExistsAsync(string operatorId, string password) |
|
||||
{ |
|
||||
// 오프라인 모드인 경우 기본 성공
|
|
||||
if (_loginSettings.OfflinePreview) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
// DB 커넥션 스트링이 비어있으면 기본값 확인
|
|
||||
if (string.IsNullOrWhiteSpace(_dbSettings.ConnectionString)) |
|
||||
{ |
|
||||
return IsDefaultAccount(operatorId, password); |
|
||||
} |
|
||||
|
|
||||
try |
|
||||
{ |
|
||||
// 실제 DB에서 계정을 조회해보기 위한 시도
|
|
||||
using (var conn = new System.Data.SqlClient.SqlConnection(_dbSettings.ConnectionString)) |
|
||||
{ |
|
||||
await conn.OpenAsync(); |
|
||||
|
|
||||
// 우선 Users 또는 Accounts와 같이 로그인 테이블명이 뭔지 모를 수 있으므로
|
|
||||
// 쿼리 실패 시 default 계정 검증으로 fallback 하도록 구성
|
|
||||
const string query = "SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id AND [Password] = @Pw"; |
|
||||
using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn)) |
|
||||
{ |
|
||||
cmd.Parameters.AddWithValue("@Id", operatorId); |
|
||||
cmd.Parameters.AddWithValue("@Pw", password); |
|
||||
|
|
||||
var count = (int)await cmd.ExecuteScalarAsync(); |
|
||||
return count > 0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
catch |
|
||||
{ |
|
||||
// DB 연결이나 쿼리 오류 발생 시, 테스트 편의를 위해 test/test 혹은 admin/admin 등 디폴트 계정을 제공
|
|
||||
return IsDefaultAccount(operatorId, password); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private bool IsDefaultAccount(string operatorId, string password) |
|
||||
{ |
|
||||
// 기본 개발/테스트용 계정 처리
|
|
||||
if (string.Equals(operatorId, "test", StringComparison.OrdinalIgnoreCase) && |
|
||||
string.Equals(password, "test", StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
if (string.Equals(operatorId, "admin", StringComparison.OrdinalIgnoreCase) && |
|
||||
string.Equals(password, "admin", StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
if (string.Equals(operatorId, "su", StringComparison.OrdinalIgnoreCase) && |
|
||||
string.Equals(password, "su", StringComparison.OrdinalIgnoreCase)) |
|
||||
{ |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,82 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
|
||||
|
namespace Housing.Services |
||||
|
{ |
||||
|
public class LoginSettings |
||||
|
{ |
||||
|
public bool OfflinePreview { get; set; } = true; // 기본값은 오프라인 테스트 편의를 위해 true
|
||||
|
public string Procedure { get; set; } = string.Empty; |
||||
|
public string ProcessName { get; set; } = string.Empty; |
||||
|
|
||||
|
private static readonly object _loginLock = new object(); |
||||
|
private static LoginSettings _cachedSettings; |
||||
|
private static DateTime _lastWriteTime; |
||||
|
private static string _cachedPath; |
||||
|
|
||||
|
public static LoginSettings Load(string path) |
||||
|
{ |
||||
|
lock (_loginLock) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
if (File.Exists(path)) |
||||
|
{ |
||||
|
var currentWriteTime = File.GetLastWriteTime(path); |
||||
|
if (_cachedSettings != null && _cachedPath == path && _lastWriteTime == currentWriteTime) |
||||
|
{ |
||||
|
return _cachedSettings; |
||||
|
} |
||||
|
|
||||
|
var settings = new LoginSettings(); |
||||
|
var lines = File.ReadAllLines(path); |
||||
|
foreach (var line in lines) |
||||
|
{ |
||||
|
var trimmed = line.Trim(); |
||||
|
if (trimmed.StartsWith("OfflinePreview", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
if (bool.TryParse(parts[1].Trim(), out var val)) |
||||
|
{ |
||||
|
settings.OfflinePreview = val; |
||||
|
} |
||||
|
else if (int.TryParse(parts[1].Trim(), out var intVal)) |
||||
|
{ |
||||
|
settings.OfflinePreview = intVal != 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
else if (trimmed.StartsWith("Procedure", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.Procedure = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
else if (trimmed.StartsWith("ProcessName", StringComparison.OrdinalIgnoreCase)) |
||||
|
{ |
||||
|
var parts = trimmed.Split(new[] { '=' }, 2); |
||||
|
if (parts.Length == 2) |
||||
|
{ |
||||
|
settings.ProcessName = parts[1].Trim(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
_cachedSettings = settings; |
||||
|
_lastWriteTime = currentWriteTime; |
||||
|
_cachedPath = path; |
||||
|
return settings; |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
System.Diagnostics.Debug.WriteLine($"[LoginSettings Load Error] {ex.Message}"); |
||||
|
} |
||||
|
return _cachedSettings ?? new LoginSettings(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System.Windows; |
||||
|
|
||||
|
namespace leak_test_project.Utils |
||||
|
{ |
||||
|
public interface IDialogService |
||||
|
{ |
||||
|
void ShowMessage(string message, string title = "알림"); |
||||
|
void ShowWarning(string message, string title = "경고"); |
||||
|
} |
||||
|
|
||||
|
public class DefaultDialogService : IDialogService |
||||
|
{ |
||||
|
public void ShowMessage(string message, string title = "알림") |
||||
|
{ |
||||
|
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Information); |
||||
|
} |
||||
|
|
||||
|
public void ShowWarning(string message, string title = "경고") |
||||
|
{ |
||||
|
MessageBox.Show(message, title, MessageBoxButton.OK, MessageBoxImage.Warning); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
; ===================================================================== |
||||
|
; Mobi Leak Test System - 데이터베이스 및 로그인 설정 파일 (Database.ini) |
||||
|
; ===================================================================== |
||||
|
|
||||
|
; [데이터베이스 접속 상세 설정] |
||||
|
; Db_Server : 중앙 MSSQL 데이터베이스 서버의 IP 주소 또는 호스트명 |
||||
|
; Db_Name : 검사 데이터를 저장할 대상 데이터베이스 이름 |
||||
|
; Db_User : 데이터베이스 접속 계정 ID (예: sa) |
||||
|
; Db_Password: 데이터베이스 접속 계정의 비밀번호 |
||||
|
Db_Server=192.168.0.20 |
||||
|
Db_Name=NE1aW_PT_SENSOR |
||||
|
Db_User=sa |
||||
|
Db_Password=sa1234 |
||||
|
|
||||
|
|
||||
|
[Login] |
||||
|
; Procedure: 로그인 검증에 사용할 DB 저장 프로시저명입니다. (공란일 경우 기본 테이블 조회 쿼리 작동) |
||||
|
Procedure=dbo.CheckOperator |
||||
|
|
||||
|
; ProcessName: 로그인 검증 시 전달할 공정 이름입니다. |
||||
|
ProcessName=EOL |
||||
|
|
||||
|
; OfflinePreview: 오프라인 모드 활성화 여부 (true = DB 연결 없이 로그인 통과, false = 실제 DB 인증) |
||||
|
OfflinePreview=false |
||||
@ -0,0 +1,24 @@ |
|||||
|
; ===================================================================== |
||||
|
; Mobi Leak Test System - 데이터베이스 및 로그인 설정 파일 (Database.ini) |
||||
|
; ===================================================================== |
||||
|
|
||||
|
; [데이터베이스 접속 상세 설정] |
||||
|
; Db_Server : 중앙 MSSQL 데이터베이스 서버의 IP 주소 또는 호스트명 |
||||
|
; Db_Name : 검사 데이터를 저장할 대상 데이터베이스 이름 |
||||
|
; Db_User : 데이터베이스 접속 계정 ID (예: sa) |
||||
|
; Db_Password: 데이터베이스 접속 계정의 비밀번호 |
||||
|
Db_Server=192.168.0.20 |
||||
|
Db_Name=NE1aW_PT_SENSOR |
||||
|
Db_User=sa |
||||
|
Db_Password=sa1234 |
||||
|
|
||||
|
|
||||
|
[Login] |
||||
|
; Procedure: 로그인 검증에 사용할 DB 저장 프로시저명입니다. (공란일 경우 기본 테이블 조회 쿼리 작동) |
||||
|
Procedure=dbo.CheckOperator |
||||
|
|
||||
|
; ProcessName: 로그인 검증 시 전달할 공정 이름입니다. |
||||
|
ProcessName=EOL |
||||
|
|
||||
|
; OfflinePreview: 오프라인 모드 활성화 여부 (true = DB 연결 없이 로그인 통과, false = 실제 DB 인증) |
||||
|
OfflinePreview=false |
||||
@ -0,0 +1,2 @@ |
|||||
|
[11:13:29.052] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o |
||||
|
[11:13:30.394] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801 |
||||
@ -0,0 +1,8 @@ |
|||||
|
[10:17:36.444] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o |
||||
|
[10:17:37.692] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801 |
||||
|
[10:17:39.458] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o |
||||
|
[10:17:40.707] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801 |
||||
|
[10:17:43.159] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o |
||||
|
[10:17:44.414] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801 |
||||
|
[10:17:46.175] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o |
||||
|
[10:17:47.441] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801 |
||||
@ -1 +1 @@ |
|||||
f74477eeb98a106556f1b65e440f8cfa8362ac43ae60c03174d4f2bb878b7a02 |
628713ffbc5f55cd805898b1d31f7980abb2a5a8c3c1aad9e2f31b3c27ae4897 |
||||
|
|||||
@ -1,62 +1,2 @@ |
|||||
//------------------------------------------------------------------------------
|
|
||||
// <auto-generated>
|
|
||||
// 이 코드는 도구를 사용하여 생성되었습니다.
|
|
||||
// 런타임 버전:4.0.30319.42000
|
|
||||
//
|
|
||||
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
|
|
||||
// 이러한 변경 내용이 손실됩니다.
|
|
||||
// </auto-generated>
|
|
||||
//------------------------------------------------------------------------------
|
|
||||
|
|
||||
namespace XamlGeneratedNamespace { |
|
||||
|
|
||||
|
|
||||
/// <summary>
|
|
||||
/// GeneratedInternalTypeHelper
|
|
||||
/// </summary>
|
|
||||
[System.Diagnostics.DebuggerNonUserCodeAttribute()] |
|
||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
|
|||||
@ -1 +1 @@ |
|||||
cbc4c65e0c5c8abdc171ee59e1f11d54d6b7bd62192c87410037a9efab12cf46 |
8765edc9fc5fcfe1db8d41cec7744abfac5abd652e5521541b6bb33dc8fa7af8 |
||||
|
|||||