From c9fd5cf0b91141c7427d54a77bdaac3cff70802b Mon Sep 17 00:00:00 2001 From: seuuung0 Date: Thu, 25 Jun 2026 10:28:23 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=A4=EC=A0=95=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/AGENTS.md | 4 + DB/dbo.EOL_Schema.sql | 82 ++-- leak_test_project/App.xaml.cs | 38 ++ leak_test_project/Database.ini | 24 ++ .../Infrastructure/CommunicationBase.cs | 4 +- .../Manual/.obsidian/workspace.json | 26 +- leak_test_project/Manual/01_시스템_개요.md | 19 +- leak_test_project/Manual/02_초기_설정.md | 47 ++- leak_test_project/Manual/03_메인_화면.md | 60 +-- leak_test_project/Manual/04_통신_설정.md | 13 +- leak_test_project/Manual/05_시험_설정.md | 9 +- leak_test_project/Manual/06_데이터_조회.md | 99 +++++ leak_test_project/Manual/07_데이터_조회.md | 99 ----- ...8_시험_프로세스.md => 07_시험_프로세스.md} | 44 ++- leak_test_project/Manual/08_로그_관리.md | 219 +++++++++++ leak_test_project/Manual/09_로그_관리.md | 150 -------- .../{10_오류_대응.md => 09_오류_대응.md} | 21 +- .../Manual/Pasted image 20260624104139.png | Bin 0 -> 45899 bytes .../Manual/Pasted image 20260624104208.png | Bin 0 -> 50405 bytes .../Manual/Pasted image 20260624104256.png | Bin 0 -> 11018 bytes .../Manual/Pasted image 20260624104353.png | Bin 0 -> 27947 bytes .../Manual/Pasted image 20260624104445.png | Bin 0 -> 8315 bytes .../Manual/Pasted image 20260624104506.png | Bin 0 -> 24608 bytes .../Manual/Pasted image 20260624104531.png | Bin 0 -> 17388 bytes .../Manual/Pasted image 20260624104558.png | Bin 0 -> 31216 bytes leak_test_project/Manual/사용자_메뉴얼.md | 16 +- leak_test_project/Models/ProjectModels.cs | 21 +- leak_test_project/Services/Board4251.cs | 61 +-- .../Services/DatabaseSettings.cs | 122 ++++++ .../Services/EolDatabaseService.cs | 82 +++- .../Services/IIdSensorService.cs | 2 +- .../Services/LoginAccountRepository.cs | 99 +++++ .../Services/LoginHistoryStore.cs | 147 +++++++ .../Services/LoginServicesStub.cs | 295 -------------- leak_test_project/Services/LoginSettings.cs | 82 ++++ .../Services/TestProcessService.cs | 170 +++++++-- leak_test_project/Utils/ConfigHelper.cs | 36 +- leak_test_project/Utils/DialogService.cs | 23 ++ leak_test_project/Utils/LogUtils.cs | 361 +++++++++++++++--- leak_test_project/ViewModels/DataViewModel.cs | 31 +- leak_test_project/ViewModels/HomeViewModel.cs | 14 +- leak_test_project/Views/DataView.xaml | 138 ++++--- .../Views/StartupLoginWindow.xaml | 8 - .../Views/StartupLoginWindow.xaml.cs | 2 +- leak_test_project/bin/Debug/Database.ini | 24 ++ .../Debug/app.publish/leak_test_project.exe | Bin 219136 -> 237568 bytes .../bin/Debug/leak_test_project.application | 2 +- .../bin/Debug/leak_test_project.exe | Bin 219136 -> 237568 bytes .../bin/Debug/leak_test_project.exe.manifest | 4 +- .../bin/Debug/leak_test_project.pdb | Bin 372224 -> 400896 bytes leak_test_project/bin/Release/Database.ini | 24 ++ .../bin/Release/Logs/2026-06-18_system.log | 2 + .../bin/Release/Logs/2026-06-25_system.log | 8 + .../Release/app.publish/leak_test_project.exe | Bin 190464 -> 210432 bytes leak_test_project/bin/Release/config.xml | 3 +- .../bin/Release/leak_test_project.application | 2 +- .../bin/Release/leak_test_project.exe | Bin 190464 -> 210432 bytes .../Release/leak_test_project.exe.manifest | 4 +- .../bin/Release/leak_test_project.pdb | Bin 357888 -> 386560 bytes leak_test_project/leak_test_project.csproj | 12 +- leak_test_project/obj/Debug/App.baml | Bin 2557 -> 2557 bytes leak_test_project/obj/Debug/MainWindow.baml | Bin 7432 -> 7432 bytes .../obj/Debug/Views/CommunicationWindow.baml | Bin 5193 -> 5193 bytes .../obj/Debug/Views/DataView.baml | Bin 7807 -> 9669 bytes .../obj/Debug/Views/DataView.g.cs | 6 +- .../obj/Debug/Views/HomeView.baml | Bin 12565 -> 12565 bytes .../obj/Debug/Views/ParametersWindow.baml | Bin 4194 -> 4194 bytes .../obj/Debug/Views/StartupLoginWindow.baml | Bin 14388 -> 14208 bytes .../obj/Debug/Views/StartupLoginWindow.g.cs | 22 +- .../obj/Debug/leak_test_project.application | 2 +- ...est_project.csproj.CoreCompileInputs.cache | 2 +- ...k_test_project.csproj.FileListAbsolute.txt | 1 + .../obj/Debug/leak_test_project.exe | Bin 219136 -> 237568 bytes .../obj/Debug/leak_test_project.exe.manifest | 4 +- .../obj/Debug/leak_test_project.g.resources | Bin 54736 -> 56418 bytes .../obj/Debug/leak_test_project.pdb | Bin 372224 -> 400896 bytes .../leak_test_project_MarkupCompile.cache | 8 +- leak_test_project/obj/Release/App.baml | Bin 2000 -> 2000 bytes ...gnTimeResolveAssemblyReferencesInput.cache | Bin 2735 -> 2798 bytes .../Release/GeneratedInternalTypeHelper.g.cs | 62 +-- leak_test_project/obj/Release/MainWindow.baml | Bin 5029 -> 5029 bytes .../Release/Views/CommunicationWindow.baml | Bin 3452 -> 3452 bytes .../obj/Release/Views/DataView.baml | Bin 5480 -> 6623 bytes .../obj/Release/Views/DataView.g.cs | 6 +- .../obj/Release/Views/DataView.g.i.cs | 6 +- .../obj/Release/Views/HomeView.baml | Bin 7789 -> 7789 bytes .../obj/Release/Views/ParametersWindow.baml | Bin 2886 -> 2886 bytes .../obj/Release/Views/StartupLoginWindow.baml | Bin 8617 -> 8509 bytes .../obj/Release/Views/StartupLoginWindow.g.cs | 22 +- .../Release/Views/StartupLoginWindow.g.i.cs | 22 +- .../obj/Release/leak_test_project.application | 2 +- ...est_project.csproj.AssemblyReference.cache | Bin 8933 -> 3244 bytes ...est_project.csproj.CoreCompileInputs.cache | 2 +- ...k_test_project.csproj.FileListAbsolute.txt | 1 + .../obj/Release/leak_test_project.exe | Bin 190464 -> 210432 bytes .../Release/leak_test_project.exe.manifest | 4 +- .../obj/Release/leak_test_project.g.resources | Bin 35853 -> 36888 bytes .../obj/Release/leak_test_project.pdb | Bin 357888 -> 386560 bytes .../leak_test_project_MarkupCompile.cache | 8 +- .../leak_test_project_MarkupCompile.lref | 2 +- ob/01_System_Architecture.md | 135 +++++++ ob/02_DB_Design_ERD.md | 99 +++++ ob/03_API_Specification.md | 169 ++++++++ ob/04_Security_Scalability.md | 64 ++++ ob/05_Implementation_History.md | 106 +++++ ob/README.md | 10 + ob/System_Design_Guide.md | 40 ++ ob/db_login_integration.md | 151 ++++++++ 108 files changed, 2709 insertions(+), 1000 deletions(-) create mode 100644 .agents/AGENTS.md create mode 100644 leak_test_project/Database.ini create mode 100644 leak_test_project/Manual/06_데이터_조회.md delete mode 100644 leak_test_project/Manual/07_데이터_조회.md rename leak_test_project/Manual/{08_시험_프로세스.md => 07_시험_프로세스.md} (73%) create mode 100644 leak_test_project/Manual/08_로그_관리.md delete mode 100644 leak_test_project/Manual/09_로그_관리.md rename leak_test_project/Manual/{10_오류_대응.md => 09_오류_대응.md} (79%) create mode 100644 leak_test_project/Manual/Pasted image 20260624104139.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104208.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104256.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104353.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104445.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104506.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104531.png create mode 100644 leak_test_project/Manual/Pasted image 20260624104558.png create mode 100644 leak_test_project/Services/DatabaseSettings.cs create mode 100644 leak_test_project/Services/LoginAccountRepository.cs create mode 100644 leak_test_project/Services/LoginHistoryStore.cs delete mode 100644 leak_test_project/Services/LoginServicesStub.cs create mode 100644 leak_test_project/Services/LoginSettings.cs create mode 100644 leak_test_project/Utils/DialogService.cs create mode 100644 leak_test_project/bin/Debug/Database.ini create mode 100644 leak_test_project/bin/Release/Database.ini create mode 100644 leak_test_project/bin/Release/Logs/2026-06-18_system.log create mode 100644 leak_test_project/bin/Release/Logs/2026-06-25_system.log create mode 100644 ob/01_System_Architecture.md create mode 100644 ob/02_DB_Design_ERD.md create mode 100644 ob/03_API_Specification.md create mode 100644 ob/04_Security_Scalability.md create mode 100644 ob/05_Implementation_History.md create mode 100644 ob/README.md create mode 100644 ob/System_Design_Guide.md create mode 100644 ob/db_login_integration.md diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md new file mode 100644 index 0000000..be62410 --- /dev/null +++ b/.agents/AGENTS.md @@ -0,0 +1,4 @@ +# 프로젝트 개발 지침 + +- **개발 전 단계**: 작업이나 구현 계획을 세우기 전, 반드시 `ob/` 폴더 내에 있는 시스템 설계 문서 및 관련 문서들을 상세히 읽어 요구사항과 설계 방향을 확인해야 합니다. +- **개발 후 단계**: 코드 변경 또는 신규 기능 개발 완료 후, 작업 결과와 관련 설계 변경점을 `ob/` 폴더 내의 해당 문서에 성실히 업데이트하고 기록해야 합니다. diff --git a/DB/dbo.EOL_Schema.sql b/DB/dbo.EOL_Schema.sql index b20f827..3b53df3 100644 --- a/DB/dbo.EOL_Schema.sql +++ b/DB/dbo.EOL_Schema.sql @@ -1,10 +1,11 @@ -- ============================================================================ -- Project: NE1aW 온압센서 --- Document: dbo.EOL Table Schema (Simplified Version) +-- Document: dbo.EOL Table Schema (Latest Version) -- Author: Antigravity AI --- Date: 2026-05-28 --- Description: 사용자 요구사항(ID, 좌/우, SPEC UL, SPEC LL, 누설값, 판정)에 맞추어 단순화된 스키마. --- 재검사 이력 추적을 위해 Seq(PK) 및 InspectDateTime(일시) 컬럼 추가. +-- Date: 2026-06-24 +-- Description: 개편된 로그인 사양 및 16개 컬럼 결과 저장 구조에 맞춘 스키마. +-- 제품 일련번호(IC_SN)를 주 식별자로 사용하며, +-- 동일 제품 재검사 시 MERGE 문을 이용해 UPSERT 처리됩니다. -- ============================================================================ -- 1. 데이터베이스 존재 여부 확인 및 자동 생성 @@ -26,57 +27,78 @@ BEGIN END GO --- 3. 단순화된 테이블 생성 +-- 3. 개편된 테이블 생성 CREATE TABLE [dbo].[EOL] ( - [InspectDate] NVARCHAR(10) NOT NULL, -- 검사 날짜 (YYYY-MM-DD) - [InspectTime] NVARCHAR(8) NOT NULL, -- 검사 시간 (HH:MM:SS) - [ID] NVARCHAR(30) NOT NULL, -- 센서 ID (ProductId) (기본키) - [Channel] NVARCHAR(10) NOT NULL, -- 좌 / 우 (LEFT / RIGHT) - [SpecUL] NVARCHAR(10) NOT NULL, -- SPEC UL - [SpecLL] NVARCHAR(10) NOT NULL, -- SPEC LL - [LeakValue] NVARCHAR(20) NOT NULL, -- 누설 측정값 (MeasuredValue) - [Result] NVARCHAR(5) NOT NULL, -- 판정 결과 (OK / NG) + [IC_SN] NVARCHAR(10) NOT NULL, -- 제품의 최종 조합 ID (ProductId) (기본키) + [PCB_Barcode] NVARCHAR(10) NOT NULL, -- 고유 식별용 바코드 정보 (UK) + [Maker] NVARCHAR(10) NULL, -- 로그인 메이커 정보 + [Model] NVARCHAR(10) NULL, -- 로그인 모델 정보 + [Variant_1] NVARCHAR(10) NULL, -- 로그인 변형1 정보 + [Variant_2] NVARCHAR(10) NULL, -- 로그인 변형2 정보 + [Operator] NVARCHAR(10) NOT NULL, -- 로그인 검사자 ID + [Production_Date] DATETIME2(0) NOT NULL DEFAULT GETDATE(), -- 검사 일시 + [Line] NVARCHAR(5) NULL, -- 로그인 작업 라인 + [Lot_No] NVARCHAR(8) NULL, -- 로그인 Lot 번호 + [Jig_No] NVARCHAR(10) NULL, -- 로그인 지그 번호 + [Channel] NVARCHAR(5) NOT NULL, -- 검증 수행 채널 (LEFT / RIGHT) + [Spec_UL] DECIMAL(3,2) NOT NULL, -- 스펙 상한값 + [Spec_LL] DECIMAL(3,2) NOT NULL, -- 스펙 하한값 + [Leak_Value] DECIMAL(5,4) NOT NULL, -- 측정된 누설량 + [Result] NVARCHAR(5) NOT NULL, -- 합격 여부 판정 (OK / NG) - CONSTRAINT [PK_EOL_ID] PRIMARY KEY CLUSTERED ([ID] ASC) + CONSTRAINT [PK_EOL_IC_SN] PRIMARY KEY CLUSTERED ([IC_SN] ASC), + CONSTRAINT [UQ_EOL_PCB_Barcode] UNIQUE ([PCB_Barcode] ASC) ); GO -PRINT 'Table [dbo].[EOL] created successfully with simplified columns and ID as Primary Key.'; +PRINT 'Table [dbo].[EOL] created successfully with latest 16 columns and IC_SN as Primary Key.'; GO -- ============================================================================ --- [부록] 테스트 시나리오 SQL 예시 (단순화 버전) +-- [부록] 테스트 시나리오 SQL 예시 (신규 개편 버전) -- ============================================================================ /* -- 1. 테스트용 기본 데이터 INSERT (정상 삽입) INSERT INTO [dbo].[EOL] ( - [InspectDate], [InspectTime], [ID], [Channel], [SpecUL], [SpecLL], [LeakValue], [Result] + [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] ) VALUES ( - '2026-06-02', '14:48:51', 'IC_SIMPLE_0001', 'LEFT', '1.00', '-1.00', '0.125', 'OK' + 'SN00000001', 'SN00000001', 'MAKER_A', 'MODEL_X', 'V1', 'V2', + 'OP_01', '2026-06-24 11:00:00', 'L1', 'LOT00001', 'JIG01', 'LEFT', + 1.00, -1.00, 0.1250, 'OK' ); -- 데이터 확인 SELECT * FROM [dbo].[EOL]; --- 2. 동일 ID 재검사 시 MERGE 문을 이용한 UPSERT 테스트 (정상 업데이트 확인) --- 예상 결과: 기존 'IC_SIMPLE_0001' 레코드의 LeakValue가 '0.098'로 업데이트됨 +-- 2. 동일 IC_SN 재검사 시 MERGE 문을 이용한 UPSERT 테스트 (정상 업데이트 확인) +-- 예상 결과: 기존 'SN00000001' 레코드의 Leak_Value가 '0.0980'으로 업데이트되고, Production_Date 등이 갱신됨 MERGE INTO [dbo].[EOL] AS Target -USING (SELECT 'IC_SIMPLE_0001' AS ID) AS Source -ON (Target.ID = Source.ID) +USING (SELECT 'SN00000001' AS IC_SN) AS Source +ON (Target.IC_SN = Source.IC_SN) WHEN MATCHED THEN UPDATE SET + Target.PCB_Barcode = 'SN00000001', + Target.Maker = 'MAKER_A', + Target.Model = 'MODEL_X', + Target.Variant_1 = 'V1', + Target.Variant_2 = 'V2', + Target.Operator = 'OP_01', + Target.Production_Date = '2026-06-24 11:05:00', + Target.Line = 'L1', + Target.Lot_No = 'LOT00001', + Target.Jig_No = 'JIG01', Target.Channel = 'LEFT', - Target.SpecUL = '1.00', - Target.SpecLL = '-1.00', - Target.LeakValue = '0.098', -- 업데이트될 새 값 - Target.Result = 'OK', - Target.InspectDate = '2026-06-02', - Target.InspectTime = '14:48:00' + Target.Spec_UL = 1.00, + Target.Spec_LL = -1.00, + Target.Leak_Value = 0.0980, -- 업데이트될 새 값 + Target.Result = 'OK' WHEN NOT MATCHED THEN - INSERT ([ID], [Channel], [SpecUL], [SpecLL], [LeakValue], [Result], [InspectDate], [InspectTime]) - VALUES ('IC_SIMPLE_0001', 'LEFT', '1.00', '-1.00', '0.098', 'OK', '2026-06-02', '14:48:00'); + INSERT (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) + VALUES ('SN00000001', 'SN00000001', 'MAKER_A', 'MODEL_X', 'V1', 'V2', 'OP_01', '2026-06-24 11:05:00', 'L1', 'LOT00001', 'JIG01', 'LEFT', 1.00, -1.00, 0.0980, 'OK'); -- 데이터 확인 (값 업데이트 확인) SELECT * FROM [dbo].[EOL]; diff --git a/leak_test_project/App.xaml.cs b/leak_test_project/App.xaml.cs index 569508d..addd58c 100644 --- a/leak_test_project/App.xaml.cs +++ b/leak_test_project/App.xaml.cs @@ -19,6 +19,44 @@ namespace leak_test_project { base.OnStartup(e); + // Database.ini 파일이 없는 경우 기본 템플릿 생성 + try + { + string dbIniPath = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "Database.ini"); + if (!System.IO.File.Exists(dbIniPath)) + { + string defaultContent = + "; =====================================================================\r\n" + + "; Mobi Leak Test System - 데이터베이스 및 로그인 설정 파일 (Database.ini)\r\n" + + "; =====================================================================\r\n" + + "\r\n" + + "; [데이터베이스 접속 상세 설정]\r\n" + + "; Db_Server : 중앙 MSSQL 데이터베이스 서버의 IP 주소 또는 호스트명\r\n" + + "; Db_Name : 검사 데이터를 저장할 대상 데이터베이스 이름\r\n" + + "; Db_User : 데이터베이스 접속 계정 ID (예: sa)\r\n" + + "; Db_Password: 데이터베이스 접속 계정의 비밀번호\r\n" + + "Db_Server=192.168.0.20\r\n" + + "Db_Name=NE1aW_PT_SENSOR\r\n" + + "Db_User=sa\r\n" + + "Db_Password=sa1234\r\n" + + "\r\n" + + "[Login]\r\n" + + "; Procedure: 로그인 검증에 사용할 DB 저장 프로시저명입니다. (공란일 경우 기본 테이블 조회 쿼리 작동)\r\n" + + "Procedure=dbo.CheckOperator\r\n" + + "\r\n" + + "; ProcessName: 로그인 검증 시 전달할 공정 이름입니다.\r\n" + + "ProcessName=EOL\r\n" + + "\r\n" + + "; OfflinePreview: 오프라인 모드 활성화 여부 (true = DB 연결 없이 로그인 통과, false = 실제 DB 인증)\r\n" + + "OfflinePreview=false\r\n"; + System.IO.File.WriteAllText(dbIniPath, defaultContent, System.Text.Encoding.UTF8); + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[App Database.ini Create Error] {ex.Message}"); + } + // 메인 윈도우 생성 및 즉시 표시 var mainWindow = new MainWindow(); this.MainWindow = mainWindow; diff --git a/leak_test_project/Database.ini b/leak_test_project/Database.ini new file mode 100644 index 0000000..3491e9c --- /dev/null +++ b/leak_test_project/Database.ini @@ -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 diff --git a/leak_test_project/Infrastructure/CommunicationBase.cs b/leak_test_project/Infrastructure/CommunicationBase.cs index ad448e8..500490f 100644 --- a/leak_test_project/Infrastructure/CommunicationBase.cs +++ b/leak_test_project/Infrastructure/CommunicationBase.cs @@ -122,7 +122,9 @@ namespace leak_test_project.Infrastructure _serialPort.DiscardInBuffer(); _serialPort.DiscardOutBuffer(); _serialPort.ReadExisting(); - } catch { } + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine($"[CommunicationBase ClearBuffer Error] {ex.Message}"); + } } } diff --git a/leak_test_project/Manual/.obsidian/workspace.json b/leak_test_project/Manual/.obsidian/workspace.json index e608e0d..641ea91 100644 --- a/leak_test_project/Manual/.obsidian/workspace.json +++ b/leak_test_project/Manual/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "사용자_메뉴얼.md", - "mode": "preview", + "file": "07_시험_프로세스.md", + "mode": "source", "source": false }, "icon": "lucide-file", - "title": "사용자_메뉴얼" + "title": "07_시험_프로세스" } } ] @@ -185,11 +185,25 @@ }, "active": "7fc461cd97381c41", "lastOpenFiles": [ - "10_오류_대응.md", - "09_로그_관리.md", + "06_데이터_조회.md", + "Pasted image 20260624104558.png", + "05_시험_설정.md", + "Pasted image 20260624104531.png", + "04_통신_설정.md", + "Pasted image 20260624104506.png", "03_메인_화면.md", + "Pasted image 20260624104445.png", + "Pasted image 20260624104353.png", + "Pasted image 20260624104256.png", + "Pasted image 20260624104208.png", + "Pasted image 20260624104139.png", "02_초기_설정.md", "01_시스템_개요.md", - "사용자_메뉴얼.md" + "08_로그_관리.md", + "09_오류_대응.md", + "사용자_메뉴얼.md", + "07_시험_프로세스.md", + "10_오류_대응.md", + "09_로그_관리.md" ] } \ No newline at end of file diff --git a/leak_test_project/Manual/01_시스템_개요.md b/leak_test_project/Manual/01_시스템_개요.md index 21afbfc..65306e9 100644 --- a/leak_test_project/Manual/01_시스템_개요.md +++ b/leak_test_project/Manual/01_시스템_개요.md @@ -17,7 +17,6 @@ | **Sentinel C28 연동** | RS-232 시리얼 통신으로 누설 센서의 실시간 측정값을 수신합니다 | | **4251 보드 연동** | 제품 ID(ZMDI 센서) 판독을 위한 시리얼 통신을 수행합니다 | | **PCI-7432 DIO 보드** | 디지털 입출력 제어 (시작 신호 수신, OK/NG 출력) | -| **실시간 I/O 모니터링** | 디지털 입출력 상태를 500ms 주기로 실시간 감시합니다 | | **검사 기록 관리** | 날짜/판정/ID별 필터링 검색 및 CSV 파일 내보내기를 지원합니다 | | **SPEC 교차 검증** | 프로그램 판정과 센서 자체 판정을 비교하여 불일치 시 경고합니다 | | **순차 실행 보장** | 세마포어 잠금으로 좌/우 채널 동시 시험을 방지합니다 | @@ -98,23 +97,23 @@ ``` ┌────────────────────────────────────────────────┐ -│ 상단 타이틀 바 (프로그램명, 시간, 창 제어 버튼) │ +│ 상단 타이틀 바 (프로그램명, 시간, 배지, 창 제어)│ ├────────────────────────────────────────────────┤ │ │ │ 메인 콘텐츠 영역 │ -│ (Home / I/O Monitor / Data View 전환) │ +│ (Home / Data View 전환) │ │ │ ├────────────────────────────────────────────────┤ -│ 하단 메뉴 바 (I/O Monitor, Data, Parameters, │ -│ Comm. Settings, ID Test L/R) │ +│ 하단 메뉴 바 (Data, Parameters, Comm.Settings,│ +│ ID Test L/R) │ └────────────────────────────────────────────────┘ ``` -| 영역 | 높이 | 설명 | -|------|------|------| -| 상단 타이틀 바 | 55px | 프로그램 제목, 현재 시간, 버전 정보, 창 제어 버튼 | -| 메인 콘텐츠 | 가변 | 선택된 화면(Home/I-O/Data)이 표시되는 영역 | -| 하단 메뉴 바 | 75px | 화면 전환 및 설정 팝업 버튼 | +| 영역 | 설명 | +|------|------| +| 상단 타이틀 바 | 프로그램 제목, 실시간 날짜/시간, 현재 로그인된 작업자/모델/라인 정보 배지, 세션 로그아웃 버튼, 시스템 창 제어 버튼 배치 | +| 메인 콘텐츠 영역 | 선택된 기능 화면(Home 검사 모니터 또는 Data 데이터 조회)이 표시되는 주 콘텐츠 영역 | +| 하단 메뉴 바 | 각 화면 전환 및 설정을 위한 조작 버튼 영역 | --- diff --git a/leak_test_project/Manual/02_초기_설정.md b/leak_test_project/Manual/02_초기_설정.md index 8a58ffe..dbe0b8a 100644 --- a/leak_test_project/Manual/02_초기_설정.md +++ b/leak_test_project/Manual/02_초기_설정.md @@ -53,7 +53,50 @@ --- -## 2.3 최초 설정 순서 (Step-by-Step) +## 2.3 프로그램 로그인 및 오프라인 모드 설정 + +프로그램 실행이 완료되면 메인 화면이 활성화되기 전, **작업 정보 입력 및 로그인 창**이 모달로 표시됩니다. 로그인을 성공적으로 마쳐야 메인 화면을 조작할 수 있습니다. + +### 로그인 필드 구성 + +| 입력 필드 | 예시 | 설명 | +|-----------|------|------| +| **Maker** | `MOBIS` | 제조사 정보 | +| **Model** | `C28_TEST` | 검사 모델명 | +| **Variant 1** | `V1` | 세부 파생 모델 정보 1 | +| **Variant 2** | `V2` | 세부 파생 모델 정보 2 | +| **ID (Operator)** | `sa` | 작업자 ID | +| **PW (Password)** | `****` | 비밀번호 (DB 조회용) | +| **Line** | `EOL_01` | 생산 라인 번호 | +| **Lot No** | `LOT001` | 로트 번호 | +| **Jig No** | `JIG_L` | 지그 번호 | + +> **참고 (자동완성)**: 각 필드에 마우스를 클릭하거나 포커스를 두면, 이전 로그인 이력이 팝업 메뉴 형식으로 나열되어 간편하게 자동 완성을 사용할 수 있습니다. + +### 로그인 방식 설정 (Database.ini) + +프로그램 실행 폴더에 위치한 `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 +``` + +- **온라인 인증 모드 (`OfflinePreview=false`)**: 지정된 개별 데이터베이스 접속 정보(`Db_Server`, `Db_Name`, `Db_User`, `Db_Password`)를 기반으로 데이터베이스에 접속하여 Stored Procedure(`Procedure`)로 ID/PW 존재 여부를 직접 확인합니다. DB 서버 연결 실패나 미등록 사용자일 경우 로그인이 거부됩니다. +- **오프라인 테스트 모드 (`OfflinePreview=true`)**: 데이터베이스를 거치지 않고, 필수 입력 값을 채우면 즉시 로그인이 허용됩니다. 네트워크 연결이 안 된 시뮬레이션 및 테스트 환경에서 사용합니다. + + +--- + +## 2.4 최초 설정 순서 (Step-by-Step) 프로그램을 처음 사용할 때는 반드시 아래 순서로 설정을 진행하세요. @@ -122,7 +165,7 @@ --- -## 2.4 설정 변경 시 주의사항 +## 2.5 설정 변경 시 주의사항 | 상황 | 동작 | 주의 | |------|------|------| diff --git a/leak_test_project/Manual/03_메인_화면.md b/leak_test_project/Manual/03_메인_화면.md index 06afc2b..4dd5b61 100644 --- a/leak_test_project/Manual/03_메인_화면.md +++ b/leak_test_project/Manual/03_메인_화면.md @@ -7,14 +7,15 @@ 프로그램 실행 시 기본으로 표시되는 화면입니다. 좌/우 채널의 누설 시험 결과를 실시간으로 모니터링합니다. ## 3.1 전체 화면 레이아웃 +![[Pasted image 20260624104208.png]] ``` ┌────────────────────────────────────────────────────────────────┐ -│ Pressure Leak Inspect System 2026-05-08 [목] 10:30:00 ─□X │ ← 상단 타이틀 바 +│ Pressure Leak v1.0.0 2026-06-23 [화] 10:30:00 [작업자][모델][라인] [로그아웃] ─□X│ ← 타이틀 바 ├─────────────────────────────┬──────────────────────────────────┤ │ LEFT LEAK TESTER │ RIGHT LEAK TESTER │ ├─────────────────────────────┼──────────────────────────────────┤ -│ 시작 시간 │ 2026-05-08 ... │ 시작 시간 │ 2026-05-08 ... │ +│ 시작 시간 │ 2026-06-23 ... │ 시작 시간 │ 2026-06-23 ... │ │ ID/LOW ID │ ABC123 / 00FF │ ID/LOW ID │ DEF456 / 01AA │ │ 측정값 │ 0.5234 sccm │ 측정값 │ 0.3124 sccm │ │ │ SPEC UL: 1.00 │ │ SPEC UL: 1.00 │ @@ -31,14 +32,16 @@ ## 3.2 상단 타이틀 바 -프로그램 최상단에 위치한 짙은 남색(#2C3E50) 배경의 바입니다. +프로그램 최상단에 위치한 짙은 블랙 그레이 배경의 바입니다. +![[Pasted image 20260624104256.png]] | 위치 | 요소 | 설명 | |------|------|------| -| 좌측 | **프로그램 제목** | "Pressure Leak Inspect System" (백색, 24pt 볼드) | -| 중앙~우측 | **현재 날짜/시간** | `2026-05-08 [목] 10:30:00` 형식, 1초 주기로 실시간 갱신 | -| 우측 | **버전 정보** | `Ver. 1.0.0 | Updated 2026-04-03` (회색 텍스트) | -| 최우측 | **창 제어 버튼** | 최소화(─) / 최대화·복원(□) / 종료(X, 빨간색) | +| 좌측 | **프로그램 제목 & 버전** | "Pressure Leak" (백색 볼드) 및 `v1.0.0 (Updated: 2026-06-05)` 표시 | +| 중앙 | **현재 날짜/시간** | `yyyy-MM-dd [요일] HH:mm:ss` 형식, 1초 주기로 실시간 갱신 | +| 우측 | **작업 정보 배지** | 현재 로그인 상태에 따른 `작업자 ID`, `검사 모델`, `생산 라인` 정보를 배지로 시각화하여 현재 작업 상태 인지 | +| 우측 | **로그아웃 버튼** | 현재 세션을 로그아웃하고, 다시 로그인 모달 창을 띄웁니다 | +| 최우측 | **창 제어 버튼** | 최소화(─) / 최대화·복원(□) / 종료(X)를 수행하는 윈도우 창 제어 아이콘 배치 | ### 타이틀 바 조작 @@ -55,13 +58,13 @@ ## 3.3 메인 영역 - 채널 모니터링 화면은 좌측(`LEFT LEAK TESTER`)과 우측(`RIGHT LEAK TESTER`)으로 균등 분할되며, 각 채널은 동일한 구조입니다. +![[Pasted image 20260624104353.png]] ### 각 채널의 구성 요소 (위에서 아래 순서) #### ① 채널 타이틀 (Row 0) - **LEFT LEAK TESTER** 또는 **RIGHT LEAK TESTER** -- 짙은 남색(#34495E) 배경에 백색 볼드 텍스트 (20pt) -- 높이: 50px +- 짙은 남색 배경에 백색 볼드 텍스트로 채널 구분을 명시합니다. #### ② 시작 시간 (Row 1) | 항목 | 설명 | @@ -69,7 +72,6 @@ | 라벨 | `시작 시간` (회색 헤더 배경) | | 값 | `yyyy-MM-dd HH:mm:ss` 형식 (예: `2026-05-08 10:30:15`) | | 갱신 시점 | 시험이 시작될 때마다 현재 시각으로 자동 갱신 | -| 높이 | 42px | #### ③ ID / LOW ID (Row 2) | 항목 | 설명 | @@ -78,7 +80,6 @@ | 값 | `{제품ID} / {Low ID}` 형식으로 조합 표시 | | 데이터 소스 | 4251 보드를 통해 ZMDI 센서에서 읽은 값 | | 갱신 시점 | 시험 시작 시 초기화, 센서 판독 완료 시 표시 | -| 높이 | 42px | #### ④ 측정값 + SPEC (Row 3) - **가장 큰 영역** 좌측에 측정값, 우측에 SPEC 규격이 함께 표시됩니다. @@ -87,29 +88,29 @@ | 항목 | 설명 | |------|------| -| 라벨 | `측정값` (18pt 볼드) | +| 라벨 | `측정값` (볼드) | | 값 | Sentinel C28에서 수신한 누설량 (소수점 4자리, 예: `0.5234`) | | 단위 | `sccm` (우측 하단에 표시) | -| 폰트 크기 | **84pt** (매우 크게 표시하여 원거리에서도 확인 가능) | -| 색상 | 파란색(#2980B9) 볼드 | +| 폰트 크기 | 원거리에서도 쉽게 식별할 수 있도록 대형 폰트로 강조 표시 | +| 색상 | 파란색 볼드 | | 갱신 시점 | C28 센서 스트리밍 데이터 수신 시 실시간 갱신 | -**SPEC 영역 (우측 210px):** +**SPEC 영역 (우측):** | 항목 | 설명 | |------|------| -| `UL` | 상한 규격값 (예: `1.00`), 26pt 볼드 | -| `LL` | 하한 규격값 (예: `-1.00`), 26pt 볼드 | +| `UL` | 상한 규격값 (예: `1.00`) 볼드 | +| `LL` | 하한 규격값 (예: `-1.00`) 볼드 | | 갱신 시점 | Parameters에서 저장 시 즉시 반영 | #### ⑤ 판정 (Row 4) - **두 번째로 큰 영역** | 항목 | 설명 | |------|------| -| 라벨 | `판정` (18pt 볼드) | -| 값 | **OK** 또는 **NG** (96pt, ExtraBold) | -| OK 표시 | 녹색 텍스트(#27AE60) + 연녹색 배경(#D4EFDF) | -| NG 표시 | 빨간색 텍스트(#C0392B) + 연빨간색 배경(#FADBD8) | +| 라벨 | `판정` (볼드) | +| 값 | **OK** 또는 **NG** (대형 볼드) | +| OK 표시 | 녹색 텍스트 + 연녹색 배경으로 가시성 극대화 | +| NG 표시 | 빨간색 텍스트 + 연빨간색 배경으로 가시성 극대화 | > **판정 기준**: `SPEC LL ≤ 측정값 ≤ SPEC UL` → **OK**, 그 외 → **NG** @@ -118,8 +119,7 @@ | 항목 | 설명 | |------|------| | 라벨 | `진행 메시지` (연파랑 배경) | -| 값 | 현재 시험 단계를 나타내는 텍스트 (22pt 볼드, 파란색) | -| 높이 | 60px | +| 값 | 현재 시험 단계를 나타내는 파란색 볼드 텍스트 | 표시되는 메시지 목록: @@ -140,7 +140,7 @@ | 항목 | 설명 | |------|------| | 라벨 | `오류 메시지` (연주황 배경, 갈색 텍스트) | -| 값 | 오류 내용 (빨간색, 18pt 볼드). 스크롤 가능 | +| 값 | 오류 내용 (빨간색 볼드). 스크롤 가능 | | 특징 | 읽기 전용 텍스트 박스, 텍스트 줄바꿈 자동, 세로 스크롤바 | | 초기화 시점 | 새 시험 시작 시 자동 초기화 | @@ -159,9 +159,10 @@ ## 3.5 하단 메뉴 바 -프로그램 최하단의 연회색(#D5DBDB) 배경 영역입니다. 5개의 버튼이 좌측부터 수평으로 배치됩니다. +프로그램 최하단의 연회색 배경 영역입니다. 5개의 버튼이 좌측부터 수평으로 배치됩니다. +![[Pasted image 20260624104445.png]] -| 순서 | 버튼 텍스트 | 부제 | 동작 | 동작 방식 | +| 순서 | BUTTON | 부제 | 동작 | 동작 방식 | |------|------------|------|------|----------| | 1 | **Data** | (측정 데이터) | 검사 기록 조회 화면 전환 | 토글 (다시 클릭 시 Home으로 복귀) | | 2 | **Parameters** | (시험 설정) | 시험 설정 팝업 창 열기 | 모달 팝업 (설정 후 자동 닫힘) | @@ -170,10 +171,9 @@ | 5 | **ID Test (R)** | (우측 ID 테스트) | 우측 채널 센서 ID 수동 판독 | 즉시 실행 (파란색 텍스트) | ### 버튼 스타일 -- 크기: 140px × 55px -- 배경: 연회색(#F2F4F4), 마우스 오버 시 약간 어두워짐(#E5E8E8) -- 테두리: 회색(#85929E), 1.5px -- 텍스트: 14pt 볼드 (부제는 10pt 일반) +- 배경: 연회색, 마우스 오버 시 시각적 피드백을 위해 약간 어둡게 변함 +- 테두리: 회색 +- 텍스트: 볼드 (부제는 일반 텍스트) --- diff --git a/leak_test_project/Manual/04_통신_설정.md b/leak_test_project/Manual/04_통신_설정.md index b56ddb2..6106f19 100644 --- a/leak_test_project/Manual/04_통신_설정.md +++ b/leak_test_project/Manual/04_통신_설정.md @@ -5,7 +5,7 @@ --- 하단 메뉴에서 **Comm. Settings (통신 설정)** 버튼 클릭 시 열리는 **모달 팝업 창**입니다. - +![[Pasted image 20260624104506.png]] ## 4.1 창 구성 ``` @@ -24,9 +24,8 @@ | 속성 | 값 | |------|-----| -| 창 크기 | 750px × 자동 높이 (최대 700px) | -| 크기 변경 | 불가 | -| 위치 | 메인 창 중앙 | +| 창 크기 | 고정 (크기 변경 불가) | +| 위치 | 메인 창 중앙에 정렬되어 표시 | | 타입 | 모달 (이 창이 열려있으면 메인 창 조작 불가) | ## 4.2 4251 보드 설정 (좌측 영역) @@ -45,10 +44,10 @@ ## 4.4 버튼 설명 -| 버튼 | 색상 | 동작 | +| 버튼 | 특징 | 동작 | |------|------|------| -| **설정 저장** | 연녹색 배경, 녹색 테두리 | config.xml에 저장 → 기존 연결 해제 → 새 설정으로 재연결 → "통신 설정이 저장되었습니다." 메시지 → 창 닫힘 | -| **닫기** | 기본 회색 | 변경 사항 저장하지 않고 창 닫기 | +| **설정 저장** | 녹색 계열 | config.xml에 저장 → 기존 연결 해제 → 새 설정으로 재연결 → "통신 설정이 저장되었습니다." 메시지 → 창 닫힘 | +| **닫기** | 회색 계열 | 변경 사항 저장하지 않고 창 닫기 | ## 4.5 저장 시 내부 동작 순서 diff --git a/leak_test_project/Manual/05_시험_설정.md b/leak_test_project/Manual/05_시험_설정.md index 4075c6f..d2b5dfd 100644 --- a/leak_test_project/Manual/05_시험_설정.md +++ b/leak_test_project/Manual/05_시험_설정.md @@ -5,7 +5,7 @@ --- 하단 메뉴에서 **Parameters (시험 설정)** 버튼 클릭 시 열리는 **모달 팝업 창**입니다. - +![[Pasted image 20260624104531.png]] ## 5.1 창 구성 ``` @@ -25,9 +25,8 @@ | 속성 | 값 | |------|-----| -| 창 크기 | 550px × 자동 높이 (최대 600px) | -| 크기 변경 | 불가 | -| 위치 | 메인 창 중앙 | +| 창 크기 | 고정 (크기 변경 불가) | +| 위치 | 메인 창 중앙에 정렬되어 표시 | | 타입 | 모달 | ## 5.2 설정 항목 @@ -67,4 +66,4 @@ --- -[← 이전: 통신 설정](04_통신_설정.md) | [다음: I/O Monitor →](06_IO_모니터.md) +[← 이전: 통신 설정](04_통신_설정.md) | [다음: 측정 데이터 조회 →](06_데이터_조회.md) diff --git a/leak_test_project/Manual/06_데이터_조회.md b/leak_test_project/Manual/06_데이터_조회.md new file mode 100644 index 0000000..f4c3ade --- /dev/null +++ b/leak_test_project/Manual/06_데이터_조회.md @@ -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) diff --git a/leak_test_project/Manual/07_데이터_조회.md b/leak_test_project/Manual/07_데이터_조회.md deleted file mode 100644 index 369537f..0000000 --- a/leak_test_project/Manual/07_데이터_조회.md +++ /dev/null @@ -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) diff --git a/leak_test_project/Manual/08_시험_프로세스.md b/leak_test_project/Manual/07_시험_프로세스.md similarity index 73% rename from leak_test_project/Manual/08_시험_프로세스.md rename to leak_test_project/Manual/07_시험_프로세스.md index b9330f7..2d7c21c 100644 --- a/leak_test_project/Manual/08_시험_프로세스.md +++ b/leak_test_project/Manual/07_시험_프로세스.md @@ -30,40 +30,38 @@ │ 2단계: 센서 정보 읽기 │ │ · 4251 보드를 통해 ZMDI 센서에서 제품 ID 판독 │ │ · ID / LOW ID를 메인 화면에 표시 │ - │ · 읽기 실패해도 빈 데이터("-")로 계속 진행 │ + │ · ID 읽기 실패 시: "ID 읽기 실패" 오류 출력 후 │ + │ "NG" 판정으로 DB 비동기 저장 및 조기 종료 (Skip) │ + │ (DB 저장 실패 시 "DB 저장 실패" 문구 표시) │ └──────────────┬───────────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────┐ - │ 3단계: 불량 제품 필터링 │ - │ · 센서의 PrevResult 값 확인 │ - │ · PrevResult = "F" → 정상 (다음 단계로 진행) │ - │ · PrevResult ≠ "F" → "불량 제품 투입" 오류 → 시험 중단│ - └──────────────┬───────────────────────────────────────┘ - ▼ - ┌──────────────────────────────────────────────────────┐ - │ 4단계: LEAK 시험 수행 │ + │ 3단계: LEAK 시험 수행 │ │ · "LEAK 시험중" 메시지 표시 │ │ · Sentinel C28 센서의 최종 결과(Auto Result) 대기 │ │ · 최대 대기 시간: 30초 │ │ · 타임아웃 시: NG 판정 + "Sentinel C28 통신 타임아웃" │ + │ (DB 저장 실패 시 "DB 저장 실패" 문구 표시) │ └──────────────┬───────────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────┐ - │ 5단계: 판정 │ + │ 4단계: 판정 │ │ · SPEC LL ≤ 측정값 ≤ SPEC UL → OK │ │ · 범위 밖 → NG │ └──────────────┬───────────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────┐ - │ 6단계: SPEC 교차 검증 │ + │ 5단계: SPEC 교차 검증 │ │ · 프로그램 판정과 센서 자체 판정 비교 │ │ · 센서 판정 정규화: OK/A/P → Accept, NG/R/F → Reject │ │ · 불일치 시: SpecMismatch = true → 팝업 경고 │ └──────────────┬───────────────────────────────────────┘ ▼ ┌──────────────────────────────────────────────────────┐ - │ 7단계: 결과 출력 │ + │ 6단계: 결과 출력 및 저장 │ │ · CSV 로그 파일에 검사 데이터 기록 │ + │ · MSSQL 중앙 데이터베이스 비동기 저장 │ + │ · DB 저장 실패 시: 오류 메시지 칸에 "DB 저장 실패" 표시 │ │ · DIO OK 또는 NG 출력 ON │ │ · 메인 화면에 측정값, 판정 결과 표시 │ │ · "시험 완료" 메시지 표시 │ @@ -89,10 +87,10 @@ Sentinel C28 센서에서 30초 이내에 응답이 없으면: -1. "Sentinel C28 통신 타임아웃" 오류 메시지 표시 +1. "Sentinel C28 통신 타임아웃" 오류 메시지 표시 (DB 저장 실패 시 "DB 저장 실패"도 오류 메시지에 추가 표시) 2. 해당 채널 DIO `NG` 출력 ON 3. 측정값 `0.0000`, 판정 `NG`, 센서판정 `T/O` (Timeout)로 기록 -4. CSV 로그 파일에도 NG로 기록 +4. CSV 로그 파일 및 MSSQL 데이터베이스에 NG 결과 비동기 기록 ## 7.5 C28 센서 결과 채널 라우팅 @@ -114,12 +112,24 @@ Sentinel C28 센서에서 수신한 데이터는 채널 번호(ChannelNo)로 구 3. 500ms 대기 (UI 피드백용) 4. "센서 정보 읽는 중" 메시지 표시 5. 4251 보드를 통해 ZMDI 센서 ID 판독 -6. 성공 시: ID / LOW ID 표시 + 불량 제품 필터링 확인 + "ID 테스트 완료" +6. 성공 시: ID / LOW ID 표시 + "ID 테스트 완료" 7. 실패 시: ID를 "-"로 표시 + "ID 테스트 실패" 8. 세마포어 잠금 해제 -> **용도**: 설치 후 하드웨어 연결 상태 확인, 디버깅 시 활용합니다. LEAK 시험(4~7단계)은 수행하지 않습니다. +> **용도**: 설치 후 하드웨어 연결 상태 확인, 디버깅 시 활용합니다. LEAK 시험(3~6단계)은 수행하지 않습니다. + +## 7.7 SPEC 교차 검증 및 판정 저장 기준 + +시험 과정에서 프로그램(UI) 판정과 C28 센서 자체의 판정이 불일치하는 경우, 데이터베이스 및 로그 저장의 기준은 다음과 같습니다. + +### 저장 기준 요약 +* **최종 저장 판정 (Result)**: C28 센서 자체의 판정값과 무관하게, 항상 **프로그램(UI) 판정 기준(`programJudgment`)**으로 데이터베이스 및 로그에 저장됩니다. +* **동작 예시**: UI 상으로 합격(`OK`)이지만 C28 센서 자체적으로는 불합격(`NG`/`R`)인 경우, 최종 DB(`Result` 컬럼) 및 CSV 로그에는 **`OK` (합격)**로 저장됩니다. + +### 교차 검증 실패 시의 동작 (Spec Mismatch) +* 프로그램 판정과 센서 자체 판정이 다르면 `specMismatch` 플래그가 `true`로 설정됩니다. +* 불일치 발생 시 콘솔 로그에 경고(`[TestProcess] {side} SPEC Mismatch!`)를 출력하고 UI 완료 이벤트 인자로 전달되지만, **DB에 저장되는 최종 판정 결과(`Result`)에는 영향을 미치지 않고 프로그램 판정값을 유지**합니다. --- -[← 이전: 측정 데이터 조회](07_데이터_조회.md) | [다음: 로그 관리 →](09_로그_관리.md) +[← 이전: 측정 데이터 조회](06_데이터_조회.md) | [다음: 로그 관리 →](08_로그_관리.md) diff --git a/leak_test_project/Manual/08_로그_관리.md b/leak_test_project/Manual/08_로그_관리.md new file mode 100644 index 0000000..f7acf05 --- /dev/null +++ b/leak_test_project/Manual/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 + + COM1 + 9600 + COM3 + 115200 + 5000 + 1.00 + -1.00 + +``` + +| 설정 키 | 기본값 | 설정 위치 | 설명 | +|---------|--------|----------|------| +| `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) diff --git a/leak_test_project/Manual/09_로그_관리.md b/leak_test_project/Manual/09_로그_관리.md deleted file mode 100644 index 9b45644..0000000 --- a/leak_test_project/Manual/09_로그_관리.md +++ /dev/null @@ -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 - - COM1 - 9600 - COM3 - 115200 - 5000 - 1.00 - -1.00 - -``` - -| 설정 키 | 기본값 | 설정 위치 | 설명 | -|---------|--------|----------|------| -| `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) diff --git a/leak_test_project/Manual/10_오류_대응.md b/leak_test_project/Manual/09_오류_대응.md similarity index 79% rename from leak_test_project/Manual/10_오류_대응.md rename to leak_test_project/Manual/09_오류_대응.md index fc67767..c507c26 100644 --- a/leak_test_project/Manual/10_오류_대응.md +++ b/leak_test_project/Manual/09_오류_대응.md @@ -15,8 +15,6 @@ > **참고**: 통신 설정을 저장하면 기존 연결을 모두 해제하고 새 설정으로 재연결을 시도합니다. 포트 변경 후 재시작 없이 바로 적용됩니다. ---- - ## 9.2 시험 중 오류 시험 프로세스 진행 중 발생하는 오류입니다. 메인 화면의 **오류 메시지** 영역에 표시됩니다. @@ -24,11 +22,16 @@ | 오류 메시지 | 원인 | 조치 방법 | |------------|------|----------| | `Sentinel C28 통신 타임아웃` | 30초 내 C28 센서 응답 없음 | ① 센서 전원 ON 확인 ② 시리얼 케이블 연결 확인 ③ BaudRate 설정 확인 (기본: 9600) | -| `불량 제품 투입 (이전 결과: X)` | ZMDI 센서의 PrevResult가 "F"가 아님 (이전 검사 불합격 이력) | 해당 제품을 라인에서 제거하고 정상 제품 투입 | +| `통신 실패 (4251 보드 CHx 상태 타임아웃)` | 4251 보드가 응답 시간(15초) 내에 상태 응답을 회신하지 않음 | ① 4251 보드 전원 공급 및 케이블 연결선 확인 ② 통신 노이즈 방지 조치 확인 | +| `통신 실패 (4251 보드 CHx 상태 이상 또는 Fail)` | 4251 보드로부터 정상 응답을 받지 못했거나 오류 코드가 반환됨 | ① 하드웨어 오결선 여부 점검 ② 채널 센서 구동 전원 확인 | +| `통신 실패 (4251 보드 CHx ID 대기 타임아웃)` | 4251 보드를 통해 ZMDI 센서 ID를 15초 내에 판독하지 못함 | ① 센서가 지그(Jig)에 올바르게 안착되었는지 물리적 확인 ② 접촉 단자 이물질 청소 | | `SPEC 불일치 - 프로그램: OK, 센서: R` | 프로그램 SPEC과 센서 자체 SPEC의 판정이 다름 | ① 프로그램의 SPEC UL/LL 확인 (Parameters) ② C28 센서의 SPEC 설정 확인 ③ 양쪽 설정값 동기화 | | `LEAK 센서 데이터 오류` | C28에서 수신된 데이터 파싱 실패 | ① 센서 통신 상태 확인 ② 케이블 접촉 불량 확인 ③ BaudRate 불일치 확인 | +| `DB 저장 실패` | 중앙 MSSQL DB 서버에 검사 데이터를 저장하지 못함 | ① PC 네트워크 케이블 및 통신 연결 확인 ② Database.ini 파일의 접속 정보(IP/비밀번호 등) 확인 ③ 로컬 일일 CSV 로그 파일(`Logs/yyyy-MM-dd.csv`)에 백업이 잘 저장되었는지 확인 | + +--- -### SPEC 교차 검증 경고 팝업 +## 9.3 SPEC 교차 검증 경고 팝업 프로그램 판정과 센서 판정이 불일치하면 팝업 경고가 표시됩니다: @@ -42,18 +45,18 @@ --- -## 9.3 DIO 보드 오류 +## 9.4 DIO 보드 오류 | 상황 | 프로그램 동작 | 영향 | |------|-------------|------| | PCI-7432 보드 미인식 | **시뮬레이션 모드**로 자동 전환 | DIO 입출력이 동작하지 않음 (실제 입출력 포트 제어 불가) | | 보드 런타임 오류 | 좌/우 채널 오류 메시지에 `[DIO Board Error]` 표시 | 오류 내용에 따라 조치 | -> **시뮬레이션 모드**: ADLINK 드라이버가 설치되지 않은 개발 환경에서도 프로그램의 나머지 기능(통신 설정, 데이터 조회 등)을 테스트할 수 있도록 자동 전환됩니다. +> **시뮬레이션 모드**: ADLINK 드라이버가 신설되지 않은 개발 환경에서도 프로그램의 나머지 기능(통신 설정, 데이터 조회 등)을 테스트할 수 있도록 자동 전환됩니다. --- -## 9.4 일반적인 문제 해결 +## 9.5 일반적인 문제 해결 | 증상 | 가능한 원인 | 해결 방법 | |------|-----------|----------| @@ -66,7 +69,7 @@ --- -## 9.5 프로그램 종료 +## 9.6 프로그램 종료 ### 종료 방법 @@ -103,4 +106,4 @@ --- -[← 이전: 로그 관리](09_로그_관리.md) | [메뉴얼 목차로 돌아가기](사용자_메뉴얼.md) +[← 이전: 로그 관리](08_로그_관리.md) | [메뉴얼 목차로 돌아가기](사용자_메뉴얼.md) diff --git a/leak_test_project/Manual/Pasted image 20260624104139.png b/leak_test_project/Manual/Pasted image 20260624104139.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf771672967a9969fda6e1dcb22e5ff7c08e562 GIT binary patch literal 45899 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 Xe%h}Q@f8AKG7t#d$;FX=7<1*HgYTHq literal 0 HcmV?d00001 diff --git a/leak_test_project/Manual/Pasted image 20260624104208.png b/leak_test_project/Manual/Pasted image 20260624104208.png new file mode 100644 index 0000000000000000000000000000000000000000..d961026022ca7dbde300e615782b0cf40fcea08e GIT binary patch literal 50405 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 SEyNbe_4&8D?HY@vk7Ql)o!sOX1bZ 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%0D)J+{ZJEKrJc)6E9#j&l7Nbl93b1MKL5%>5rKzk! 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 CCAj4P literal 0 HcmV?d00001 diff --git a/leak_test_project/Manual/Pasted image 20260624104353.png b/leak_test_project/Manual/Pasted image 20260624104353.png new file mode 100644 index 0000000000000000000000000000000000000000..bd6e10f737fe40e41e1192756f0a4b56e237bccd GIT binary patch literal 27947 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 TIy?4<0UTv{b-D64#zFrB0?&f` literal 0 HcmV?d00001 diff --git a/leak_test_project/Manual/Pasted image 20260624104506.png b/leak_test_project/Manual/Pasted image 20260624104506.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec8702beb80952a8d14bcf404ce106e39c9618f GIT binary patch literal 24608 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 literal 0 HcmV?d00001 diff --git a/leak_test_project/Manual/Pasted image 20260624104531.png b/leak_test_project/Manual/Pasted image 20260624104531.png new file mode 100644 index 0000000000000000000000000000000000000000..c53786219a8f1b027f65e58377c4ad4dc08fa5e4 GIT binary patch literal 17388 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 WTE^)BLcY)ekdyi#StV}r>AwM5$EueA literal 0 HcmV?d00001 diff --git a/leak_test_project/Manual/Pasted image 20260624104558.png b/leak_test_project/Manual/Pasted image 20260624104558.png new file mode 100644 index 0000000000000000000000000000000000000000..f61e604699efb4c97daf022acbbad8e7c6fd24a3 GIT binary patch literal 31216 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 **참고**: 각 단계의 상세 내용은 위 문서 링크를 참조하세요. diff --git a/leak_test_project/Models/ProjectModels.cs b/leak_test_project/Models/ProjectModels.cs index 9760a57..3746696 100644 --- a/leak_test_project/Models/ProjectModels.cs +++ b/leak_test_project/Models/ProjectModels.cs @@ -16,7 +16,10 @@ namespace leak_test_project.Models public double SpecUL { get; set; } = 1.00; public double SpecLL { get; set; } = -1.00; - /// MSSQL 데이터베이스 연결 문자열 + /// + /// MSSQL 데이터베이스 연결 문자열 (더 이상 사용되지 않음. 대신 Database.ini의 설정을 읽어 접속합니다.) + /// + [Obsolete("Use Database.ini ConnectionString instead.")] public string DbConnectionString { get; set; } = "Server=192.168.0.20;Database=NE1aW_PT_SENSOR;User Id=sa;Password=sa1234;"; } @@ -91,6 +94,19 @@ namespace leak_test_project.Models public string SpecLL { get; set; } public string MeasuredValue { get; set; } public string Judgment { get; set; } + + // 신규 DB 테이블 EOL 컬럼 매핑 및 정합성 보장을 위한 추가 프로퍼티 + public string IcSn { get; set; } = string.Empty; + public string PcbBarcode { get; set; } = string.Empty; + public string Maker { get; set; } = string.Empty; + public string Model { get; set; } = string.Empty; + public string Variant1 { get; set; } = string.Empty; + public string Variant2 { get; set; } = string.Empty; + public string Operator { get; set; } = string.Empty; + public DateTime? ProductionDate { get; set; } + public string Line { get; set; } = string.Empty; + public string LotNo { get; set; } = string.Empty; + public string JigNo { get; set; } = string.Empty; } public class ParsedData @@ -154,7 +170,6 @@ namespace leak_test_project.Models /// 최종 조합 ID (년+월+일+시리얼+라인+항목) public string ID { get; set; } = ""; - /// 이전 검사 결과. F=미시험/불합격, P=합격. 불량 제품 필터링에 사용. - public string PrevResult { get; set; } = ""; + } } diff --git a/leak_test_project/Services/Board4251.cs b/leak_test_project/Services/Board4251.cs index 883a573..300052d 100644 --- a/leak_test_project/Services/Board4251.cs +++ b/leak_test_project/Services/Board4251.cs @@ -290,7 +290,7 @@ namespace leak_test_project.Services public bool Connect() => _service.Connect(); public void Disconnect() => _service.Disconnect(); - public SensorIdData ReadSensor() + public async Task ReadSensorAsync() { try { @@ -298,23 +298,33 @@ namespace leak_test_project.Services // 0. 초기화 (x00o 전송) ProgressMessage?.Invoke(this, "4251 보드 초기화 중..."); - // x00o 명령을 보내고 응답 내용에 상관없이 다음 단계 진행 (최대 5초 대기) - Task.Run(() => _service.SendCommandAsync("x00o\r\n")).Wait(1000); - Task.Delay(350).Wait(); // 보드 안정화 대기 + // x00o 명령을 보내고 응답 내용에 상관없이 다음 단계 진행 + _ = _service.SendCommandAsync("x00o\r\n"); + await Task.Delay(350); // 보드 안정화 대기 // 1. 보드 상태 확인 (Fail인 경우 중단) ProgressMessage?.Invoke(this, "4251 보드 데이터 읽는 중..."); int extendedTimeout = 15000; string statusCmd = $"x00c_00{channel}101:owt28006727ea97c7801"; - var statusTask = Task.Run(() => _service.CheckStatusAsync(channel)); - if (!statusTask.Wait(extendedTimeout)) + bool statusResult = false; + + using (var cts = new CancellationTokenSource(extendedTimeout)) { - string buf = _service.GetLastBuffer(); - string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; - ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); - return null; + var statusTask = _service.CheckStatusAsync(channel); + var delayTask = Task.Delay(extendedTimeout, cts.Token); + var completedTask = await Task.WhenAny(statusTask, delayTask); + if (completedTask == delayTask) + { + string buf = _service.GetLastBuffer(); + string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; + ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); + return null; + } + cts.Cancel(); + statusResult = await statusTask; } - if (!statusTask.Result) + + if (!statusResult) { string buf = _service.GetLastBuffer(); string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; @@ -329,20 +339,26 @@ namespace leak_test_project.Services for (int attempt = 1; attempt <= maxAttempts; attempt++) { - var idTask = Task.Run(() => _service.ReadIdAsync(channel)); - if (!idTask.Wait(extendedTimeout)) + using (var cts = new CancellationTokenSource(extendedTimeout)) { - if (attempt == maxAttempts) + var idTask = _service.ReadIdAsync(channel); + var delayTask = Task.Delay(extendedTimeout, cts.Token); + var completedTask = await Task.WhenAny(idTask, delayTask); + if (completedTask == delayTask) { - string buf = _service.GetLastBuffer(); - string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; - ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} ID 대기 타임아웃)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); - return null; + if (attempt == maxAttempts) + { + string buf = _service.GetLastBuffer(); + string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; + ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} ID 대기 타임아웃)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); + return null; + } + continue; } - continue; + cts.Cancel(); + rawId = await idTask; } - rawId = idTask.Result; if (string.IsNullOrEmpty(rawId)) { if (attempt == maxAttempts) @@ -361,7 +377,7 @@ namespace leak_test_project.Services if (attempt < maxAttempts) { ProgressMessage?.Invoke(this, $"ID 읽기 재시도 중... ({attempt}/{maxAttempts})"); - Task.Delay(350).Wait(); // 재시도 전 약간의 딜레이 + await Task.Delay(350); // 재시도 전 약간의 딜레이 continue; } else @@ -382,8 +398,7 @@ namespace leak_test_project.Services LowID = rawId, ID = rawId, // 16자리 전체를 ID로 사용 Serial = "", // 시리얼 번호는 현재 존재하지 않으므로 강제로 파싱하지 않음 - Item = "N/A", - PrevResult = "F" // '불량제품 투입' 필터를 통과하기 위한 강제 초기화 + Item = "N/A" }; ProgressMessage?.Invoke(this, "ID 읽기 성공"); diff --git a/leak_test_project/Services/DatabaseSettings.cs b/leak_test_project/Services/DatabaseSettings.cs new file mode 100644 index 0000000..89fce44 --- /dev/null +++ b/leak_test_project/Services/DatabaseSettings.cs @@ -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(); + } + } + } +} diff --git a/leak_test_project/Services/EolDatabaseService.cs b/leak_test_project/Services/EolDatabaseService.cs index 27787ea..3762db6 100644 --- a/leak_test_project/Services/EolDatabaseService.cs +++ b/leak_test_project/Services/EolDatabaseService.cs @@ -21,46 +21,84 @@ namespace leak_test_project.Services /// EOL 검사 결과를 데이터베이스에 저장합니다. (동일 ID 존재 시 업데이트, 미존재 시 삽입) /// /// 검사 결과 데이터 - public async Task SaveEolDataAsync(InspectData inspectData) + /// 저장 성공 여부 (성공: true, 실패: false) + public async Task SaveEolDataAsync(InspectData inspectData, Housing.Login.StartupLoginWindowResult loginInfo) { - if (inspectData == null) return; + if (inspectData == null) return false; - // MSSQL MERGE 문을 사용한 UPSERT 처리 + // MSSQL MERGE 문을 사용한 UPSERT 처리 (신규 dbo.EOL 스키마 적용) const string query = @" MERGE INTO [dbo].[EOL] AS Target - USING (SELECT @ID AS ID) AS Source - ON (Target.ID = Source.ID) + USING (SELECT @IC_SN AS IC_SN) AS Source + ON (Target.IC_SN = Source.IC_SN) WHEN MATCHED THEN UPDATE SET + Target.PCB_Barcode = @PCB_Barcode, + Target.Maker = @Maker, + Target.Model = @Model, + Target.Variant_1 = @Variant_1, + Target.Variant_2 = @Variant_2, + Target.Operator = @Operator, + Target.Production_Date = @Production_Date, + Target.Line = @Line, + Target.Lot_No = @Lot_No, + Target.Jig_No = @Jig_No, Target.Channel = @Channel, - Target.SpecUL = @SpecUL, - Target.SpecLL = @SpecLL, - Target.LeakValue = @LeakValue, - Target.Result = @Result, - Target.InspectDate = @InspectDate, - Target.InspectTime = @InspectTime + Target.Spec_UL = @Spec_UL, + Target.Spec_LL = @Spec_LL, + Target.Leak_Value = @Leak_Value, + Target.Result = @Result WHEN NOT MATCHED THEN - INSERT ([ID], [Channel], [SpecUL], [SpecLL], [LeakValue], [Result], [InspectDate], [InspectTime]) - VALUES (@ID, @Channel, @SpecUL, @SpecLL, @LeakValue, @Result, @InspectDate, @InspectTime);"; + INSERT (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) + VALUES (@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);"; try { + var maker = loginInfo?.Maker ?? string.Empty; + var model = loginInfo?.Model ?? string.Empty; + var variant1 = loginInfo?.Variant1 ?? string.Empty; + var variant2 = loginInfo?.Variant2 ?? string.Empty; + var operatorId = loginInfo?.Operator ?? string.Empty; + var lineNo = loginInfo?.LineNo ?? string.Empty; + var lotNo = loginInfo?.LotNo ?? string.Empty; + var jigNo = loginInfo?.JigNo ?? string.Empty; + using (var conn = new SqlConnection(_connectionString)) { using (var cmd = new SqlCommand(query, conn)) { - cmd.Parameters.AddWithValue("@ID", string.IsNullOrEmpty(inspectData.ProductId) ? "-" : inspectData.ProductId); + cmd.Parameters.AddWithValue("@IC_SN", string.IsNullOrEmpty(inspectData.IcSn) ? "-" : inspectData.IcSn); + cmd.Parameters.AddWithValue("@PCB_Barcode", string.IsNullOrEmpty(inspectData.PcbBarcode) ? "-" : inspectData.PcbBarcode); + cmd.Parameters.AddWithValue("@Maker", maker); + cmd.Parameters.AddWithValue("@Model", model); + cmd.Parameters.AddWithValue("@Variant_1", variant1); + cmd.Parameters.AddWithValue("@Variant_2", variant2); + cmd.Parameters.AddWithValue("@Operator", operatorId); + cmd.Parameters.AddWithValue("@Production_Date", inspectData.ProductionDate ?? DateTime.Now); + cmd.Parameters.AddWithValue("@Line", lineNo); + cmd.Parameters.AddWithValue("@Lot_No", lotNo); + cmd.Parameters.AddWithValue("@Jig_No", jigNo); cmd.Parameters.AddWithValue("@Channel", string.IsNullOrEmpty(inspectData.Channel) ? "N/A" : inspectData.Channel); - cmd.Parameters.AddWithValue("@SpecUL", string.IsNullOrEmpty(inspectData.SpecUL) ? "0.00" : inspectData.SpecUL); - cmd.Parameters.AddWithValue("@SpecLL", string.IsNullOrEmpty(inspectData.SpecLL) ? "0.00" : inspectData.SpecLL); - cmd.Parameters.AddWithValue("@LeakValue", string.IsNullOrEmpty(inspectData.MeasuredValue) ? "0.0000" : inspectData.MeasuredValue); + + // Spec_UL, Spec_LL, Leak_Value 형 변환 및 대입 + decimal specUL = 0; + if (decimal.TryParse(inspectData.SpecUL, out var parsedUL)) specUL = parsedUL; + cmd.Parameters.AddWithValue("@Spec_UL", specUL); + + decimal specLL = 0; + if (decimal.TryParse(inspectData.SpecLL, out var parsedLL)) specLL = parsedLL; + cmd.Parameters.AddWithValue("@Spec_LL", specLL); + + decimal leakValue = 0; + if (decimal.TryParse(inspectData.MeasuredValue, out var parsedValue)) leakValue = parsedValue; + cmd.Parameters.AddWithValue("@Leak_Value", leakValue); + cmd.Parameters.AddWithValue("@Result", string.IsNullOrEmpty(inspectData.Judgment) ? "NG" : inspectData.Judgment); - cmd.Parameters.AddWithValue("@InspectDate", string.IsNullOrEmpty(inspectData.InspectDate) ? DateTime.Now.ToString("yyyy-MM-dd") : inspectData.InspectDate); - cmd.Parameters.AddWithValue("@InspectTime", string.IsNullOrEmpty(inspectData.InspectTime) ? DateTime.Now.ToString("HH:mm:ss") : inspectData.InspectTime); await conn.OpenAsync(); await cmd.ExecuteNonQueryAsync(); - Console.WriteLine($"[DB SUCCESS] EOL Data saved/updated for ID: {inspectData.ProductId}"); + Console.WriteLine($"[DB SUCCESS] EOL Data saved/updated for IC_SN: {inspectData.ProductId}"); + return true; } } } @@ -68,6 +106,7 @@ namespace leak_test_project.Services { // 실시간 검사 공정 루프 흐름이 DB 커넥션 예외로 중단되지 않도록 예외 격리 처리 Console.WriteLine($"[DB ERROR] Failed to upsert EOL Data: {ex.Message}"); + return false; } } @@ -89,8 +128,9 @@ namespace leak_test_project.Services return true; } } - catch + catch (Exception ex) { + System.Diagnostics.Debug.WriteLine($"[DB Connection Check Error] {ex.Message}"); return false; } } diff --git a/leak_test_project/Services/IIdSensorService.cs b/leak_test_project/Services/IIdSensorService.cs index 7ffccad..5a47e3e 100644 --- a/leak_test_project/Services/IIdSensorService.cs +++ b/leak_test_project/Services/IIdSensorService.cs @@ -11,6 +11,6 @@ namespace leak_test_project.Services bool Connect(); void Disconnect(); - SensorIdData ReadSensor(); + System.Threading.Tasks.Task ReadSensorAsync(); } } diff --git a/leak_test_project/Services/LoginAccountRepository.cs b/leak_test_project/Services/LoginAccountRepository.cs new file mode 100644 index 0000000..af21e2d --- /dev/null +++ b/leak_test_project/Services/LoginAccountRepository.cs @@ -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 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; + } + } +} diff --git a/leak_test_project/Services/LoginHistoryStore.cs b/leak_test_project/Services/LoginHistoryStore.cs new file mode 100644 index 0000000..75001a2 --- /dev/null +++ b/leak_test_project/Services/LoginHistoryStore.cs @@ -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 Categories { get; set; } = new List(); + } + + public class HistoryCategory + { + [XmlAttribute("Name")] + public string Name { get; set; } + + [XmlElement("Value")] + public List Values { get; set; } = new List(); + } + + public class LoginHistoryStore + { + private readonly string _filePath; + private readonly Dictionary> _history = new Dictionary>(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 GetValues(string fieldName) + { + if (_history.TryGetValue(fieldName, out var list)) + { + return new List(list); + } + return new List(); + } + + 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(); + _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(); + } + } + } + } + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[LoginHistoryStore Load Error] {ex.Message}"); + } + } + } +} diff --git a/leak_test_project/Services/LoginServicesStub.cs b/leak_test_project/Services/LoginServicesStub.cs deleted file mode 100644 index f0f945c..0000000 --- a/leak_test_project/Services/LoginServicesStub.cs +++ /dev/null @@ -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> _history = new Dictionary>(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 GetValues(string fieldName) - { - if (_history.TryGetValue(fieldName, out var list)) - { - return new List(list); - } - return new List(); - } - - 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(); - _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(_history.Keys); - for (int i = 0; i < keys.Count; i++) - { - var key = keys[i]; - var values = _history[key]; - var arrayItems = new List(); - 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(); - 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 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; - } - } -} diff --git a/leak_test_project/Services/LoginSettings.cs b/leak_test_project/Services/LoginSettings.cs new file mode 100644 index 0000000..f963a05 --- /dev/null +++ b/leak_test_project/Services/LoginSettings.cs @@ -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(); + } + } + } +} diff --git a/leak_test_project/Services/TestProcessService.cs b/leak_test_project/Services/TestProcessService.cs index b94588c..a8e99bd 100644 --- a/leak_test_project/Services/TestProcessService.cs +++ b/leak_test_project/Services/TestProcessService.cs @@ -42,6 +42,7 @@ namespace leak_test_project.Services private readonly IIdSensorService _leftSensor; private readonly IIdSensorService _rightSensor; private readonly SentinelC28Service _sentinelService; + private readonly Housing.Login.StartupLoginWindowResult _loginInfo; private Thread _leftThread; private Thread _rightThread; @@ -50,6 +51,10 @@ namespace leak_test_project.Services private volatile bool _running = true; private readonly SemaphoreSlim _testLock = new SemaphoreSlim(1, 1); + // DIO 시작 신호를 폴링 대신 대기하기 위한 이벤트 + private readonly AutoResetEvent _leftStartEvent = new AutoResetEvent(false); + private readonly AutoResetEvent _rightStartEvent = new AutoResetEvent(false); + // LEAK 시험 결과를 수신하기 위한 필드 private ParsedData _leftResult; private ParsedData _rightResult; @@ -79,12 +84,14 @@ namespace leak_test_project.Services IDioBoard dioBoard, IIdSensorService leftSensor, IIdSensorService rightSensor, - SentinelC28Service sentinelService) + SentinelC28Service sentinelService, + Housing.Login.StartupLoginWindowResult loginInfo) { _dioBoard = dioBoard; _leftSensor = leftSensor; _rightSensor = rightSensor; _sentinelService = sentinelService; + _loginInfo = loginInfo; // DIO START 신호 이벤트 연결 _dioBoard.InputChanged += OnDioInputChanged; @@ -110,8 +117,16 @@ namespace leak_test_project.Services /// 시뮬레이션용: 수동으로 시험 시작 트리거 public void TriggerTestStart(int testIndex) { - if (testIndex == 0) _leftTestStart = true; - else _rightTestStart = true; + if (testIndex == 0) + { + _leftTestStart = true; + _leftStartEvent.Set(); + } + else + { + _rightTestStart = true; + _rightStartEvent.Set(); + } } /// @@ -140,19 +155,14 @@ namespace leak_test_project.Services var sensor = isLeft ? _leftSensor : _rightSensor; - // 시리얼 통신이므로 백그라운드에서 실행 - var sensorData = await System.Threading.Tasks.Task.Run(() => sensor.ReadSensor()); + // 비동기로 센서 정보 판독 + var sensorData = await sensor.ReadSensorAsync(); if (sensorData != null) { SensorReadComplete?.Invoke(this, (testIndex, sensorData)); - // 실제 검사 시퀀스와 동일한 불량 제품 필터링 체크 - if (sensorData.PrevResult != "F") - { - NotifyError(testIndex, "불량 제품 투입 (이전 결과: " + sensorData.PrevResult + ")"); - return; - } + NotifyProgress(testIndex, "ID 테스트 완료", "LightBlue"); } @@ -160,7 +170,7 @@ namespace leak_test_project.Services { // 실패 시 NotifyError를 호출하지 않는 것도 실제 시퀀스와 동일 (센서 서비스가 이미 에러 이벤트 발생시킴) // 다만 UI 갱신을 위해 빈 데이터는 전송 - sensorData = new SensorIdData { ID = "-", PrevResult = "F" }; + sensorData = new SensorIdData { ID = "-" }; SensorReadComplete?.Invoke(this, (testIndex, sensorData)); NotifyProgress(testIndex, "ID 테스트 실패", "Red"); } @@ -181,12 +191,14 @@ namespace leak_test_project.Services _dioBoard.WriteOutput("LEFT_OK", false); _dioBoard.WriteOutput("LEFT_NG", false); _leftTestStart = true; + _leftStartEvent.Set(); } else if (e.PointName == "RIGHT_START" && e.NewValue) { _dioBoard.WriteOutput("RIGHT_OK", false); _dioBoard.WriteOutput("RIGHT_NG", false); _rightTestStart = true; + _rightStartEvent.Set(); } } @@ -198,10 +210,14 @@ namespace leak_test_project.Services { bool isLeft = (testIndex == 0); string side = isLeft ? "LEFT" : "RIGHT"; + var startEvent = isLeft ? _leftStartEvent : _rightStartEvent; while (_running) { - Thread.Sleep(2); + // 이벤트 신호 대기 (2ms 폴링 대신 신호 수신 시까지 차단) + startEvent.WaitOne(); + + if (!_running) break; // 시작 신호 대기 bool shouldStart = isLeft ? _leftTestStart : _rightTestStart; @@ -237,7 +253,7 @@ namespace leak_test_project.Services Debug.WriteLine($"[TestProcess-{side}] Phase 1: 센서 정보 읽기 시작"); NotifyProgress(testIndex, "센서 정보 읽는 중", "LightSeaGreen"); var sensor = isLeft ? _leftSensor : _rightSensor; - var sensorData = sensor.ReadSensor(); + var sensorData = sensor.ReadSensorAsync().GetAwaiter().GetResult(); if (sensorData != null) { @@ -245,19 +261,71 @@ namespace leak_test_project.Services } else { - // 센서 읽기 실패해도 시험은 계속 진행 - sensorData = new SensorIdData { ID = "-", PrevResult = "F" }; - SensorReadComplete?.Invoke(this, (testIndex, sensorData)); - } + NotifyError(testIndex, "ID 읽기 실패"); + _dioBoard.WriteOutput(side + "_NG", true); - // === 2단계: 불량 제품 필터링 === - Debug.WriteLine($"[TestProcess-{side}] Phase 2: 센서 ID ({sensorData?.ID}), 이전결과 ({sensorData?.PrevResult}) 필터링 확인"); - if (sensorData.PrevResult != "F") - { - NotifyError(testIndex, "불량 제품 투입 (이전 결과: " + sensorData.PrevResult + ")"); + var failedSensorData = new SensorIdData { ID = "-" }; + SensorReadComplete?.Invoke(this, (testIndex, failedSensorData)); + + TestCompleted?.Invoke(this, new TestResultEventArgs + { + TestIndex = testIndex, + SensorData = failedSensorData, + MeasuredValue = "0.0000", + Judgment = "NG", + SensorJudgment = "FAIL", + SpecMismatch = false + }); + + var failData = new InspectData + { + InspectDate = DateTime.Now.ToString("yyyy-MM-dd"), + InspectTime = DateTime.Now.ToString("HH:mm:ss"), + Channel = side, + ProductId = "-", + IcSn = "-", + PcbBarcode = "-", + MeasuredValue = "0.0000", + Judgment = "NG", + SpecUL = ConfigManager.Current.SpecUL.ToString("F2"), + SpecLL = ConfigManager.Current.SpecLL.ToString("F2"), + Maker = _loginInfo?.Maker ?? string.Empty, + Model = _loginInfo?.Model ?? string.Empty, + Variant1 = _loginInfo?.Variant1 ?? string.Empty, + Variant2 = _loginInfo?.Variant2 ?? string.Empty, + Operator = _loginInfo?.Operator ?? string.Empty, + Line = _loginInfo?.LineNo ?? string.Empty, + LotNo = _loginInfo?.LotNo ?? string.Empty, + JigNo = _loginInfo?.JigNo ?? string.Empty, + ProductionDate = DateTime.Now + }; + FileLogger.LogInspectData(failData); + + try + { + var dbIniPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Database.ini"); + var dbConnStr = Housing.Services.DatabaseSettings.Load(dbIniPath).ConnectionString; + var dbService = new EolDatabaseService(dbConnStr); + _ = Task.Run(async () => + { + bool success = await dbService.SaveEolDataAsync(failData, _loginInfo); + if (!success) + { + NotifyError(testIndex, "DB 저장 실패"); + } + }); + } + catch (Exception ex) + { + Console.WriteLine("[DB ID Read Fail Integration Error] " + ex.Message); + } + + NotifyProgress(testIndex, "시험 완료", "LightBlue"); continue; } + + // === 3단계: LEAK 시험 수행 === Debug.WriteLine($"[TestProcess-{side}] Phase 3: LEAK 시험 대기 시작 (최대 30초)"); NotifyProgress(testIndex, "LEAK 시험중", "LightYellow"); @@ -289,18 +357,38 @@ namespace leak_test_project.Services InspectTime = DateTime.Now.ToString("HH:mm:ss"), Channel = side, ProductId = sensorData.ID, + IcSn = sensorData.ID, + PcbBarcode = sensorData.ID, MeasuredValue = "0.0000", Judgment = "NG", SpecUL = ConfigManager.Current.SpecUL.ToString("F2"), - SpecLL = ConfigManager.Current.SpecLL.ToString("F2") + SpecLL = ConfigManager.Current.SpecLL.ToString("F2"), + Maker = _loginInfo?.Maker ?? string.Empty, + Model = _loginInfo?.Model ?? string.Empty, + Variant1 = _loginInfo?.Variant1 ?? string.Empty, + Variant2 = _loginInfo?.Variant2 ?? string.Empty, + Operator = _loginInfo?.Operator ?? string.Empty, + Line = _loginInfo?.LineNo ?? string.Empty, + LotNo = _loginInfo?.LotNo ?? string.Empty, + JigNo = _loginInfo?.JigNo ?? string.Empty, + ProductionDate = DateTime.Now }; FileLogger.LogInspectData(timeoutData); // === MSSQL 데이터베이스 비동기 저장 === try { - var dbService = new EolDatabaseService(ConfigManager.Current.DbConnectionString); - _ = Task.Run(async () => await dbService.SaveEolDataAsync(timeoutData)); + var dbIniPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Database.ini"); + var dbConnStr = Housing.Services.DatabaseSettings.Load(dbIniPath).ConnectionString; + var dbService = new EolDatabaseService(dbConnStr); + _ = Task.Run(async () => + { + bool success = await dbService.SaveEolDataAsync(timeoutData, _loginInfo); + if (!success) + { + NotifyError(testIndex, "DB 저장 실패"); + } + }); } catch (Exception ex) { @@ -349,18 +437,38 @@ namespace leak_test_project.Services InspectTime = DateTime.Now.ToString("HH:mm:ss"), Channel = side, ProductId = sensorData.ID, + IcSn = sensorData.ID, + PcbBarcode = sensorData.ID, MeasuredValue = leakResult.MeasuredValueString, Judgment = programJudgment, SpecUL = specUL.ToString("F2"), - SpecLL = specLL.ToString("F2") + SpecLL = specLL.ToString("F2"), + Maker = _loginInfo?.Maker ?? string.Empty, + Model = _loginInfo?.Model ?? string.Empty, + Variant1 = _loginInfo?.Variant1 ?? string.Empty, + Variant2 = _loginInfo?.Variant2 ?? string.Empty, + Operator = _loginInfo?.Operator ?? string.Empty, + Line = _loginInfo?.LineNo ?? string.Empty, + LotNo = _loginInfo?.LotNo ?? string.Empty, + JigNo = _loginInfo?.JigNo ?? string.Empty, + ProductionDate = DateTime.Now }; FileLogger.LogInspectData(inspectData); // === MSSQL 데이터베이스 비동기 저장 === try { - var dbService = new EolDatabaseService(ConfigManager.Current.DbConnectionString); - _ = Task.Run(async () => await dbService.SaveEolDataAsync(inspectData)); + var dbIniPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Database.ini"); + var dbConnStr = Housing.Services.DatabaseSettings.Load(dbIniPath).ConnectionString; + var dbService = new EolDatabaseService(dbConnStr); + _ = Task.Run(async () => + { + bool success = await dbService.SaveEolDataAsync(inspectData, _loginInfo); + if (!success) + { + NotifyError(testIndex, "DB 저장 실패"); + } + }); } catch (Exception ex) { @@ -451,12 +559,16 @@ namespace leak_test_project.Services _running = false; _leftResultReady.Set(); // 대기 중인 스레드 해제 _rightResultReady.Set(); + _leftStartEvent.Set(); // 시작 대기 해제 + _rightStartEvent.Set(); _dioBoard.InputChanged -= OnDioInputChanged; _sentinelService.OnFinalResultParsed -= OnSentinelFinalResult; _leftResultReady?.Dispose(); _rightResultReady?.Dispose(); + _leftStartEvent?.Dispose(); + _rightStartEvent?.Dispose(); Console.WriteLine("[TestProcess] Disposed."); } diff --git a/leak_test_project/Utils/ConfigHelper.cs b/leak_test_project/Utils/ConfigHelper.cs index bb0c61c..018a492 100644 --- a/leak_test_project/Utils/ConfigHelper.cs +++ b/leak_test_project/Utils/ConfigHelper.cs @@ -47,9 +47,21 @@ namespace leak_test_project.Utils try { XmlSerializer serializer = new XmlSerializer(typeof(AppConfig)); - using (FileStream fs = new FileStream(ConfigPath, FileMode.Create)) + var settings = new System.Xml.XmlWriterSettings { - serializer.Serialize(fs, Current); + Indent = true, + IndentChars = " ", + NewLineChars = "\r\n", + NewLineHandling = System.Xml.NewLineHandling.Replace + }; + + // 네임스페이스 생략하여 XML 가독성 개선 + var namespaces = new XmlSerializerNamespaces(); + namespaces.Add(string.Empty, string.Empty); + + using (var writer = System.Xml.XmlWriter.Create(ConfigPath, settings)) + { + serializer.Serialize(writer, Current, namespaces); } ConfigChanged?.Invoke(null, EventArgs.Empty); } @@ -143,22 +155,28 @@ namespace leak_test_project.Utils using (var sw = new StreamWriter(filePath)) { + sw.WriteLine("; ====================================================================="); + sw.WriteLine("; 하드웨어 디지털 입출력 설정 파일 (DioConfig.ini)"); + sw.WriteLine("; ====================================================================="); + sw.WriteLine(); sw.WriteLine("[DIO]"); - sw.WriteLine($"CompanyName={CompanyName}"); - sw.WriteLine("BoardCnt=1"); - sw.WriteLine("LogEnabled=False"); + sw.WriteLine($"CompanyName={CompanyName} ; 입출력 보드 제조사명 (예: ADLink)"); + sw.WriteLine("BoardCnt=1 ; 설치된 총 입출력 보드 개수"); + sw.WriteLine("LogEnabled=False ; DIO 동작 로그 기록 여부 (True / False)"); sw.WriteLine(); sw.WriteLine("[BOARD_1]"); - sw.WriteLine($"BoardType={BoardType}"); - sw.WriteLine($"BoardNumber={BoardNumber}"); - sw.WriteLine($"InPortCnt={InputPoints.Count}"); - sw.WriteLine($"OutPortCnt={OutputPoints.Count}"); + sw.WriteLine($"BoardType={BoardType} ; ADLINK DIO 보드 하드웨어 모델명"); + sw.WriteLine($"BoardNumber={BoardNumber} ; 시스템 내 보드 식별 번호 (기본 0)"); + sw.WriteLine($"InPortCnt={InputPoints.Count} ; 입력에 사용할 접점 포트 개수"); + sw.WriteLine($"OutPortCnt={OutputPoints.Count} ; 출력에 사용할 접점 포트 개수"); sw.WriteLine(); sw.WriteLine("[BOARD_1_IN]"); + sw.WriteLine("; [핀 번호]=[기능 이름],[신호 레벨],[설명]"); for (int i = 0; i < InputPoints.Count; i++) sw.WriteLine($"{i}={InputPoints[i].Name},1,{(string.IsNullOrEmpty(InputPoints[i].Description) ? "" : InputPoints[i].Description)}"); sw.WriteLine(); sw.WriteLine("[BOARD_1_OUT]"); + sw.WriteLine("; [핀 번호]=[기능 이름],[신호 레벨],[설명]"); for (int i = 0; i < OutputPoints.Count; i++) sw.WriteLine($"{i}={OutputPoints[i].Name},1,{(string.IsNullOrEmpty(OutputPoints[i].Description) ? "" : OutputPoints[i].Description)}"); } diff --git a/leak_test_project/Utils/DialogService.cs b/leak_test_project/Utils/DialogService.cs new file mode 100644 index 0000000..2ef44b6 --- /dev/null +++ b/leak_test_project/Utils/DialogService.cs @@ -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); + } + } +} diff --git a/leak_test_project/Utils/LogUtils.cs b/leak_test_project/Utils/LogUtils.cs index 6a77ba8..5297410 100644 --- a/leak_test_project/Utils/LogUtils.cs +++ b/leak_test_project/Utils/LogUtils.cs @@ -4,7 +4,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; -using System.Windows; using leak_test_project.Models; namespace leak_test_project.Utils @@ -18,7 +17,7 @@ namespace leak_test_project.Utils private static readonly object _lock = new object(); /// - /// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함 + /// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함 (16컬럼) /// public static void LogInspectData(InspectData data) { @@ -34,14 +33,22 @@ namespace leak_test_project.Utils bool isNewFile = !File.Exists(filePath); - // CSV 헤더: Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment + // 신규 CSV 헤더: DB 스키마 16개 컬럼과 일치 if (isNewFile) { - string header = "Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment" + Environment.NewLine; + string header = "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" + Environment.NewLine; File.WriteAllText(filePath, header, Encoding.UTF8); } - string csvLine = $"{Esc(data.InspectDate)},{Esc(data.InspectTime)},{Esc(data.ProductId)},{Esc(data.Channel)}," + + string prodDateStr = data.ProductionDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? $"{data.InspectDate} {data.InspectTime}".Trim(); + if (string.IsNullOrEmpty(prodDateStr)) + { + prodDateStr = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + } + + string csvLine = $"{Esc(data.IcSn)},{Esc(data.PcbBarcode)},{Esc(data.Maker)},{Esc(data.Model)}," + + $"{Esc(data.Variant1)},{Esc(data.Variant2)},{Esc(data.Operator)},{Esc(prodDateStr)}," + + $"{Esc(data.Line)},{Esc(data.LotNo)},{Esc(data.JigNo)},{Esc(data.Channel)}," + $"{Esc(data.SpecUL)},{Esc(data.SpecLL)},{Esc(data.MeasuredValue)},{Esc(data.Judgment)}{Environment.NewLine}"; File.AppendAllText(filePath, csvLine, Encoding.UTF8); @@ -93,21 +100,165 @@ namespace leak_test_project.Utils { private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); - public static List ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "") + public static List ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "", string opFilter = "", string lotFilter = "", string modelFilter = "") + { + var results = new List(); + string dbIniPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Database.ini"); + string connectionString = string.Empty; + + try + { + connectionString = Housing.Services.DatabaseSettings.Load(dbIniPath).ConnectionString; + } + catch + { + // 로드 실패 시 무시 + } + + if (string.IsNullOrWhiteSpace(connectionString)) + { + // DB 연결 주소가 정의되지 않은 경우 로컬 CSV 탐색으로 Fallback + return ParseLogsFromCsv(start, end, judgmentFilter, serialFilter, opFilter, lotFilter, modelFilter); + } + + try + { + using (var conn = new System.Data.SqlClient.SqlConnection(connectionString)) + { + conn.Open(); + + // DATETIME2(0) 밀리초 오차 없이 안전한 인덱스 스캔을 위해 < @EndLimit 비교식 사용 + string query = @" + SELECT 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 + FROM [dbo].[EOL] + WHERE Production_Date >= @Start AND Production_Date < @EndLimit"; + + if (judgmentFilter != "[전체]") + { + query += " AND Result = @Judgment"; + } + if (!string.IsNullOrEmpty(serialFilter)) + { + query += " AND (IC_SN LIKE @Serial OR PCB_Barcode LIKE @Serial)"; + } + if (!string.IsNullOrEmpty(opFilter)) + { + query += " AND Operator LIKE @Operator"; + } + if (!string.IsNullOrEmpty(lotFilter)) + { + query += " AND Lot_No LIKE @LotNo"; + } + if (!string.IsNullOrEmpty(modelFilter)) + { + query += " AND Model LIKE @Model"; + } + + query += " ORDER BY Production_Date DESC"; + + using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn)) + { + cmd.Parameters.AddWithValue("@Start", start.Date); + cmd.Parameters.AddWithValue("@EndLimit", end.Date.AddDays(1)); // 익일 00:00:00 미만으로 설정하여 당일 23:59:59까지 포함 + + if (judgmentFilter != "[전체]") + { + cmd.Parameters.AddWithValue("@Judgment", judgmentFilter); + } + if (!string.IsNullOrEmpty(serialFilter)) + { + cmd.Parameters.AddWithValue("@Serial", $"%{serialFilter}%"); + } + if (!string.IsNullOrEmpty(opFilter)) + { + cmd.Parameters.AddWithValue("@Operator", $"%{opFilter}%"); + } + if (!string.IsNullOrEmpty(lotFilter)) + { + cmd.Parameters.AddWithValue("@LotNo", $"%{lotFilter}%"); + } + if (!string.IsNullOrEmpty(modelFilter)) + { + cmd.Parameters.AddWithValue("@Model", $"%{modelFilter}%"); + } + + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var icSn = reader.IsDBNull(0) ? "" : reader.GetString(0); + var pcbBarcode = reader.IsDBNull(1) ? "" : reader.GetString(1); + var maker = reader.IsDBNull(2) ? "" : reader.GetString(2); + var model = reader.IsDBNull(3) ? "" : reader.GetString(3); + var variant1 = reader.IsDBNull(4) ? "" : reader.GetString(4); + var variant2 = reader.IsDBNull(5) ? "" : reader.GetString(5); + var op = reader.IsDBNull(6) ? "" : reader.GetString(6); + var prodDate = reader.IsDBNull(7) ? (DateTime?)null : reader.GetDateTime(7); + var line = reader.IsDBNull(8) ? "" : reader.GetString(8); + var lotNo = reader.IsDBNull(9) ? "" : reader.GetString(9); + var jigNo = reader.IsDBNull(10) ? "" : reader.GetString(10); + var channel = reader.IsDBNull(11) ? "" : reader.GetString(11); + var specUl = reader.IsDBNull(12) ? 0m : reader.GetDecimal(12); + var specLl = reader.IsDBNull(13) ? 0m : reader.GetDecimal(13); + var leakValue = reader.IsDBNull(14) ? 0m : reader.GetDecimal(14); + var result = reader.IsDBNull(15) ? "NG" : reader.GetString(15); + + var data = new InspectData + { + IcSn = icSn, + PcbBarcode = pcbBarcode, + Maker = maker, + Model = model, + Variant1 = variant1, + Variant2 = variant2, + Operator = op, + ProductionDate = prodDate, + Line = line, + LotNo = lotNo, + JigNo = jigNo, + Channel = channel, + SpecUL = specUl.ToString("F2"), + SpecLL = specLl.ToString("F2"), + MeasuredValue = leakValue.ToString("F4"), + Judgment = result, + Retest = "N", + + // 기존 레거시 UI 바인딩 호환성 유지용 매핑 + ProductId = icSn, + InspectDate = prodDate?.ToString("yyyy-MM-dd") ?? "", + InspectTime = prodDate?.ToString("HH:mm:ss") ?? "" + }; + + results.Add(data); + } + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"[DB LogParser Error] {ex.Message}. Falling back to CSV."); + // DB 접속이나 쿼리 에러 발생 시 안전하게 CSV 로그 파일 파싱으로 대체 + return ParseLogsFromCsv(start, end, judgmentFilter, serialFilter, opFilter, lotFilter, modelFilter); + } + + return results; + } + + private static List ParseLogsFromCsv(DateTime start, DateTime end, string judgmentFilter, string serialFilter, string opFilter, string lotFilter, string modelFilter) { var results = new List(); if (!Directory.Exists(LogDirectory)) { - // 디렉토리가 없으면 빈 리스트 반환 return results; } - // 날짜 범위 내의 모든 로그 파일 찾기 (.csv) + // 파일명의 yyyy-MM-dd 날짜 파싱 시 시스템 로캘 영향을 막기 위해 TryParseExact 사용 var files = Directory.GetFiles(LogDirectory, "*.csv") .Where(f => { string fileName = Path.GetFileNameWithoutExtension(f); - if (DateTime.TryParse(fileName, out DateTime fileDate)) + if (DateTime.TryParseExact(fileName, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime fileDate)) { return fileDate.Date >= start.Date && fileDate.Date <= end.Date; } @@ -118,11 +269,21 @@ namespace leak_test_project.Utils { try { - var lines = File.ReadAllLines(file); - if (lines.Length <= 1) continue; // Header only or empty + // 엑셀 등으로 파일이 열려 파일 락(Lock)이 걸려있어도 안전하게 읽기 위해 FileShare.ReadWrite 적용 + var linesList = new List(); + using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var reader = new StreamReader(fs, Encoding.UTF8)) + { + string lineVal; + while ((lineVal = reader.ReadLine()) != null) + { + linesList.Add(lineVal); + } + } + + var lines = linesList.ToArray(); + if (lines.Length <= 1) continue; - // CSV 헤더 (구형): Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest - // CSV 헤더 (신형): Date,Time,Channel,ID,Value,Judgment,SpecUL,SpecLL for (int i = 1; i < lines.Length; i++) { string line = lines[i]; @@ -130,43 +291,157 @@ namespace leak_test_project.Utils var parts = line.Split(','); if (parts.Length < 8) continue; - string date = parts[0].Trim(); - string time = parts[1].Trim(); - - // 하위호환성 제거: 신형 8컬럼 순서(Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment)로 다이렉트 파싱 - string id = parts[2].Trim(); - string channel = parts[3].Trim(); - string specUl = parts[4].Trim(); - string specLl = parts[5].Trim(); - string val = parts[6].Trim(); - string judg = parts[7].Trim(); - string retest = "N"; - - // 필터 적용 - if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue; - if (!string.IsNullOrEmpty(serialFilter) && !id.Contains(serialFilter)) continue; - - results.Add(new InspectData + InspectData data = null; + + if (parts.Length >= 16) + { + // 16개 컬럼 신규 포맷 + // 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 + string icSn = parts[0].Trim(); + string pcbBarcode = parts[1].Trim(); + string maker = parts[2].Trim(); + string model = parts[3].Trim(); + string variant1 = parts[4].Trim(); + string variant2 = parts[5].Trim(); + string op = parts[6].Trim(); + string prodDateRaw = parts[7].Trim(); + string lineVal = parts[8].Trim(); + string lotNo = parts[9].Trim(); + string jigNo = parts[10].Trim(); + string channel = parts[11].Trim(); + string specUl = parts[12].Trim(); + string specLl = parts[13].Trim(); + string val = parts[14].Trim(); + string judg = parts[15].Trim(); + + DateTime? prodDate = null; + if (DateTime.TryParseExact(prodDateRaw, "yyyy-MM-dd HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime dt)) + { + prodDate = dt; + } + else if (DateTime.TryParse(prodDateRaw, out DateTime dtFallback)) + { + prodDate = dtFallback; + } + + // 필터링 적용 (부분 일치 LIKE 검색 흉내) + if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue; + if (!string.IsNullOrEmpty(serialFilter) && !icSn.Contains(serialFilter) && !pcbBarcode.Contains(serialFilter)) continue; + if (!string.IsNullOrEmpty(opFilter) && !op.Contains(opFilter)) continue; + if (!string.IsNullOrEmpty(lotFilter) && !lotNo.Contains(lotFilter)) continue; + if (!string.IsNullOrEmpty(modelFilter) && !model.Contains(modelFilter)) continue; + + data = new InspectData + { + IcSn = icSn, + PcbBarcode = pcbBarcode, + Maker = maker, + Model = model, + Variant1 = variant1, + Variant2 = variant2, + Operator = op, + ProductionDate = prodDate, + Line = lineVal, + LotNo = lotNo, + JigNo = jigNo, + Channel = channel, + SpecUL = specUl, + SpecLL = specLl, + MeasuredValue = val, + Judgment = judg, + Retest = "N", + + // 기존 UI 및 구형 바인딩 호환성용 매핑 + ProductId = icSn, + InspectDate = prodDate?.ToString("yyyy-MM-dd") ?? "", + InspectTime = prodDate?.ToString("HH:mm:ss") ?? "" + }; + } + else + { + // 8개 컬럼 구형 포맷 + // Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment + string date = parts[0].Trim(); + string time = parts[1].Trim(); + string id = parts[2].Trim(); + string channel = parts[3].Trim(); + string specUl = parts[4].Trim(); + string specLl = parts[5].Trim(); + string val = parts[6].Trim(); + string judg = parts[7].Trim(); + + // 필터링 적용 (구형 포맷에는 Operator, LotNo, Model이 없으므로 필터값이 있는 경우 결과에서 제외) + if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue; + if (!string.IsNullOrEmpty(serialFilter) && !id.Contains(serialFilter)) continue; + if (!string.IsNullOrEmpty(opFilter)) continue; + if (!string.IsNullOrEmpty(lotFilter)) continue; + if (!string.IsNullOrEmpty(modelFilter)) continue; + + DateTime? parsedProdDate = null; + string dateTimeStr = $"{date} {time}"; + if (DateTime.TryParseExact(dateTimeStr, new[] { "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm" }, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out DateTime dtOld)) + { + parsedProdDate = dtOld; + } + else if (DateTime.TryParse(dateTimeStr, out DateTime dtOldFallback)) + { + parsedProdDate = dtOldFallback; + } + + data = new InspectData + { + InspectDate = date, + InspectTime = time, + Channel = channel, + ProductId = id, + MeasuredValue = val, + Judgment = judg, + SpecUL = specUl, + SpecLL = specLl, + Retest = "N", + + // 정합성을 위해 호환 필드 매핑 + IcSn = id, + PcbBarcode = id, + ProductionDate = parsedProdDate + }; + } + + if (data != null) { - InspectDate = date, - InspectTime = time, - Channel = channel, - ProductId = id, - MeasuredValue = val, - Judgment = judg, - SpecUL = specUl, - SpecLL = specLl, - Retest = retest - }); + results.Add(data); + } } } catch (Exception ex) { - Console.WriteLine($"[LogParser] Error reading file {file}: {ex.Message}"); + Console.WriteLine($"[LogParser CSV Fallback] Error reading file {file}: {ex.Message}"); + } + } + + var sorted = results.OrderByDescending(r => r.InspectDate).ThenByDescending(r => r.InspectTime).ToList(); + var uniqueResults = new List(); + var seenIds = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var item in sorted) + { + string id = string.IsNullOrEmpty(item.IcSn) ? item.ProductId : item.IcSn; + if (string.IsNullOrEmpty(id) || id == "-") + { + // 바코드가 없거나 식별 불가한 데이터는 중복 필터링 없이 그대로 출력 + uniqueResults.Add(item); + } + else + { + // 이미 정렬되어 내림차순이므로 최초로 추가된 건(가장 최신 검사)만 유지 + if (seenIds.Add(id)) + { + uniqueResults.Add(item); + } } } - return results.OrderByDescending(r => r.InspectDate).ThenByDescending(r => r.InspectTime).ToList(); + return uniqueResults; } } @@ -216,7 +491,7 @@ namespace leak_test_project.Utils } catch (Exception ex) { - MessageBox.Show($"CSV 저장 실패: {ex.Message}"); + System.Diagnostics.Debug.WriteLine($"[CsvExporter ExportToCsv Error] {ex.Message}"); return false; } } diff --git a/leak_test_project/ViewModels/DataViewModel.cs b/leak_test_project/ViewModels/DataViewModel.cs index 848beb5..05fbf43 100644 --- a/leak_test_project/ViewModels/DataViewModel.cs +++ b/leak_test_project/ViewModels/DataViewModel.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Windows; using System.Windows.Input; using Microsoft.Win32; using leak_test_project.Models; @@ -27,6 +26,15 @@ namespace leak_test_project.ViewModels private string _idFilter = ""; public string IdFilter { get => _idFilter; set => SetProperty(ref _idFilter, value); } + private string _operatorFilter = ""; + public string OperatorFilter { get => _operatorFilter; set => SetProperty(ref _operatorFilter, value); } + + private string _lotNoFilter = ""; + public string LotNoFilter { get => _lotNoFilter; set => SetProperty(ref _lotNoFilter, value); } + + private string _modelFilter = ""; + public string ModelFilter { get => _modelFilter; set => SetProperty(ref _modelFilter, value); } + private List _searchResults = new List(); public List SearchResults { get => _searchResults; set => SetProperty(ref _searchResults, value); } @@ -37,14 +45,17 @@ namespace leak_test_project.ViewModels private int _resultCount = 0; public int ResultCount { get => _resultCount; set => SetProperty(ref _resultCount, value); } + private readonly IDialogService _dialogService; + public ICommand SearchCommand { get; } public ICommand SaveCsvCommand { get; } /// Close 버튼 시 홈으로 돌아가는 커맨드 public ICommand CloseCommand { get; } - public DataViewModel(Action navigateHome) + public DataViewModel(Action navigateHome, IDialogService dialogService = null) { + _dialogService = dialogService ?? new DefaultDialogService(); SearchCommand = new RelayCommand(o => ExecuteSearch()); SaveCsvCommand = new RelayCommand(o => ExecuteSaveCsv()); CloseCommand = new RelayCommand(o => navigateHome?.Invoke()); @@ -74,7 +85,15 @@ namespace leak_test_project.ViewModels ? judgmentOptions[JudgmentIndex] : "[전체]"; - var results = LogParser.ParseLogs(StartDate, EndDate, judgment, IdFilter?.Trim() ?? ""); + var results = LogParser.ParseLogs( + StartDate, + EndDate, + judgment, + IdFilter?.Trim() ?? "", + OperatorFilter?.Trim() ?? "", + LotNoFilter?.Trim() ?? "", + ModelFilter?.Trim() ?? "" + ); SearchResults = results; ResultCount = results.Count; @@ -82,7 +101,7 @@ namespace leak_test_project.ViewModels if (results.Count == 0) { - MessageBox.Show("해당 조건의 데이터가 없습니다."); + _dialogService.ShowWarning("해당 조건의 데이터가 없습니다."); } } @@ -92,7 +111,7 @@ namespace leak_test_project.ViewModels // 빈 행이 포함되지 않은 원본 검색 결과(SearchResults)만 저장 if (SearchResults == null || SearchResults.Count == 0) { - MessageBox.Show("저장할 데이터가 없습니다."); + _dialogService.ShowWarning("저장할 데이터가 없습니다."); return; } @@ -106,7 +125,7 @@ namespace leak_test_project.ViewModels { if (CsvExporter.ExportToCsv(SearchResults, saveFileDialog.FileName)) { - MessageBox.Show("CSV 파일로 저장되었습니다."); + _dialogService.ShowMessage("CSV 파일로 저장되었습니다."); } } } diff --git a/leak_test_project/ViewModels/HomeViewModel.cs b/leak_test_project/ViewModels/HomeViewModel.cs index 98f9e14..a04d446 100644 --- a/leak_test_project/ViewModels/HomeViewModel.cs +++ b/leak_test_project/ViewModels/HomeViewModel.cs @@ -127,8 +127,11 @@ namespace leak_test_project.ViewModels - public HomeViewModel(IDioBoard dioBoard) + private readonly IDialogService _dialogService; + + public HomeViewModel(IDioBoard dioBoard, IDialogService dialogService = null) { + _dialogService = dialogService ?? new DefaultDialogService(); _dioBoard = dioBoard; _dispatcher = Dispatcher.CurrentDispatcher; @@ -261,7 +264,7 @@ namespace leak_test_project.ViewModels }); // 자동 시험 프로세스 - _testProcess = new TestProcessService(_dioBoard, _leftSensor, _rightSensor, _sentinelService); + _testProcess = new TestProcessService(_dioBoard, _leftSensor, _rightSensor, _sentinelService, App.LoginResult); _testProcess.ProgressChanged += (s, e) => _dispatcher.Invoke(() => { @@ -332,11 +335,7 @@ namespace leak_test_project.ViewModels if (e.TestIndex == 0) LeftError = msg; else RightError = msg; - MessageBox.Show( - "프로그램 스팩과 센서의 스팩이 서로 맞지 않습니다.", - "SPEC 교차 검증 경고", - MessageBoxButton.OK, - MessageBoxImage.Warning); + _dialogService.ShowWarning("프로그램 스팩과 센서의 스팩이 서로 맞지 않습니다.", "SPEC 교차 검증 경고"); } }); @@ -425,7 +424,6 @@ namespace leak_test_project.ViewModels { ConfigManager.ConfigChanged -= OnConfigChanged; CleanupAll(); - _dioBoard?.Dispose(); } } } diff --git a/leak_test_project/Views/DataView.xaml b/leak_test_project/Views/DataView.xaml index 4deb431..e37d7d9 100644 --- a/leak_test_project/Views/DataView.xaml +++ b/leak_test_project/Views/DataView.xaml @@ -88,50 +88,86 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - -