Browse Source

설정파일 수정

main
최승민 1 month ago
parent
commit
c9fd5cf0b9
  1. 4
      .agents/AGENTS.md
  2. 82
      DB/dbo.EOL_Schema.sql
  3. 38
      leak_test_project/App.xaml.cs
  4. 24
      leak_test_project/Database.ini
  5. 4
      leak_test_project/Infrastructure/CommunicationBase.cs
  6. 26
      leak_test_project/Manual/.obsidian/workspace.json
  7. 19
      leak_test_project/Manual/01_시스템_개요.md
  8. 47
      leak_test_project/Manual/02_초기_설정.md
  9. 60
      leak_test_project/Manual/03_메인_화면.md
  10. 13
      leak_test_project/Manual/04_통신_설정.md
  11. 9
      leak_test_project/Manual/05_시험_설정.md
  12. 99
      leak_test_project/Manual/06_데이터_조회.md
  13. 99
      leak_test_project/Manual/07_데이터_조회.md
  14. 44
      leak_test_project/Manual/07_시험_프로세스.md
  15. 219
      leak_test_project/Manual/08_로그_관리.md
  16. 150
      leak_test_project/Manual/09_로그_관리.md
  17. 21
      leak_test_project/Manual/09_오류_대응.md
  18. BIN
      leak_test_project/Manual/Pasted image 20260624104139.png
  19. BIN
      leak_test_project/Manual/Pasted image 20260624104208.png
  20. BIN
      leak_test_project/Manual/Pasted image 20260624104256.png
  21. BIN
      leak_test_project/Manual/Pasted image 20260624104353.png
  22. BIN
      leak_test_project/Manual/Pasted image 20260624104445.png
  23. BIN
      leak_test_project/Manual/Pasted image 20260624104506.png
  24. BIN
      leak_test_project/Manual/Pasted image 20260624104531.png
  25. BIN
      leak_test_project/Manual/Pasted image 20260624104558.png
  26. 16
      leak_test_project/Manual/사용자_메뉴얼.md
  27. 21
      leak_test_project/Models/ProjectModels.cs
  28. 41
      leak_test_project/Services/Board4251.cs
  29. 122
      leak_test_project/Services/DatabaseSettings.cs
  30. 82
      leak_test_project/Services/EolDatabaseService.cs
  31. 2
      leak_test_project/Services/IIdSensorService.cs
  32. 99
      leak_test_project/Services/LoginAccountRepository.cs
  33. 147
      leak_test_project/Services/LoginHistoryStore.cs
  34. 295
      leak_test_project/Services/LoginServicesStub.cs
  35. 82
      leak_test_project/Services/LoginSettings.cs
  36. 168
      leak_test_project/Services/TestProcessService.cs
  37. 36
      leak_test_project/Utils/ConfigHelper.cs
  38. 23
      leak_test_project/Utils/DialogService.cs
  39. 321
      leak_test_project/Utils/LogUtils.cs
  40. 31
      leak_test_project/ViewModels/DataViewModel.cs
  41. 14
      leak_test_project/ViewModels/HomeViewModel.cs
  42. 70
      leak_test_project/Views/DataView.xaml
  43. 8
      leak_test_project/Views/StartupLoginWindow.xaml
  44. 2
      leak_test_project/Views/StartupLoginWindow.xaml.cs
  45. 24
      leak_test_project/bin/Debug/Database.ini
  46. BIN
      leak_test_project/bin/Debug/app.publish/leak_test_project.exe
  47. 2
      leak_test_project/bin/Debug/leak_test_project.application
  48. BIN
      leak_test_project/bin/Debug/leak_test_project.exe
  49. 4
      leak_test_project/bin/Debug/leak_test_project.exe.manifest
  50. BIN
      leak_test_project/bin/Debug/leak_test_project.pdb
  51. 24
      leak_test_project/bin/Release/Database.ini
  52. 2
      leak_test_project/bin/Release/Logs/2026-06-18_system.log
  53. 8
      leak_test_project/bin/Release/Logs/2026-06-25_system.log
  54. BIN
      leak_test_project/bin/Release/app.publish/leak_test_project.exe
  55. 3
      leak_test_project/bin/Release/config.xml
  56. 2
      leak_test_project/bin/Release/leak_test_project.application
  57. BIN
      leak_test_project/bin/Release/leak_test_project.exe
  58. 4
      leak_test_project/bin/Release/leak_test_project.exe.manifest
  59. BIN
      leak_test_project/bin/Release/leak_test_project.pdb
  60. 12
      leak_test_project/leak_test_project.csproj
  61. BIN
      leak_test_project/obj/Debug/App.baml
  62. BIN
      leak_test_project/obj/Debug/MainWindow.baml
  63. BIN
      leak_test_project/obj/Debug/Views/CommunicationWindow.baml
  64. BIN
      leak_test_project/obj/Debug/Views/DataView.baml
  65. 6
      leak_test_project/obj/Debug/Views/DataView.g.cs
  66. BIN
      leak_test_project/obj/Debug/Views/HomeView.baml
  67. BIN
      leak_test_project/obj/Debug/Views/ParametersWindow.baml
  68. BIN
      leak_test_project/obj/Debug/Views/StartupLoginWindow.baml
  69. 22
      leak_test_project/obj/Debug/Views/StartupLoginWindow.g.cs
  70. 2
      leak_test_project/obj/Debug/leak_test_project.application
  71. 2
      leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache
  72. 1
      leak_test_project/obj/Debug/leak_test_project.csproj.FileListAbsolute.txt
  73. BIN
      leak_test_project/obj/Debug/leak_test_project.exe
  74. 4
      leak_test_project/obj/Debug/leak_test_project.exe.manifest
  75. BIN
      leak_test_project/obj/Debug/leak_test_project.g.resources
  76. BIN
      leak_test_project/obj/Debug/leak_test_project.pdb
  77. 8
      leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache
  78. BIN
      leak_test_project/obj/Release/App.baml
  79. BIN
      leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache
  80. 62
      leak_test_project/obj/Release/GeneratedInternalTypeHelper.g.cs
  81. BIN
      leak_test_project/obj/Release/MainWindow.baml
  82. BIN
      leak_test_project/obj/Release/Views/CommunicationWindow.baml
  83. BIN
      leak_test_project/obj/Release/Views/DataView.baml
  84. 6
      leak_test_project/obj/Release/Views/DataView.g.cs
  85. 6
      leak_test_project/obj/Release/Views/DataView.g.i.cs
  86. BIN
      leak_test_project/obj/Release/Views/HomeView.baml
  87. BIN
      leak_test_project/obj/Release/Views/ParametersWindow.baml
  88. BIN
      leak_test_project/obj/Release/Views/StartupLoginWindow.baml
  89. 22
      leak_test_project/obj/Release/Views/StartupLoginWindow.g.cs
  90. 22
      leak_test_project/obj/Release/Views/StartupLoginWindow.g.i.cs
  91. 2
      leak_test_project/obj/Release/leak_test_project.application
  92. BIN
      leak_test_project/obj/Release/leak_test_project.csproj.AssemblyReference.cache
  93. 2
      leak_test_project/obj/Release/leak_test_project.csproj.CoreCompileInputs.cache
  94. 1
      leak_test_project/obj/Release/leak_test_project.csproj.FileListAbsolute.txt
  95. BIN
      leak_test_project/obj/Release/leak_test_project.exe
  96. 4
      leak_test_project/obj/Release/leak_test_project.exe.manifest
  97. BIN
      leak_test_project/obj/Release/leak_test_project.g.resources
  98. BIN
      leak_test_project/obj/Release/leak_test_project.pdb
  99. 8
      leak_test_project/obj/Release/leak_test_project_MarkupCompile.cache
  100. 2
      leak_test_project/obj/Release/leak_test_project_MarkupCompile.lref

4
.agents/AGENTS.md

@ -0,0 +1,4 @@
# 프로젝트 개발 지침
- **개발 전 단계**: 작업이나 구현 계획을 세우기 전, 반드시 `ob/` 폴더 내에 있는 시스템 설계 문서 및 관련 문서들을 상세히 읽어 요구사항과 설계 방향을 확인해야 합니다.
- **개발 후 단계**: 코드 변경 또는 신규 기능 개발 완료 후, 작업 결과와 관련 설계 변경점을 `ob/` 폴더 내의 해당 문서에 성실히 업데이트하고 기록해야 합니다.

82
DB/dbo.EOL_Schema.sql

@ -1,10 +1,11 @@
-- ============================================================================ -- ============================================================================
-- Project: NE1aW 온압센서 -- Project: NE1aW 온압센서
-- Document: dbo.EOL Table Schema (Simplified Version) -- Document: dbo.EOL Table Schema (Latest Version)
-- Author: Antigravity AI -- Author: Antigravity AI
-- Date: 2026-05-28 -- Date: 2026-06-24
-- Description: 사용자 요구사항(ID, 좌/우, SPEC UL, SPEC LL, 누설값, 판정)에 맞추어 단순화된 스키마. -- Description: 개편된 로그인 사양 및 16개 컬럼 결과 저장 구조에 맞춘 스키마.
-- 재검사 이력 추적을 위해 Seq(PK) 및 InspectDateTime(일시) 컬럼 추가. -- 제품 일련번호(IC_SN)를 주 식별자로 사용하며,
-- 동일 제품 재검사 시 MERGE 문을 이용해 UPSERT 처리됩니다.
-- ============================================================================ -- ============================================================================
-- 1. 데이터베이스 존재 여부 확인 및 자동 생성 -- 1. 데이터베이스 존재 여부 확인 및 자동 생성
@ -26,57 +27,78 @@ BEGIN
END END
GO GO
-- 3. 단순화된 테이블 생성 -- 3. 개편된 테이블 생성
CREATE TABLE [dbo].[EOL] ( CREATE TABLE [dbo].[EOL] (
[InspectDate] NVARCHAR(10) NOT NULL, -- 검사 날짜 (YYYY-MM-DD) [IC_SN] NVARCHAR(10) NOT NULL, -- 제품의 최종 조합 ID (ProductId) (기본키)
[InspectTime] NVARCHAR(8) NOT NULL, -- 검사 시간 (HH:MM:SS) [PCB_Barcode] NVARCHAR(10) NOT NULL, -- 고유 식별용 바코드 정보 (UK)
[ID] NVARCHAR(30) NOT NULL, -- 센서 ID (ProductId) (기본키) [Maker] NVARCHAR(10) NULL, -- 로그인 메이커 정보
[Channel] NVARCHAR(10) NOT NULL, -- 좌 / 우 (LEFT / RIGHT) [Model] NVARCHAR(10) NULL, -- 로그인 모델 정보
[SpecUL] NVARCHAR(10) NOT NULL, -- SPEC UL [Variant_1] NVARCHAR(10) NULL, -- 로그인 변형1 정보
[SpecLL] NVARCHAR(10) NOT NULL, -- SPEC LL [Variant_2] NVARCHAR(10) NULL, -- 로그인 변형2 정보
[LeakValue] NVARCHAR(20) NOT NULL, -- 누설 측정값 (MeasuredValue) [Operator] NVARCHAR(10) NOT NULL, -- 로그인 검사자 ID
[Result] NVARCHAR(5) NOT NULL, -- 판정 결과 (OK / NG) [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 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 GO
-- ============================================================================ -- ============================================================================
-- [부록] 테스트 시나리오 SQL 예시 (단순화 버전) -- [부록] 테스트 시나리오 SQL 예시 (신규 개편 버전)
-- ============================================================================ -- ============================================================================
/* /*
-- 1. 테스트용 기본 데이터 INSERT (정상 삽입) -- 1. 테스트용 기본 데이터 INSERT (정상 삽입)
INSERT INTO [dbo].[EOL] ( 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 ( 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]; SELECT * FROM [dbo].[EOL];
-- 2. 동일 ID 재검사 시 MERGE 문을 이용한 UPSERT 테스트 (정상 업데이트 확인) -- 2. 동일 IC_SN 재검사 시 MERGE 문을 이용한 UPSERT 테스트 (정상 업데이트 확인)
-- 예상 결과: 기존 'IC_SIMPLE_0001' 레코드의 LeakValue가 '0.098'로 업데이트 -- 예상 결과: 기존 'SN00000001' 레코드의 Leak_Value가 '0.0980'으로 업데이트되고, Production_Date 등이 갱신
MERGE INTO [dbo].[EOL] AS Target MERGE INTO [dbo].[EOL] AS Target
USING (SELECT 'IC_SIMPLE_0001' AS ID) AS Source USING (SELECT 'SN00000001' AS IC_SN) AS Source
ON (Target.ID = Source.ID) ON (Target.IC_SN = Source.IC_SN)
WHEN MATCHED THEN WHEN MATCHED THEN
UPDATE SET 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.Channel = 'LEFT',
Target.SpecUL = '1.00', Target.Spec_UL = 1.00,
Target.SpecLL = '-1.00', Target.Spec_LL = -1.00,
Target.LeakValue = '0.098', -- 업데이트될 새 값 Target.Leak_Value = 0.0980, -- 업데이트될 새 값
Target.Result = 'OK', Target.Result = 'OK'
Target.InspectDate = '2026-06-02',
Target.InspectTime = '14:48:00'
WHEN NOT MATCHED THEN WHEN NOT MATCHED THEN
INSERT ([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_SIMPLE_0001', 'LEFT', '1.00', '-1.00', '0.098', 'OK', '2026-06-02', '14:48:00'); 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]; SELECT * FROM [dbo].[EOL];

38
leak_test_project/App.xaml.cs

@ -19,6 +19,44 @@ namespace leak_test_project
{ {
base.OnStartup(e); 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(); var mainWindow = new MainWindow();
this.MainWindow = mainWindow; this.MainWindow = mainWindow;

24
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

4
leak_test_project/Infrastructure/CommunicationBase.cs

@ -122,7 +122,9 @@ namespace leak_test_project.Infrastructure
_serialPort.DiscardInBuffer(); _serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer(); _serialPort.DiscardOutBuffer();
_serialPort.ReadExisting(); _serialPort.ReadExisting();
} catch { } } catch (Exception ex) {
System.Diagnostics.Debug.WriteLine($"[CommunicationBase ClearBuffer Error] {ex.Message}");
}
} }
} }

26
leak_test_project/Manual/.obsidian/workspace.json

@ -13,12 +13,12 @@
"state": { "state": {
"type": "markdown", "type": "markdown",
"state": { "state": {
"file": "사용자_메뉴얼.md", "file": "07_시험_프로세스.md",
"mode": "preview", "mode": "source",
"source": false "source": false
}, },
"icon": "lucide-file", "icon": "lucide-file",
"title": "사용자_메뉴얼" "title": "07_시험_프로세스"
} }
} }
] ]
@ -185,11 +185,25 @@
}, },
"active": "7fc461cd97381c41", "active": "7fc461cd97381c41",
"lastOpenFiles": [ "lastOpenFiles": [
"10_오류_대응.md", "06_데이터_조회.md",
"09_로그_관리.md", "Pasted image 20260624104558.png",
"05_시험_설정.md",
"Pasted image 20260624104531.png",
"04_통신_설정.md",
"Pasted image 20260624104506.png",
"03_메인_화면.md", "03_메인_화면.md",
"Pasted image 20260624104445.png",
"Pasted image 20260624104353.png",
"Pasted image 20260624104256.png",
"Pasted image 20260624104208.png",
"Pasted image 20260624104139.png",
"02_초기_설정.md", "02_초기_설정.md",
"01_시스템_개요.md", "01_시스템_개요.md",
"사용자_메뉴얼.md" "08_로그_관리.md",
"09_오류_대응.md",
"사용자_메뉴얼.md",
"07_시험_프로세스.md",
"10_오류_대응.md",
"09_로그_관리.md"
] ]
} }

19
leak_test_project/Manual/01_시스템_개요.md

@ -17,7 +17,6 @@
| **Sentinel C28 연동** | RS-232 시리얼 통신으로 누설 센서의 실시간 측정값을 수신합니다 | | **Sentinel C28 연동** | RS-232 시리얼 통신으로 누설 센서의 실시간 측정값을 수신합니다 |
| **4251 보드 연동** | 제품 ID(ZMDI 센서) 판독을 위한 시리얼 통신을 수행합니다 | | **4251 보드 연동** | 제품 ID(ZMDI 센서) 판독을 위한 시리얼 통신을 수행합니다 |
| **PCI-7432 DIO 보드** | 디지털 입출력 제어 (시작 신호 수신, OK/NG 출력) | | **PCI-7432 DIO 보드** | 디지털 입출력 제어 (시작 신호 수신, OK/NG 출력) |
| **실시간 I/O 모니터링** | 디지털 입출력 상태를 500ms 주기로 실시간 감시합니다 |
| **검사 기록 관리** | 날짜/판정/ID별 필터링 검색 및 CSV 파일 내보내기를 지원합니다 | | **검사 기록 관리** | 날짜/판정/ID별 필터링 검색 및 CSV 파일 내보내기를 지원합니다 |
| **SPEC 교차 검증** | 프로그램 판정과 센서 자체 판정을 비교하여 불일치 시 경고합니다 | | **SPEC 교차 검증** | 프로그램 판정과 센서 자체 판정을 비교하여 불일치 시 경고합니다 |
| **순차 실행 보장** | 세마포어 잠금으로 좌/우 채널 동시 시험을 방지합니다 | | **순차 실행 보장** | 세마포어 잠금으로 좌/우 채널 동시 시험을 방지합니다 |
@ -98,23 +97,23 @@
``` ```
┌────────────────────────────────────────────────┐ ┌────────────────────────────────────────────────┐
│ 상단 타이틀 바 (프로그램명, 시간, 창 제어 버튼) │ 상단 타이틀 바 (프로그램명, 시간, 배지, 창 제어)│
├────────────────────────────────────────────────┤ ├────────────────────────────────────────────────┤
│ │ │ │
│ 메인 콘텐츠 영역 │ │ 메인 콘텐츠 영역 │
│ (Home / I/O Monitor / Data View 전환) │ (Home / Data View 전환)
│ │ │ │
├────────────────────────────────────────────────┤ ├────────────────────────────────────────────────┤
│ 하단 메뉴 바 (I/O Monitor, Data, Parameters, │ 하단 메뉴 바 (Data, Parameters, Comm.Settings,
Comm. Settings, ID Test L/R) │ │ ID Test L/R)
└────────────────────────────────────────────────┘ └────────────────────────────────────────────────┘
``` ```
| 영역 | 높이 | 설명 | | 영역 | 설명 |
|------|------|------| |------|------|
| 상단 타이틀 바 | 55px | 프로그램 제목, 현재 시간, 버전 정보, 창 제어 버튼 | | 상단 타이틀 바 | 프로그램 제목, 실시간 날짜/시간, 현재 로그인된 작업자/모델/라인 정보 배지, 세션 로그아웃 버튼, 시스템 창 제어 버튼 배치 |
| 메인 콘텐츠 | 가변 | 선택된 화면(Home/I-O/Data)이 표시되는 영역 | | 메인 콘텐츠 영역 | 선택된 기능 화면(Home 검사 모니터 또는 Data 데이터 조회)이 표시되는 주 콘텐츠 영역 |
| 하단 메뉴 바 | 75px | 화면 전환 및 설정 팝업 버튼 | | 하단 메뉴 바 | 각 화면 전환 및 설정을 위한 조작 버튼 영역 |
--- ---

47
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 설정 변경 시 주의사항
| 상황 | 동작 | 주의 | | 상황 | 동작 | 주의 |
|------|------|------| |------|------|------|

60
leak_test_project/Manual/03_메인_화면.md

@ -7,14 +7,15 @@
프로그램 실행 시 기본으로 표시되는 화면입니다. 좌/우 채널의 누설 시험 결과를 실시간으로 모니터링합니다. 프로그램 실행 시 기본으로 표시되는 화면입니다. 좌/우 채널의 누설 시험 결과를 실시간으로 모니터링합니다.
## 3.1 전체 화면 레이아웃 ## 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 │ │ 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 │ │ ID/LOW ID │ ABC123 / 00FF │ ID/LOW ID │ DEF456 / 01AA │
│ 측정값 │ 0.5234 sccm │ 측정값 │ 0.3124 sccm │ │ 측정값 │ 0.5234 sccm │ 측정값 │ 0.3124 sccm │
│ │ SPEC UL: 1.00 │ │ SPEC UL: 1.00 │ │ │ SPEC UL: 1.00 │ │ SPEC UL: 1.00 │
@ -31,14 +32,16 @@
## 3.2 상단 타이틀 바 ## 3.2 상단 타이틀 바
프로그램 최상단에 위치한 짙은 남색(#2C3E50) 배경의 바입니다. 프로그램 최상단에 위치한 짙은 블랙 그레이 배경의 바입니다.
![[Pasted image 20260624104256.png]]
| 위치 | 요소 | 설명 | | 위치 | 요소 | 설명 |
|------|------|------| |------|------|------|
| 좌측 | **프로그램 제목** | "Pressure Leak Inspect System" (백색, 24pt 볼드) | | 좌측 | **프로그램 제목 & 버전** | "Pressure Leak" (백색 볼드) 및 `v1.0.0 (Updated: 2026-06-05)` 표시 |
| 중앙~우측 | **현재 날짜/시간** | `2026-05-08 [목] 10:30:00` 형식, 1초 주기로 실시간 갱신 | | 중앙 | **현재 날짜/시간** | `yyyy-MM-dd [요일] HH:mm:ss` 형식, 1초 주기로 실시간 갱신 |
| 우측 | **버전 정보** | `Ver. 1.0.0 | Updated 2026-04-03` (회색 텍스트) | | 우측 | **작업 정보 배지** | 현재 로그인 상태에 따른 `작업자 ID`, `검사 모델`, `생산 라인` 정보를 배지로 시각화하여 현재 작업 상태 인지 |
| 최우측 | **창 제어 버튼** | 최소화(─) / 최대화·복원(□) / 종료(X, 빨간색) | | 우측 | **로그아웃 버튼** | 현재 세션을 로그아웃하고, 다시 로그인 모달 창을 띄웁니다 |
| 최우측 | **창 제어 버튼** | 최소화(─) / 최대화·복원(□) / 종료(X)를 수행하는 윈도우 창 제어 아이콘 배치 |
### 타이틀 바 조작 ### 타이틀 바 조작
@ -55,13 +58,13 @@
## 3.3 메인 영역 - 채널 모니터링 ## 3.3 메인 영역 - 채널 모니터링
화면은 좌측(`LEFT LEAK TESTER`)과 우측(`RIGHT LEAK TESTER`)으로 균등 분할되며, 각 채널은 동일한 구조입니다. 화면은 좌측(`LEFT LEAK TESTER`)과 우측(`RIGHT LEAK TESTER`)으로 균등 분할되며, 각 채널은 동일한 구조입니다.
![[Pasted image 20260624104353.png]]
### 각 채널의 구성 요소 (위에서 아래 순서) ### 각 채널의 구성 요소 (위에서 아래 순서)
#### ① 채널 타이틀 (Row 0) #### ① 채널 타이틀 (Row 0)
- **LEFT LEAK TESTER** 또는 **RIGHT LEAK TESTER** - **LEFT LEAK TESTER** 또는 **RIGHT LEAK TESTER**
- 짙은 남색(#34495E) 배경에 백색 볼드 텍스트 (20pt) - 짙은 남색 배경에 백색 볼드 텍스트로 채널 구분을 명시합니다.
- 높이: 50px
#### ② 시작 시간 (Row 1) #### ② 시작 시간 (Row 1)
| 항목 | 설명 | | 항목 | 설명 |
@ -69,7 +72,6 @@
| 라벨 | `시작 시간` (회색 헤더 배경) | | 라벨 | `시작 시간` (회색 헤더 배경) |
| 값 | `yyyy-MM-dd HH:mm:ss` 형식 (예: `2026-05-08 10:30:15`) | | 값 | `yyyy-MM-dd HH:mm:ss` 형식 (예: `2026-05-08 10:30:15`) |
| 갱신 시점 | 시험이 시작될 때마다 현재 시각으로 자동 갱신 | | 갱신 시점 | 시험이 시작될 때마다 현재 시각으로 자동 갱신 |
| 높이 | 42px |
#### ③ ID / LOW ID (Row 2) #### ③ ID / LOW ID (Row 2)
| 항목 | 설명 | | 항목 | 설명 |
@ -78,7 +80,6 @@
| 값 | `{제품ID} / {Low ID}` 형식으로 조합 표시 | | 값 | `{제품ID} / {Low ID}` 형식으로 조합 표시 |
| 데이터 소스 | 4251 보드를 통해 ZMDI 센서에서 읽은 값 | | 데이터 소스 | 4251 보드를 통해 ZMDI 센서에서 읽은 값 |
| 갱신 시점 | 시험 시작 시 초기화, 센서 판독 완료 시 표시 | | 갱신 시점 | 시험 시작 시 초기화, 센서 판독 완료 시 표시 |
| 높이 | 42px |
#### ④ 측정값 + SPEC (Row 3) - **가장 큰 영역** #### ④ 측정값 + SPEC (Row 3) - **가장 큰 영역**
좌측에 측정값, 우측에 SPEC 규격이 함께 표시됩니다. 좌측에 측정값, 우측에 SPEC 규격이 함께 표시됩니다.
@ -87,29 +88,29 @@
| 항목 | 설명 | | 항목 | 설명 |
|------|------| |------|------|
| 라벨 | `측정값` (18pt 볼드) | | 라벨 | `측정값` (볼드) |
| 값 | Sentinel C28에서 수신한 누설량 (소수점 4자리, 예: `0.5234`) | | 값 | Sentinel C28에서 수신한 누설량 (소수점 4자리, 예: `0.5234`) |
| 단위 | `sccm` (우측 하단에 표시) | | 단위 | `sccm` (우측 하단에 표시) |
| 폰트 크기 | **84pt** (매우 크게 표시하여 원거리에서도 확인 가능) | | 폰트 크기 | 원거리에서도 쉽게 식별할 수 있도록 대형 폰트로 강조 표시 |
| 색상 | 파란색(#2980B9) 볼드 | | 색상 | 파란색 볼드 |
| 갱신 시점 | C28 센서 스트리밍 데이터 수신 시 실시간 갱신 | | 갱신 시점 | C28 센서 스트리밍 데이터 수신 시 실시간 갱신 |
**SPEC 영역 (우측 210px):** **SPEC 영역 (우측):**
| 항목 | 설명 | | 항목 | 설명 |
|------|------| |------|------|
| `UL` | 상한 규격값 (예: `1.00`), 26pt 볼드 | | `UL` | 상한 규격값 (예: `1.00`) 볼드 |
| `LL` | 하한 규격값 (예: `-1.00`), 26pt 볼드 | | `LL` | 하한 규격값 (예: `-1.00`) 볼드 |
| 갱신 시점 | Parameters에서 저장 시 즉시 반영 | | 갱신 시점 | Parameters에서 저장 시 즉시 반영 |
#### ⑤ 판정 (Row 4) - **두 번째로 큰 영역** #### ⑤ 판정 (Row 4) - **두 번째로 큰 영역**
| 항목 | 설명 | | 항목 | 설명 |
|------|------| |------|------|
| 라벨 | `판정` (18pt 볼드) | | 라벨 | `판정` (볼드) |
| 값 | **OK** 또는 **NG** (96pt, ExtraBold) | | 값 | **OK** 또는 **NG** (대형 볼드) |
| OK 표시 | 녹색 텍스트(#27AE60) + 연녹색 배경(#D4EFDF) | | OK 표시 | 녹색 텍스트 + 연녹색 배경으로 가시성 극대화 |
| NG 표시 | 빨간색 텍스트(#C0392B) + 연빨간색 배경(#FADBD8) | | NG 표시 | 빨간색 텍스트 + 연빨간색 배경으로 가시성 극대화 |
> **판정 기준**: `SPEC LL ≤ 측정값 ≤ SPEC UL`**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 하단 메뉴 바 ## 3.5 하단 메뉴 바
프로그램 최하단의 연회색(#D5DBDB) 배경 영역입니다. 5개의 버튼이 좌측부터 수평으로 배치됩니다. 프로그램 최하단의 연회색 배경 영역입니다. 5개의 버튼이 좌측부터 수평으로 배치됩니다.
![[Pasted image 20260624104445.png]]
| 순서 | 버튼 텍스트 | 부제 | 동작 | 동작 방식 | | 순서 | BUTTON | 부제 | 동작 | 동작 방식 |
|------|------------|------|------|----------| |------|------------|------|------|----------|
| 1 | **Data** | (측정 데이터) | 검사 기록 조회 화면 전환 | 토글 (다시 클릭 시 Home으로 복귀) | | 1 | **Data** | (측정 데이터) | 검사 기록 조회 화면 전환 | 토글 (다시 클릭 시 Home으로 복귀) |
| 2 | **Parameters** | (시험 설정) | 시험 설정 팝업 창 열기 | 모달 팝업 (설정 후 자동 닫힘) | | 2 | **Parameters** | (시험 설정) | 시험 설정 팝업 창 열기 | 모달 팝업 (설정 후 자동 닫힘) |
@ -170,10 +171,9 @@
| 5 | **ID Test (R)** | (우측 ID 테스트) | 우측 채널 센서 ID 수동 판독 | 즉시 실행 (파란색 텍스트) | | 5 | **ID Test (R)** | (우측 ID 테스트) | 우측 채널 센서 ID 수동 판독 | 즉시 실행 (파란색 텍스트) |
### 버튼 스타일 ### 버튼 스타일
- 크기: 140px × 55px - 배경: 연회색, 마우스 오버 시 시각적 피드백을 위해 약간 어둡게 변함
- 배경: 연회색(#F2F4F4), 마우스 오버 시 약간 어두워짐(#E5E8E8) - 테두리: 회색
- 테두리: 회색(#85929E), 1.5px - 텍스트: 볼드 (부제는 일반 텍스트)
- 텍스트: 14pt 볼드 (부제는 10pt 일반)
--- ---

13
leak_test_project/Manual/04_통신_설정.md

@ -5,7 +5,7 @@
--- ---
하단 메뉴에서 **Comm. Settings (통신 설정)** 버튼 클릭 시 열리는 **모달 팝업 창**입니다. 하단 메뉴에서 **Comm. Settings (통신 설정)** 버튼 클릭 시 열리는 **모달 팝업 창**입니다.
![[Pasted image 20260624104506.png]]
## 4.1 창 구성 ## 4.1 창 구성
``` ```
@ -24,9 +24,8 @@
| 속성 | 값 | | 속성 | 값 |
|------|-----| |------|-----|
| 창 크기 | 750px × 자동 높이 (최대 700px) | | 창 크기 | 고정 (크기 변경 불가) |
| 크기 변경 | 불가 | | 위치 | 메인 창 중앙에 정렬되어 표시 |
| 위치 | 메인 창 중앙 |
| 타입 | 모달 (이 창이 열려있으면 메인 창 조작 불가) | | 타입 | 모달 (이 창이 열려있으면 메인 창 조작 불가) |
## 4.2 4251 보드 설정 (좌측 영역) ## 4.2 4251 보드 설정 (좌측 영역)
@ -45,10 +44,10 @@
## 4.4 버튼 설명 ## 4.4 버튼 설명
| 버튼 | 색상 | 동작 | | 버튼 | 특징 | 동작 |
|------|------|------| |------|------|------|
| **설정 저장** | 연녹색 배경, 녹색 테두리 | config.xml에 저장 → 기존 연결 해제 → 새 설정으로 재연결 → "통신 설정이 저장되었습니다." 메시지 → 창 닫힘 | | **설정 저장** | 녹색 계열 | config.xml에 저장 → 기존 연결 해제 → 새 설정으로 재연결 → "통신 설정이 저장되었습니다." 메시지 → 창 닫힘 |
| **닫기** | 기본 회색 | 변경 사항 저장하지 않고 창 닫기 | | **닫기** | 회색 계열 | 변경 사항 저장하지 않고 창 닫기 |
## 4.5 저장 시 내부 동작 순서 ## 4.5 저장 시 내부 동작 순서

9
leak_test_project/Manual/05_시험_설정.md

@ -5,7 +5,7 @@
--- ---
하단 메뉴에서 **Parameters (시험 설정)** 버튼 클릭 시 열리는 **모달 팝업 창**입니다. 하단 메뉴에서 **Parameters (시험 설정)** 버튼 클릭 시 열리는 **모달 팝업 창**입니다.
![[Pasted image 20260624104531.png]]
## 5.1 창 구성 ## 5.1 창 구성
``` ```
@ -25,9 +25,8 @@
| 속성 | 값 | | 속성 | 값 |
|------|-----| |------|-----|
| 창 크기 | 550px × 자동 높이 (최대 600px) | | 창 크기 | 고정 (크기 변경 불가) |
| 크기 변경 | 불가 | | 위치 | 메인 창 중앙에 정렬되어 표시 |
| 위치 | 메인 창 중앙 |
| 타입 | 모달 | | 타입 | 모달 |
## 5.2 설정 항목 ## 5.2 설정 항목
@ -67,4 +66,4 @@
--- ---
[← 이전: 통신 설정](04_통신_설정.md) | [다음: I/O Monitor →](06_IO_모니터.md) [← 이전: 통신 설정](04_통신_설정.md) | [다음: 측정 데이터 조회 →](06_데이터_조회.md)

99
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)

99
leak_test_project/Manual/07_데이터_조회.md

@ -1,99 +0,0 @@
# 6. 측정 데이터 조회 (Data View)
[← 메뉴얼 목차로 돌아가기](사용자_메뉴얼.md)
---
하단 메뉴에서 **Data (측정 데이터)** 버튼을 클릭하면 표시됩니다. 다시 클릭하면 메인 화면으로 복귀합니다.
## 7.1 화면 구성
```
┌───────────────────────────────────────────────────────────┐
│ INSPECT DATA LIST (측정 기록 조회) │ ← 타이틀 바
├───────────────────────────────────────────────────────────┤
│ [조회기간] 2026-05-01 ~ 2026-05-08 [판정] 전체 [ID검색] │
│ [Search] [CSV] │ ← 필터 영역
├──────┬──────┬───────┬────┬──────┬──────┬──────┬────┬─────┤
│ 날짜 │재시험│ ID │좌우│UL │LL │측정값│판정│비고 │ ← 데이터 그리드
│ │ │ │ │ │ │ │ │ │
│ ... │ N │ABC123 │LEFT│ 1.00 │-1.00 │0.5234│ OK │ │
│ ... │ N │DEF456 │RIGHT│1.00 │-1.00 │1.2000│ NG │ │
├──────┴──────┴───────┴────┴──────┴──────┴──────┴────┴─────┤
│ [ Close ] │
└───────────────────────────────────────────────────────────┘
```
## 6.2 필터 조건 상세
### 조회 기간 (폭 320px)
| 항목 | UI 타입 | 기본값 | 설명 |
|------|---------|--------|------|
| **시작일** | DatePicker (달력) | 오늘로부터 7일 전 | 조회 시작 날짜 |
| **종료일** | DatePicker (달력) | 오늘 | 조회 종료 날짜 |
> **참고**: 달력 팝업은 1.25배 확대되어 표시됩니다.
### 판정 결과 (폭 120px)
| 항목 | UI 타입 | 선택 가능 값 | 기본값 |
|------|---------|-------------|--------|
| **판정 결과** | 드롭다운 | `[전체]` / `OK` / `NG` | `[전체]` |
### ID 검색 (폭 280px)
| 항목 | UI 타입 | 설명 | 기본값 |
|------|---------|------|--------|
| **ID 검색** | 텍스트 입력 | 제품 ID에 포함된 텍스트로 부분 일치 검색 | (빈 값 = 전체) |
## 6.3 조회 및 내보내기 버튼
| 버튼 | 색상 | 크기 | 동작 |
|------|------|------|------|
| **Search** | 연파랑 배경, 파랑 테두리 | 110×45px | 설정된 필터 조건으로 Logs 폴더의 CSV 파일을 검색합니다 |
| **CSV** | 연녹색 배경, 녹색 테두리 | 110×45px | 검색 결과를 새 CSV 파일로 저장합니다 (저장 위치 선택 대화상자) |
### Search 버튼 동작 상세
1. `[프로그램폴더]\Logs\` 폴더에서 조회 기간에 해당하는 `yyyy-MM-dd.csv` 파일을 탐색
2. 각 CSV 파일의 데이터를 판정/ID 필터 조건으로 필터링
3. 결과를 날짜 역순(최신순)으로 정렬하여 데이터 그리드에 표시
4. 검색 결과가 없으면 "해당 조건의 데이터가 없습니다." 알림 표시
### CSV 버튼 동작 상세
1. 검색 결과가 없으면 "저장할 데이터가 없습니다." 알림 표시
2. 파일 저장 대화상자 표시 (기본 파일명: `InspectData_yyyyMMdd_HHmmss.csv`)
3. 저장 완료 시 "CSV 파일로 저장되었습니다." 알림 표시
## 6.4 데이터 그리드 컬럼
| 컬럼 | 폭 | 정렬 | 설명 |
|------|-----|------|------|
| **날짜** | 130px | 좌측 | 검사 일자 (yyyy-MM-dd 형식) |
| **재시험** | 70px | 좌측 | 재시험 여부 (`Y` 또는 `N`) |
| **ID** | 250px | 좌측 | 제품 ID (ZMDI 센서에서 읽은 값) |
| **좌/우** | 70px | 좌측 | 검사 채널 (`LEFT` 또는 `RIGHT`) |
| **SPEC UL** | 80px | 좌측 | 검사 시점의 상한 규격값 |
| **SPEC LL** | 80px | 좌측 | 검사 시점의 하한 규격값 |
| **측정값** | 100px | **우측** | 측정된 결과 sccm (**볼드**) |
| **판정** | 80px | **중앙** | `OK`(녹색, #27AE60) 또는 `NG`(빨간색, #C0392B), **ExtraBold** |
| **비고** | 나머지(300px~) | 좌측 | 추가 메모 (현재 미사용) |
### 그리드 스타일
- 행 높이: 35px
- 격자선: 수평/수직 모두 연회색(#ECF0F1)
- 홀수 행 배경: 흰색, 짝수 행 배경: 연회색(#F9FBFC)
- 선택된 행: 연파랑 배경(#D6EAF8), 파란색 텍스트(#2E86C1)
- 컬럼 헤더 정렬 가능, 컬럼 순서 변경 불가
- 최소 50행이 항상 표시됩니다 (데이터가 적어도 빈 행으로 채워짐)
## 6.5 화면 닫기
| 방법 | 설명 |
|------|------|
| 하단 **Close** 버튼 클릭 | 메인 화면으로 복귀 |
| 하단 메뉴 **Data** 버튼 재클릭 | 토글 동작으로 메인 화면으로 복귀 |
---
[← 이전: 시험 설정](05_시험_설정.md) | [다음: 자동 시험 프로세스 →](08_시험_프로세스.md)

44
leak_test_project/Manual/08_시험_프로세스.md → leak_test_project/Manual/07_시험_프로세스.md

@ -30,40 +30,38 @@
│ 2단계: 센서 정보 읽기 │ │ 2단계: 센서 정보 읽기 │
│ · 4251 보드를 통해 ZMDI 센서에서 제품 ID 판독 │ │ · 4251 보드를 통해 ZMDI 센서에서 제품 ID 판독 │
│ · ID / LOW ID를 메인 화면에 표시 │ │ · ID / LOW ID를 메인 화면에 표시 │
│ · 읽기 실패해도 빈 데이터("-")로 계속 진행 │ │ · ID 읽기 실패 시: "ID 읽기 실패" 오류 출력 후 │
│ "NG" 판정으로 DB 비동기 저장 및 조기 종료 (Skip) │
│ (DB 저장 실패 시 "DB 저장 실패" 문구 표시) │
└──────────────┬───────────────────────────────────────┘ └──────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐ ┌──────────────────────────────────────────────────────┐
│ 3단계: 불량 제품 필터링 │ │ 3단계: LEAK 시험 수행 │
│ · 센서의 PrevResult 값 확인 │
│ · PrevResult = "F" → 정상 (다음 단계로 진행) │
│ · PrevResult ≠ "F" → "불량 제품 투입" 오류 → 시험 중단│
└──────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ 4단계: LEAK 시험 수행 │
│ · "LEAK 시험중" 메시지 표시 │ │ · "LEAK 시험중" 메시지 표시 │
│ · Sentinel C28 센서의 최종 결과(Auto Result) 대기 │ │ · Sentinel C28 센서의 최종 결과(Auto Result) 대기 │
│ · 최대 대기 시간: 30초 │ │ · 최대 대기 시간: 30초 │
│ · 타임아웃 시: NG 판정 + "Sentinel C28 통신 타임아웃" │ │ · 타임아웃 시: NG 판정 + "Sentinel C28 통신 타임아웃" │
│ (DB 저장 실패 시 "DB 저장 실패" 문구 표시) │
└──────────────┬───────────────────────────────────────┘ └──────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐ ┌──────────────────────────────────────────────────────┐
5단계: 판정 │ 4단계: 판정 │
│ · SPEC LL ≤ 측정값 ≤ SPEC UL → OK │ │ · SPEC LL ≤ 측정값 ≤ SPEC UL → OK │
│ · 범위 밖 → NG │ │ · 범위 밖 → NG │
└──────────────┬───────────────────────────────────────┘ └──────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐ ┌──────────────────────────────────────────────────────┐
6단계: SPEC 교차 검증 │ 5단계: SPEC 교차 검증 │
│ · 프로그램 판정과 센서 자체 판정 비교 │ │ · 프로그램 판정과 센서 자체 판정 비교 │
│ · 센서 판정 정규화: OK/A/P → Accept, NG/R/F → Reject │ │ · 센서 판정 정규화: OK/A/P → Accept, NG/R/F → Reject │
│ · 불일치 시: SpecMismatch = true → 팝업 경고 │ │ · 불일치 시: SpecMismatch = true → 팝업 경고 │
└──────────────┬───────────────────────────────────────┘ └──────────────┬───────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐ ┌──────────────────────────────────────────────────────┐
7단계: 결과 출력 6단계: 결과 출력 및 저장
│ · CSV 로그 파일에 검사 데이터 기록 │ │ · CSV 로그 파일에 검사 데이터 기록 │
│ · MSSQL 중앙 데이터베이스 비동기 저장 │
│ · DB 저장 실패 시: 오류 메시지 칸에 "DB 저장 실패" 표시 │
│ · DIO OK 또는 NG 출력 ON │ │ · DIO OK 또는 NG 출력 ON │
│ · 메인 화면에 측정값, 판정 결과 표시 │ │ · 메인 화면에 측정값, 판정 결과 표시 │
│ · "시험 완료" 메시지 표시 │ │ · "시험 완료" 메시지 표시 │
@ -89,10 +87,10 @@
Sentinel C28 센서에서 30초 이내에 응답이 없으면: Sentinel C28 센서에서 30초 이내에 응답이 없으면:
1. "Sentinel C28 통신 타임아웃" 오류 메시지 표시 1. "Sentinel C28 통신 타임아웃" 오류 메시지 표시 (DB 저장 실패 시 "DB 저장 실패"도 오류 메시지에 추가 표시)
2. 해당 채널 DIO `NG` 출력 ON 2. 해당 채널 DIO `NG` 출력 ON
3. 측정값 `0.0000`, 판정 `NG`, 센서판정 `T/O` (Timeout)로 기록 3. 측정값 `0.0000`, 판정 `NG`, 센서판정 `T/O` (Timeout)로 기록
4. CSV 로그 파일에도 NG로 기록 4. CSV 로그 파일 및 MSSQL 데이터베이스에 NG 결과 비동기 기록
## 7.5 C28 센서 결과 채널 라우팅 ## 7.5 C28 센서 결과 채널 라우팅
@ -114,12 +112,24 @@ Sentinel C28 센서에서 수신한 데이터는 채널 번호(ChannelNo)로 구
3. 500ms 대기 (UI 피드백용) 3. 500ms 대기 (UI 피드백용)
4. "센서 정보 읽는 중" 메시지 표시 4. "센서 정보 읽는 중" 메시지 표시
5. 4251 보드를 통해 ZMDI 센서 ID 판독 5. 4251 보드를 통해 ZMDI 센서 ID 판독
6. 성공 시: ID / LOW ID 표시 + 불량 제품 필터링 확인 + "ID 테스트 완료" 6. 성공 시: ID / LOW ID 표시 + "ID 테스트 완료"
7. 실패 시: ID를 "-"로 표시 + "ID 테스트 실패" 7. 실패 시: ID를 "-"로 표시 + "ID 테스트 실패"
8. 세마포어 잠금 해제 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)

219
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
<AppConfig>
<SensorPort>COM1</SensorPort>
<SensorBaudRate>9600</SensorBaudRate>
<Board4251Port>COM3</Board4251Port>
<Board4251BaudRate>115200</Board4251BaudRate>
<Board4251Timeout>5000</Board4251Timeout>
<SpecUL>1.00</SpecUL>
<SpecLL>-1.00</SpecLL>
</AppConfig>
```
| 설정 키 | 기본값 | 설정 위치 | 설명 |
|---------|--------|----------|------|
| `SensorPort` | COM1 | Comm. Settings | C28 센서 COM 포트 |
| `SensorBaudRate` | 9600 | Comm. Settings | C28 센서 통신 속도 |
| `Board4251Port` | COM3 | Comm. Settings | 4251 보드 COM 포트 |
| `Board4251BaudRate` | 115200 | Comm. Settings | 4251 보드 통신 속도 |
| `Board4251Timeout` | 5000 | (코드 내 고정) | 4251 보드 응답 대기 시간(ms) |
| `SpecUL` | 1.00 | Parameters | 누설 상한 규격값 |
| `SpecLL` | -1.00 | Parameters | 누설 하한 규격값 |
### Database.ini
| 항목 | 내용 |
|------|------|
| **위치** | 프로그램 실행 폴더 |
| **생성** | 배포 시 기본 포함 및 파일 누락 상태로 기동 시 기본 양식이 자동 생성됨 |
| **수정** | 메모장 등의 텍스트 에디터를 통해 연결 주소 및 프로시저 정보 직접 수정 |
```ini
; [데이터베이스 접속 상세 설정]
Db_Server=192.168.0.20
Db_Name=NE1aW_PT_SENSOR
Db_User=sa
Db_Password=sa1234
[Login]
Procedure=dbo.CheckOperator
ProcessName=EOL
OfflinePreview=false
```
| 설정 키 | 예시 | 설명 |
|---------|--------|------|
| `Db_Server` | 192.168.0.20 | 데이터베이스 서버 IP 주소 또는 호스트명 |
| `Db_Name` | NE1aW_PT_SENSOR | 대상 데이터베이스(DB) 이름 |
| `Db_User` | sa | 데이터베이스 접속 계정 ID |
| `Db_Password` | sa1234 | 데이터베이스 접속 계정 비밀번호 |
| `Procedure` | dbo.CheckOperator | 로그인 계정 검증용 저장 프로시저명 |
| `ProcessName` | EOL | 검증 공정명 |
| `OfflinePreview` | false | true 시 DB 연결 없이 로그인 즉시 성공 (오프라인 모드) |
### DioConfig.ini
| 항목 | 내용 |
|------|------|
| **위치** | `[프로그램 폴더]\Settings\DioConfig.ini` |
| **생성** | 최초 실행 시 자동 생성 |
| **수정** | 일반적으로 수정 불필요 (하드웨어 변경 시에만) |
```ini
[DIO]
CompanyName=ADLink
BoardCnt=1
[BOARD_1]
BoardType=PCI_7432
BoardNumber=0
[BOARD_1_IN]
0=LEFT_START,1,좌측 시험 시작 신호
1=RIGHT_START,1,우측 시험 시작 신호
[BOARD_1_OUT]
0=LEFT_OK,1,좌측 합격 출력
1=RIGHT_OK,1,우측 합격 출력
2=PC_ON,1,PC 가동 신호
3=LEFT_NG,1,좌측 불합격 출력
4=RIGHT_NG,1,우측 불합격 출력
```
**INI 포맷 설명**: `비트인덱스=신호명,활성여부,설명`
---
## 8.4 파일 관리 주의사항
| 주의사항 | 설명 |
|---------|------|
| Logs 폴더 삭제 금지 | 검사 기록이 영구 삭제됩니다 |
| CSV 파일 직접 편집 | Excel 등으로 편집 시 인코딩이 변경될 수 있으므로 주의 |
| config.xml 직접 편집 | 잘못된 값 입력 시 프로그램 오작동 가능. 프로그램 내 설정 화면 사용 권장 |
| 디스크 용량 | 일일 수백 건 검사 기준, 연간 약 50~100MB 정도의 로그가 생성됩니다 |
---
## 8.5 데이터베이스 데이터 관리 (dbo.EOL)
매 시험 완료 시 검사 결과 및 로그인 정보가 데이터베이스에 실시간으로 저장(MERGE/UPSERT)됩니다.
### 데이터베이스 테이블 및 컬럼 매핑
* **대상 테이블**: `[dbo].[EOL]`
* **매핑 상세**:
| DB 컬럼명 | 데이터 타입 | 매핑되는 C# 데이터 | 비고 |
| :--- | :--- | :--- | :--- |
| **`IC_SN`** | `NVARCHAR(10)` | 제품의 최종 조합 ID (`inspectData.ProductId`) | **PK / FK** |
| **`PCB_Barcode`** | `NVARCHAR(10)` | 고유 식별용 바코드 정보 (`inspectData.ProductId` 매핑) | **UK** |
| `Maker` | `NVARCHAR(10)` | 로그인 제조사 정보 (`App.LoginResult.Maker`) | |
| `Model` | `NVARCHAR(10)` | 로그인 모델 정보 (`App.LoginResult.Model`) | |
| `Variant_1` | `NVARCHAR(10)` | 로그인 변형1 정보 (`App.LoginResult.Variant1`) | |
| `Variant_2` | `NVARCHAR(10)` | 로그인 변형2 정보 (`App.LoginResult.Variant2`) | |
| `Operator` | `NVARCHAR(10)` | 로그인 검사자 ID (`App.LoginResult.Operator`) | |
| `Production_Date` | `DATETIME2(0)` | 검사 수행 일시 (`DateTime.Now`) | |
| `Line` | `NVARCHAR(5)` | 로그인 작업 라인 (`App.LoginResult.LineNo`) | |
| `Lot_No` | `NVARCHAR(8)` | 로그인 Lot 번호 (`App.LoginResult.LotNo`) | |
| `Jig_No` | `NVARCHAR(10)` | 로그인 지그 번호 (`App.LoginResult.JigNo`) | |
| `Channel` | `NVARCHAR(5)` | 검사 채널 (`LEFT` / `RIGHT`) | |
| `Spec_UL` | `DECIMAL(3,2)` | 규격 상한값 (소수점 2자리) | |
| `Spec_LL` | `DECIMAL(3,2)` | 규격 하한값 (소수점 2자리) | |
| `Leak_Value` | `DECIMAL(5,4)` | 측정된 릭 값 (소수점 4자리) | |
| `Result` | `NVARCHAR(5)` | 최종 판정 결과 (`OK` 또는 `NG`) | |
---
[← 이전: 시험 프로세스](07_시험_프로세스.md) | [다음: 오류 대응 →](09_오류_대응.md)

150
leak_test_project/Manual/09_로그_관리.md

@ -1,150 +0,0 @@
# 8. 로그 및 데이터 관리
[← 메뉴얼 목차로 돌아가기](사용자_메뉴얼.md)
---
## 8.1 검사 데이터 로그 (CSV)
### 기본 정보
| 항목 | 내용 |
|------|------|
| **저장 위치** | `[프로그램 폴더]\Logs\yyyy-MM-dd.csv` |
| **파일 형식** | CSV (UTF-8 인코딩) |
| **파일 생성** | 날짜별 자동 생성 (해당 날짜 첫 시험 시) |
| **데이터 추가** | 매 시험 완료 시 자동으로 해당 날짜 파일에 1행 추가 |
### CSV 헤더 및 컬럼
```
Date,Time,Channel,ID,Value,Judgment,SpecUL,SpecLL,Retest
```
| 컬럼 | 예시 | 설명 |
|------|------|------|
| `Date` | `2026-05-08` | 검사 일자 |
| `Time` | `10:30:15` | 검사 시각 |
| `Channel` | `LEFT` | 검사 채널 (LEFT 또는 RIGHT) |
| `ID` | `260508A00123B01C` | 제품 ID |
| `Value` | `0.5234` | 측정값 (sccm, 소수점 4자리) |
| `Judgment` | `OK` | 판정 결과 (OK 또는 NG) |
| `SpecUL` | `1.00` | 검사 시점의 상한 규격값 |
| `SpecLL` | `-1.00` | 검사 시점의 하한 규격값 |
| `Retest` | `N` | 재시험 여부 (Y 또는 N) |
### CSV 예시
```csv
Date,Time,Channel,ID,Value,Judgment,SpecUL,SpecLL,Retest
2026-05-08,10:30:15,LEFT,260508A00123B01C,0.5234,OK,1.00,-1.00,N
2026-05-08,10:30:45,RIGHT,260508A00456D02E,1.2000,NG,1.00,-1.00,N
2026-05-08,10:31:20,LEFT,260508A00789F03G,0.0000,NG,1.00,-1.00,N
```
> **참고**: 타임아웃 발생 시에도 측정값 `0.0000`, 판정 `NG`로 로그에 기록됩니다.
> **참고**: CSV 값에 쉼표, 따옴표, 줄바꿈이 포함된 경우 자동으로 이스케이프 처리됩니다.
---
## 8.2 시스템 로그
### 기본 정보
| 항목 | 내용 |
|------|------|
| **저장 위치** | `[프로그램 폴더]\Logs\yyyy-MM-dd_system.log` |
| **파일 형식** | 텍스트 파일 (UTF-8 인코딩) |
| **내용** | 통신 오류, 시스템 이벤트 등 |
### 로그 형식
```
[HH:mm:ss.fff] [TAG] 메시지 내용
```
### 예시
```
[10:30:00.123] [SYSTEM] 프로그램 시작
[10:30:01.456] [COMM] Sentinel C28 포트 연결 성공 (COM1)
[10:35:15.789] [ERROR] Sentinel C28 통신 타임아웃
```
---
## 8.3 설정 파일
### config.xml
| 항목 | 내용 |
|------|------|
| **위치** | 프로그램 실행 폴더 |
| **생성** | 최초 실행 시 기본값으로 자동 생성 |
| **수정** | Comm. Settings 또는 Parameters에서 저장 시 갱신 |
```xml
<AppConfig>
<SensorPort>COM1</SensorPort>
<SensorBaudRate>9600</SensorBaudRate>
<Board4251Port>COM3</Board4251Port>
<Board4251BaudRate>115200</Board4251BaudRate>
<Board4251Timeout>5000</Board4251Timeout>
<SpecUL>1.00</SpecUL>
<SpecLL>-1.00</SpecLL>
</AppConfig>
```
| 설정 키 | 기본값 | 설정 위치 | 설명 |
|---------|--------|----------|------|
| `SensorPort` | COM1 | Comm. Settings | C28 센서 COM 포트 |
| `SensorBaudRate` | 9600 | Comm. Settings | C28 센서 통신 속도 |
| `Board4251Port` | COM3 | Comm. Settings | 4251 보드 COM 포트 |
| `Board4251BaudRate` | 115200 | Comm. Settings | 4251 보드 통신 속도 |
| `Board4251Timeout` | 5000 | (코드 내 고정) | 4251 보드 응답 대기 시간(ms) |
| `SpecUL` | 1.00 | Parameters | 누설 상한 규격값 |
| `SpecLL` | -1.00 | Parameters | 누설 하한 규격값 |
### DioConfig.ini
| 항목 | 내용 |
|------|------|
| **위치** | `[프로그램 폴더]\Settings\DioConfig.ini` |
| **생성** | 최초 실행 시 자동 생성 |
| **수정** | 일반적으로 수정 불필요 (하드웨어 변경 시에만) |
```ini
[DIO]
CompanyName=ADLink
BoardCnt=1
[BOARD_1]
BoardType=PCI_7432
BoardNumber=0
[BOARD_1_IN]
0=LEFT_START,1,좌측 시험 시작 신호
1=RIGHT_START,1,우측 시험 시작 신호
[BOARD_1_OUT]
0=LEFT_OK,1,좌측 합격 출력
1=RIGHT_OK,1,우측 합격 출력
2=PC_ON,1,PC 가동 신호
3=LEFT_NG,1,좌측 불합격 출력
4=RIGHT_NG,1,우측 불합격 출력
```
**INI 포맷 설명**: `비트인덱스=신호명,활성여부,설명`
---
## 8.4 파일 관리 주의사항
| 주의사항 | 설명 |
|---------|------|
| Logs 폴더 삭제 금지 | 검사 기록이 영구 삭제됩니다 |
| CSV 파일 직접 편집 | Excel 등으로 편집 시 인코딩이 변경될 수 있으므로 주의 |
| config.xml 직접 편집 | 잘못된 값 입력 시 프로그램 오작동 가능. 프로그램 내 설정 화면 사용 권장 |
| 디스크 용량 | 일일 수백 건 검사 기준, 연간 약 50~100MB 정도의 로그가 생성됩니다 |
---
[← 이전: 시험 프로세스](08_시험_프로세스.md) | [다음: 오류 대응 →](10_오류_대응.md)

21
leak_test_project/Manual/10_오류_대응.md → leak_test_project/Manual/09_오류_대응.md

@ -15,8 +15,6 @@
> **참고**: 통신 설정을 저장하면 기존 연결을 모두 해제하고 새 설정으로 재연결을 시도합니다. 포트 변경 후 재시작 없이 바로 적용됩니다. > **참고**: 통신 설정을 저장하면 기존 연결을 모두 해제하고 새 설정으로 재연결을 시도합니다. 포트 변경 후 재시작 없이 바로 적용됩니다.
---
## 9.2 시험 중 오류 ## 9.2 시험 중 오류
시험 프로세스 진행 중 발생하는 오류입니다. 메인 화면의 **오류 메시지** 영역에 표시됩니다. 시험 프로세스 진행 중 발생하는 오류입니다. 메인 화면의 **오류 메시지** 영역에 표시됩니다.
@ -24,11 +22,16 @@
| 오류 메시지 | 원인 | 조치 방법 | | 오류 메시지 | 원인 | 조치 방법 |
|------------|------|----------| |------------|------|----------|
| `Sentinel C28 통신 타임아웃` | 30초 내 C28 센서 응답 없음 | ① 센서 전원 ON 확인 ② 시리얼 케이블 연결 확인 ③ BaudRate 설정 확인 (기본: 9600) | | `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 설정 확인 ③ 양쪽 설정값 동기화 | | `SPEC 불일치 - 프로그램: OK, 센서: R` | 프로그램 SPEC과 센서 자체 SPEC의 판정이 다름 | ① 프로그램의 SPEC UL/LL 확인 (Parameters) ② C28 센서의 SPEC 설정 확인 ③ 양쪽 설정값 동기화 |
| `LEAK 센서 데이터 오류` | C28에서 수신된 데이터 파싱 실패 | ① 센서 통신 상태 확인 ② 케이블 접촉 불량 확인 ③ BaudRate 불일치 확인 | | `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 입출력이 동작하지 않음 (실제 입출력 포트 제어 불가) | | PCI-7432 보드 미인식 | **시뮬레이션 모드**로 자동 전환 | DIO 입출력이 동작하지 않음 (실제 입출력 포트 제어 불가) |
| 보드 런타임 오류 | 좌/우 채널 오류 메시지에 `[DIO Board Error]` 표시 | 오류 내용에 따라 조치 | | 보드 런타임 오류 | 좌/우 채널 오류 메시지에 `[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)

BIN
leak_test_project/Manual/Pasted image 20260624104139.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104208.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104256.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104353.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104445.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104506.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104531.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
leak_test_project/Manual/Pasted image 20260624104558.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

16
leak_test_project/Manual/사용자_메뉴얼.md

@ -11,14 +11,14 @@
| 번호 | 문서 | 설명 | | 번호 | 문서 | 설명 |
|------|------|------| |------|------|------|
| 1 | [시스템 개요 및 요구사항](01_시스템_개요.md) | 프로그램 소개, 주요 기능, 하드웨어/소프트웨어 요구사항, DIO 신호 매핑 | | 1 | [시스템 개요 및 요구사항](01_시스템_개요.md) | 프로그램 소개, 주요 기능, 하드웨어/소프트웨어 요구사항, DIO 신호 매핑 |
| 2 | [초기 설정 가이드](02_초기_설정.md) | 설치 전 준비, 프로그램 실행, 최초 설정 순서 (통신→시험→I/O 확인) | | 2 | [초기 설정 가이드](02_초기_설정.md) | 설치 전 준비, 로그인 및 프로그램 실행, 최초 설정 순서 (통신→시험→동작 테스트) |
| 3 | [메인 화면 (Home View)](03_메인_화면.md) | 타이틀 바, 좌/우 채널 모니터링 영역, 하단 메뉴 바 상세 설명 | | 3 | [메인 화면 (Home View)](03_메인_화면.md) | 타이틀 바(작업자/모델/라인 정보 배지 및 로그아웃), 좌/우 채널 모니터링 영역, 하단 메뉴 바 상세 설명 |
| 4 | [통신 설정 (Comm. Settings)](04_통신_설정.md) | 4251 보드 및 Sentinel C28 센서의 COM 포트/BaudRate 설정 방법 | | 4 | [통신 설정 (Comm. Settings)](04_통신_설정.md) | 4251 보드 및 Sentinel C28 센서의 COM 포트/BaudRate 설정 방법 |
| 5 | [시험 설정 (Parameters)](05_시험_설정.md) | SPEC UL/LL 판정 규격값 설정 방법 | | 5 | [시험 설정 (Parameters)](05_시험_설정.md) | SPEC UL/LL 판정 규격값 설정 방법 |
| 6 | [측정 데이터 조회 (Data View)](07_데이터_조회.md) | 검사 기록 검색, 필터링, CSV 내보내기 방법 | | 6 | [측정 데이터 조회 (Data View)](06_데이터_조회.md) | 검사 기록 검색, 필터링, CSV 내보내기 방법 |
| 7 | [자동 시험 프로세스](08_시험_프로세스.md) | 7단계 자동 시험 흐름, 순차 실행, ID 수동 테스트 | | 7 | [자동 시험 프로세스](07_시험_프로세스.md) | 7단계 자동 시험 흐름, 순차 실행, ID 수동 테스트 |
| 8 | [로그 및 데이터 관리](09_로그_관리.md) | 검사 데이터 로그, 시스템 로그, 설정 파일 구조 | | 8 | [로그 및 데이터 관리](08_로그_관리.md) | 검사 데이터 로그, 시스템 로그, 설정 파일 구조 |
| 9 | [오류 대응 및 프로그램 종료](10_오류_대응.md) | 통신 오류, 시험 중 오류, DIO 보드 오류 대응, 종료 절차 | | 9 | [오류 대응 및 프로그램 종료](09_오류_대응.md) | 통신 오류, 시험 중 오류, DIO 보드 오류 대응, 종료 절차 |
--- ---
@ -27,10 +27,10 @@
처음 사용하는 경우 아래 순서를 따르세요: 처음 사용하는 경우 아래 순서를 따르세요:
1. **하드웨어 연결 확인** → [시스템 개요 및 요구사항](01_시스템_개요.md) 참조 1. **하드웨어 연결 확인** → [시스템 개요 및 요구사항](01_시스템_개요.md) 참조
2. **프로그램 실행**`leak_test_project.exe` 더블 클릭 2. **프로그램 실행 및 로그인**`leak_test_project.exe` 더블 클릭 후 작업 정보 입력 및 로그인 완료 (데모 계정은 ID/PW `test`/`test` 입력, 오프라인 환경일 경우 `Database.ini`에서 `OfflinePreview=true` 설정 확인)
3. **통신 설정** → 하단 메뉴 `Comm. Settings` → COM 포트 선택 → `설정 저장` 3. **통신 설정** → 하단 메뉴 `Comm. Settings` → COM 포트 선택 → `설정 저장`
4. **시험 설정** → 하단 메뉴 `Parameters` → SPEC UL/LL 입력 → `설정 저장` 4. **시험 설정** → 하단 메뉴 `Parameters` → SPEC UL/LL 입력 → `설정 저장`
5. **시험 시작** → DIO 시작 신호 또는 `ID Test` 버튼으로 동작 확인 5. **시험 시작** → DIO 시작 신호 또는 `ID Test (L/R)` 버튼으로 동작 확인
> **참고**: 각 단계의 상세 내용은 위 문서 링크를 참조하세요. > **참고**: 각 단계의 상세 내용은 위 문서 링크를 참조하세요.

21
leak_test_project/Models/ProjectModels.cs

@ -16,7 +16,10 @@ namespace leak_test_project.Models
public double SpecUL { get; set; } = 1.00; public double SpecUL { get; set; } = 1.00;
public double SpecLL { get; set; } = -1.00; public double SpecLL { get; set; } = -1.00;
/// <summary>MSSQL 데이터베이스 연결 문자열</summary> /// <summary>
/// MSSQL 데이터베이스 연결 문자열 (더 이상 사용되지 않음. 대신 Database.ini의 설정을 읽어 접속합니다.)
/// </summary>
[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;"; 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 SpecLL { get; set; }
public string MeasuredValue { get; set; } public string MeasuredValue { get; set; }
public string Judgment { 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 public class ParsedData
@ -154,7 +170,6 @@ namespace leak_test_project.Models
/// <summary>최종 조합 ID (년+월+일+시리얼+라인+항목)</summary> /// <summary>최종 조합 ID (년+월+일+시리얼+라인+항목)</summary>
public string ID { get; set; } = ""; public string ID { get; set; } = "";
/// <summary>이전 검사 결과. F=미시험/불합격, P=합격. 불량 제품 필터링에 사용.</summary>
public string PrevResult { get; set; } = "";
} }
} }

41
leak_test_project/Services/Board4251.cs

@ -290,7 +290,7 @@ namespace leak_test_project.Services
public bool Connect() => _service.Connect(); public bool Connect() => _service.Connect();
public void Disconnect() => _service.Disconnect(); public void Disconnect() => _service.Disconnect();
public SensorIdData ReadSensor() public async Task<SensorIdData> ReadSensorAsync()
{ {
try try
{ {
@ -298,23 +298,33 @@ namespace leak_test_project.Services
// 0. 초기화 (x00o 전송) // 0. 초기화 (x00o 전송)
ProgressMessage?.Invoke(this, "4251 보드 초기화 중..."); ProgressMessage?.Invoke(this, "4251 보드 초기화 중...");
// x00o 명령을 보내고 응답 내용에 상관없이 다음 단계 진행 (최대 5초 대기) // x00o 명령을 보내고 응답 내용에 상관없이 다음 단계 진행
Task.Run(() => _service.SendCommandAsync("x00o\r\n")).Wait(1000); _ = _service.SendCommandAsync("x00o\r\n");
Task.Delay(350).Wait(); // 보드 안정화 대기 await Task.Delay(350); // 보드 안정화 대기
// 1. 보드 상태 확인 (Fail인 경우 중단) // 1. 보드 상태 확인 (Fail인 경우 중단)
ProgressMessage?.Invoke(this, "4251 보드 데이터 읽는 중..."); ProgressMessage?.Invoke(this, "4251 보드 데이터 읽는 중...");
int extendedTimeout = 15000; int extendedTimeout = 15000;
string statusCmd = $"x00c_00{channel}101:owt28006727ea97c7801"; string statusCmd = $"x00c_00{channel}101:owt28006727ea97c7801";
var statusTask = Task.Run(() => _service.CheckStatusAsync(channel)); bool statusResult = false;
if (!statusTask.Wait(extendedTimeout))
using (var cts = new CancellationTokenSource(extendedTimeout))
{
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 buf = _service.GetLastBuffer();
string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf;
ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}");
return null; return null;
} }
if (!statusTask.Result) cts.Cancel();
statusResult = await statusTask;
}
if (!statusResult)
{ {
string buf = _service.GetLastBuffer(); string buf = _service.GetLastBuffer();
string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf;
@ -329,8 +339,12 @@ namespace leak_test_project.Services
for (int attempt = 1; attempt <= maxAttempts; attempt++) for (int attempt = 1; attempt <= maxAttempts; attempt++)
{ {
var idTask = Task.Run(() => _service.ReadIdAsync(channel)); using (var cts = new CancellationTokenSource(extendedTimeout))
if (!idTask.Wait(extendedTimeout)) {
var idTask = _service.ReadIdAsync(channel);
var delayTask = Task.Delay(extendedTimeout, cts.Token);
var completedTask = await Task.WhenAny(idTask, delayTask);
if (completedTask == delayTask)
{ {
if (attempt == maxAttempts) if (attempt == maxAttempts)
{ {
@ -341,8 +355,10 @@ namespace leak_test_project.Services
} }
continue; continue;
} }
cts.Cancel();
rawId = await idTask;
}
rawId = idTask.Result;
if (string.IsNullOrEmpty(rawId)) if (string.IsNullOrEmpty(rawId))
{ {
if (attempt == maxAttempts) if (attempt == maxAttempts)
@ -361,7 +377,7 @@ namespace leak_test_project.Services
if (attempt < maxAttempts) if (attempt < maxAttempts)
{ {
ProgressMessage?.Invoke(this, $"ID 읽기 재시도 중... ({attempt}/{maxAttempts})"); ProgressMessage?.Invoke(this, $"ID 읽기 재시도 중... ({attempt}/{maxAttempts})");
Task.Delay(350).Wait(); // 재시도 전 약간의 딜레이 await Task.Delay(350); // 재시도 전 약간의 딜레이
continue; continue;
} }
else else
@ -382,8 +398,7 @@ namespace leak_test_project.Services
LowID = rawId, LowID = rawId,
ID = rawId, // 16자리 전체를 ID로 사용 ID = rawId, // 16자리 전체를 ID로 사용
Serial = "", // 시리얼 번호는 현재 존재하지 않으므로 강제로 파싱하지 않음 Serial = "", // 시리얼 번호는 현재 존재하지 않으므로 강제로 파싱하지 않음
Item = "N/A", Item = "N/A"
PrevResult = "F" // '불량제품 투입' 필터를 통과하기 위한 강제 초기화
}; };
ProgressMessage?.Invoke(this, "ID 읽기 성공"); ProgressMessage?.Invoke(this, "ID 읽기 성공");

122
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();
}
}
}
}

82
leak_test_project/Services/EolDatabaseService.cs

@ -21,46 +21,84 @@ namespace leak_test_project.Services
/// EOL 검사 결과를 데이터베이스에 저장합니다. (동일 ID 존재 시 업데이트, 미존재 시 삽입) /// EOL 검사 결과를 데이터베이스에 저장합니다. (동일 ID 존재 시 업데이트, 미존재 시 삽입)
/// </summary> /// </summary>
/// <param name="inspectData">검사 결과 데이터</param> /// <param name="inspectData">검사 결과 데이터</param>
public async Task SaveEolDataAsync(InspectData inspectData) /// <returns>저장 성공 여부 (성공: true, 실패: false)</returns>
public async Task<bool> 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 = @" const string query = @"
MERGE INTO [dbo].[EOL] AS Target MERGE INTO [dbo].[EOL] AS Target
USING (SELECT @ID AS ID) AS Source USING (SELECT @IC_SN AS IC_SN) AS Source
ON (Target.ID = Source.ID) ON (Target.IC_SN = Source.IC_SN)
WHEN MATCHED THEN WHEN MATCHED THEN
UPDATE SET 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.Channel = @Channel,
Target.SpecUL = @SpecUL, Target.Spec_UL = @Spec_UL,
Target.SpecLL = @SpecLL, Target.Spec_LL = @Spec_LL,
Target.LeakValue = @LeakValue, Target.Leak_Value = @Leak_Value,
Target.Result = @Result, Target.Result = @Result
Target.InspectDate = @InspectDate,
Target.InspectTime = @InspectTime
WHEN NOT MATCHED THEN WHEN NOT MATCHED THEN
INSERT ([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 (@ID, @Channel, @SpecUL, @SpecLL, @LeakValue, @Result, @InspectDate, @InspectTime);"; 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 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 conn = new SqlConnection(_connectionString))
{ {
using (var cmd = new SqlCommand(query, conn)) 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("@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); // Spec_UL, Spec_LL, Leak_Value 형 변환 및 대입
cmd.Parameters.AddWithValue("@LeakValue", string.IsNullOrEmpty(inspectData.MeasuredValue) ? "0.0000" : inspectData.MeasuredValue); 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("@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 conn.OpenAsync();
await cmd.ExecuteNonQueryAsync(); 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 커넥션 예외로 중단되지 않도록 예외 격리 처리 // 실시간 검사 공정 루프 흐름이 DB 커넥션 예외로 중단되지 않도록 예외 격리 처리
Console.WriteLine($"[DB ERROR] Failed to upsert EOL Data: {ex.Message}"); Console.WriteLine($"[DB ERROR] Failed to upsert EOL Data: {ex.Message}");
return false;
} }
} }
@ -89,8 +128,9 @@ namespace leak_test_project.Services
return true; return true;
} }
} }
catch catch (Exception ex)
{ {
System.Diagnostics.Debug.WriteLine($"[DB Connection Check Error] {ex.Message}");
return false; return false;
} }
} }

2
leak_test_project/Services/IIdSensorService.cs

@ -11,6 +11,6 @@ namespace leak_test_project.Services
bool Connect(); bool Connect();
void Disconnect(); void Disconnect();
SensorIdData ReadSensor(); System.Threading.Tasks.Task<SensorIdData> ReadSensorAsync();
} }
} }

99
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<bool> ExistsAsync(string operatorId, string password)
{
// 1. 데모 계정(test/test)인 경우 DB 조회 없이 즉시 로그인 통과
if (IsDefaultAccount(operatorId, password))
{
return true;
}
// 2. 오프라인 모드인 경우 기본 성공
if (_loginSettings.OfflinePreview)
{
return true;
}
// DB 커넥션 스트링이 비어있으면 실패 처리
if (string.IsNullOrWhiteSpace(_dbSettings.ConnectionString))
{
return false;
}
try
{
// 실제 DB에서 계정을 조회해보기 위한 시도
using (var conn = new System.Data.SqlClient.SqlConnection(_dbSettings.ConnectionString))
{
await conn.OpenAsync();
if (!string.IsNullOrWhiteSpace(_loginSettings.Procedure))
{
// 저장 프로시저 호출
using (var cmd = new System.Data.SqlClient.SqlCommand(_loginSettings.Procedure, conn))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("@OperatorId", operatorId);
cmd.Parameters.AddWithValue("@Password", password);
cmd.Parameters.AddWithValue("@ProcessName", _loginSettings.ProcessName ?? string.Empty);
var result = await cmd.ExecuteScalarAsync();
if (result != null && result != DBNull.Value)
{
if (int.TryParse(result.ToString(), out var count))
{
return count > 0;
}
}
return false;
}
}
else
{
// 일반 쿼리 수행
const string query = "SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id AND [Password] = @Pw";
using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn))
{
cmd.Parameters.AddWithValue("@Id", operatorId);
cmd.Parameters.AddWithValue("@Pw", password);
var count = (int)await cmd.ExecuteScalarAsync();
return count > 0;
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[LoginAccountRepository ExistsAsync Error] {ex.Message}");
// DB 연결이나 쿼리 오류 발생 시 로그인 실패 처리
return false;
}
}
private bool IsDefaultAccount(string operatorId, string password)
{
// 기본 개발/테스트용 데모 계정 처리 (test/test만 허용)
if (string.Equals(operatorId, "test", StringComparison.OrdinalIgnoreCase) &&
string.Equals(password, "test", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
}
}

147
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<HistoryCategory> Categories { get; set; } = new List<HistoryCategory>();
}
public class HistoryCategory
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlElement("Value")]
public List<string> Values { get; set; } = new List<string>();
}
public class LoginHistoryStore
{
private readonly string _filePath;
private readonly Dictionary<string, List<string>> _history = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
private LoginHistoryStore(string filePath)
{
_filePath = filePath;
}
public static LoginHistoryStore Load(string path)
{
var store = new LoginHistoryStore(path);
store.LoadFromFile();
return store;
}
public List<string> GetValues(string fieldName)
{
if (_history.TryGetValue(fieldName, out var list))
{
return new List<string>(list);
}
return new List<string>();
}
public void Remember(string fieldName, string value)
{
if (string.IsNullOrWhiteSpace(value)) return;
value = value.Trim();
if (!_history.TryGetValue(fieldName, out var list))
{
list = new List<string>();
_history[fieldName] = list;
}
// 중복 제거 및 맨 앞으로 이동
list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase));
list.Insert(0, value);
// 최대 5개 이력 유지
if (list.Count > 5)
{
list.RemoveRange(5, list.Count - 5);
}
}
public bool Forget(string fieldName, string value)
{
if (string.IsNullOrWhiteSpace(value)) return false;
value = value.Trim();
if (_history.TryGetValue(fieldName, out var list))
{
bool removed = list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)) > 0;
return removed;
}
return false;
}
public void Save()
{
try
{
var dir = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
var data = new LoginHistoryData();
foreach (var kvp in _history)
{
data.Categories.Add(new HistoryCategory
{
Name = kvp.Key,
Values = kvp.Value
});
}
XmlSerializer serializer = new XmlSerializer(typeof(LoginHistoryData));
using (var sw = new StreamWriter(_filePath))
{
serializer.Serialize(sw, data);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[LoginHistoryStore Save Error] {ex.Message}");
}
}
private void LoadFromFile()
{
try
{
if (!File.Exists(_filePath)) return;
XmlSerializer serializer = new XmlSerializer(typeof(LoginHistoryData));
using (var sr = new StreamReader(_filePath))
{
var data = (LoginHistoryData)serializer.Deserialize(sr);
if (data?.Categories != null)
{
_history.Clear();
foreach (var cat in data.Categories)
{
if (!string.IsNullOrEmpty(cat.Name))
{
_history[cat.Name] = cat.Values ?? new List<string>();
}
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"[LoginHistoryStore Load Error] {ex.Message}");
}
}
}
}

295
leak_test_project/Services/LoginServicesStub.cs

@ -1,295 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace Housing.Services
{
// 1. DatabaseSettings
public class DatabaseSettings
{
public string ConnectionString { get; set; } = string.Empty;
public static DatabaseSettings Load(string path)
{
var settings = new DatabaseSettings();
try
{
if (File.Exists(path))
{
var lines = File.ReadAllLines(path);
foreach (var line in lines)
{
var trimmed = line.Trim();
if (trimmed.StartsWith("ConnectionString", StringComparison.OrdinalIgnoreCase))
{
var parts = trimmed.Split(new[] { '=' }, 2);
if (parts.Length == 2)
{
settings.ConnectionString = parts[1].Trim();
}
}
}
}
}
catch
{
// 로드 실패 시 무시
}
return settings;
}
}
// 2. LoginSettings
public class LoginSettings
{
public bool OfflinePreview { get; set; } = true; // 기본값은 오프라인 테스트 편의를 위해 true
public static LoginSettings Load(string path)
{
var settings = new LoginSettings();
try
{
if (File.Exists(path))
{
var lines = File.ReadAllLines(path);
foreach (var line in lines)
{
var trimmed = line.Trim();
if (trimmed.StartsWith("OfflinePreview", StringComparison.OrdinalIgnoreCase))
{
var parts = trimmed.Split(new[] { '=' }, 2);
if (parts.Length == 2)
{
if (bool.TryParse(parts[1].Trim(), out var val))
{
settings.OfflinePreview = val;
}
else if (int.TryParse(parts[1].Trim(), out var intVal))
{
settings.OfflinePreview = intVal != 0;
}
}
}
}
}
}
catch
{
// 로드 실패 시 무시
}
return settings;
}
}
// 3. LoginHistoryStore
public class LoginHistoryStore
{
private readonly string _filePath;
private readonly Dictionary<string, List<string>> _history = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
private LoginHistoryStore(string filePath)
{
_filePath = filePath;
}
public static LoginHistoryStore Load(string path)
{
var store = new LoginHistoryStore(path);
store.LoadFromFile();
return store;
}
public List<string> GetValues(string fieldName)
{
if (_history.TryGetValue(fieldName, out var list))
{
return new List<string>(list);
}
return new List<string>();
}
public void Remember(string fieldName, string value)
{
if (string.IsNullOrWhiteSpace(value)) return;
value = value.Trim();
if (!_history.TryGetValue(fieldName, out var list))
{
list = new List<string>();
_history[fieldName] = list;
}
// 중복 제거 및 맨 앞으로 이동
list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase));
list.Insert(0, value);
// 최대 5개 이력 유지
if (list.Count > 5)
{
list.RemoveRange(5, list.Count - 5);
}
}
public bool Forget(string fieldName, string value)
{
if (string.IsNullOrWhiteSpace(value)) return false;
value = value.Trim();
if (_history.TryGetValue(fieldName, out var list))
{
bool removed = list.RemoveAll(x => string.Equals(x, value, StringComparison.OrdinalIgnoreCase)) > 0;
return removed;
}
return false;
}
public void Save()
{
try
{
var dir = Path.GetDirectoryName(_filePath);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
// 초간단 JSON 형식으로 직렬화 (의존성 최소화)
using (var sw = new StreamWriter(_filePath))
{
sw.WriteLine("{");
var keys = new List<string>(_history.Keys);
for (int i = 0; i < keys.Count; i++)
{
var key = keys[i];
var values = _history[key];
var arrayItems = new List<string>();
foreach (var val in values)
{
// 따옴표 escape
var escaped = val.Replace("\"", "\\\"");
arrayItems.Add($"\"{escaped}\"");
}
var line = $" \"{key}\": [{string.Join(", ", arrayItems)}]";
if (i < keys.Count - 1)
{
line += ",";
}
sw.WriteLine(line);
}
sw.WriteLine("}");
}
}
catch
{
// 저장 오류 시 무시
}
}
private void LoadFromFile()
{
try
{
if (!File.Exists(_filePath)) return;
var text = File.ReadAllText(_filePath);
// 정규식이나 아주 심플한 파싱으로 키와 배열 파싱
// 형식 예: "Maker": ["MOBI", "TEST"]
var matches = Regex.Matches(text, @"""([^""]+)""\s*:\s*\[([^\]]*)\]");
foreach (Match match in matches)
{
var key = match.Groups[1].Value;
var arrayContent = match.Groups[2].Value;
var valueMatches = Regex.Matches(arrayContent, @"""([^""]*)""");
var list = new List<string>();
foreach (Match valMatch in valueMatches)
{
list.Add(valMatch.Groups[1].Value);
}
_history[key] = list;
}
}
catch
{
// 로드 오류 시 무시
}
}
}
// 4. LoginAccountRepository
public class LoginAccountRepository
{
private readonly DatabaseSettings _dbSettings;
private readonly LoginSettings _loginSettings;
public LoginAccountRepository(DatabaseSettings dbSettings, LoginSettings loginSettings)
{
_dbSettings = dbSettings;
_loginSettings = loginSettings;
}
public async Task<bool> ExistsAsync(string operatorId, string password)
{
// 오프라인 모드인 경우 기본 성공
if (_loginSettings.OfflinePreview)
{
return true;
}
// DB 커넥션 스트링이 비어있으면 기본값 확인
if (string.IsNullOrWhiteSpace(_dbSettings.ConnectionString))
{
return IsDefaultAccount(operatorId, password);
}
try
{
// 실제 DB에서 계정을 조회해보기 위한 시도
using (var conn = new System.Data.SqlClient.SqlConnection(_dbSettings.ConnectionString))
{
await conn.OpenAsync();
// 우선 Users 또는 Accounts와 같이 로그인 테이블명이 뭔지 모를 수 있으므로
// 쿼리 실패 시 default 계정 검증으로 fallback 하도록 구성
const string query = "SELECT COUNT(*) FROM [Users] WHERE [OperatorId] = @Id AND [Password] = @Pw";
using (var cmd = new System.Data.SqlClient.SqlCommand(query, conn))
{
cmd.Parameters.AddWithValue("@Id", operatorId);
cmd.Parameters.AddWithValue("@Pw", password);
var count = (int)await cmd.ExecuteScalarAsync();
return count > 0;
}
}
}
catch
{
// DB 연결이나 쿼리 오류 발생 시, 테스트 편의를 위해 test/test 혹은 admin/admin 등 디폴트 계정을 제공
return IsDefaultAccount(operatorId, password);
}
}
private bool IsDefaultAccount(string operatorId, string password)
{
// 기본 개발/테스트용 계정 처리
if (string.Equals(operatorId, "test", StringComparison.OrdinalIgnoreCase) &&
string.Equals(password, "test", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (string.Equals(operatorId, "admin", StringComparison.OrdinalIgnoreCase) &&
string.Equals(password, "admin", StringComparison.OrdinalIgnoreCase))
{
return true;
}
if (string.Equals(operatorId, "su", StringComparison.OrdinalIgnoreCase) &&
string.Equals(password, "su", StringComparison.OrdinalIgnoreCase))
{
return true;
}
return false;
}
}
}

82
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();
}
}
}
}

168
leak_test_project/Services/TestProcessService.cs

@ -42,6 +42,7 @@ namespace leak_test_project.Services
private readonly IIdSensorService _leftSensor; private readonly IIdSensorService _leftSensor;
private readonly IIdSensorService _rightSensor; private readonly IIdSensorService _rightSensor;
private readonly SentinelC28Service _sentinelService; private readonly SentinelC28Service _sentinelService;
private readonly Housing.Login.StartupLoginWindowResult _loginInfo;
private Thread _leftThread; private Thread _leftThread;
private Thread _rightThread; private Thread _rightThread;
@ -50,6 +51,10 @@ namespace leak_test_project.Services
private volatile bool _running = true; private volatile bool _running = true;
private readonly SemaphoreSlim _testLock = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _testLock = new SemaphoreSlim(1, 1);
// DIO 시작 신호를 폴링 대신 대기하기 위한 이벤트
private readonly AutoResetEvent _leftStartEvent = new AutoResetEvent(false);
private readonly AutoResetEvent _rightStartEvent = new AutoResetEvent(false);
// LEAK 시험 결과를 수신하기 위한 필드 // LEAK 시험 결과를 수신하기 위한 필드
private ParsedData _leftResult; private ParsedData _leftResult;
private ParsedData _rightResult; private ParsedData _rightResult;
@ -79,12 +84,14 @@ namespace leak_test_project.Services
IDioBoard dioBoard, IDioBoard dioBoard,
IIdSensorService leftSensor, IIdSensorService leftSensor,
IIdSensorService rightSensor, IIdSensorService rightSensor,
SentinelC28Service sentinelService) SentinelC28Service sentinelService,
Housing.Login.StartupLoginWindowResult loginInfo)
{ {
_dioBoard = dioBoard; _dioBoard = dioBoard;
_leftSensor = leftSensor; _leftSensor = leftSensor;
_rightSensor = rightSensor; _rightSensor = rightSensor;
_sentinelService = sentinelService; _sentinelService = sentinelService;
_loginInfo = loginInfo;
// DIO START 신호 이벤트 연결 // DIO START 신호 이벤트 연결
_dioBoard.InputChanged += OnDioInputChanged; _dioBoard.InputChanged += OnDioInputChanged;
@ -110,8 +117,16 @@ namespace leak_test_project.Services
/// <summary>시뮬레이션용: 수동으로 시험 시작 트리거</summary> /// <summary>시뮬레이션용: 수동으로 시험 시작 트리거</summary>
public void TriggerTestStart(int testIndex) public void TriggerTestStart(int testIndex)
{ {
if (testIndex == 0) _leftTestStart = true; if (testIndex == 0)
else _rightTestStart = true; {
_leftTestStart = true;
_leftStartEvent.Set();
}
else
{
_rightTestStart = true;
_rightStartEvent.Set();
}
} }
/// <summary> /// <summary>
@ -140,19 +155,14 @@ namespace leak_test_project.Services
var sensor = isLeft ? _leftSensor : _rightSensor; var sensor = isLeft ? _leftSensor : _rightSensor;
// 시리얼 통신이므로 백그라운드에서 실행 // 비동기로 센서 정보 판독
var sensorData = await System.Threading.Tasks.Task.Run(() => sensor.ReadSensor()); var sensorData = await sensor.ReadSensorAsync();
if (sensorData != null) if (sensorData != null)
{ {
SensorReadComplete?.Invoke(this, (testIndex, sensorData)); SensorReadComplete?.Invoke(this, (testIndex, sensorData));
// 실제 검사 시퀀스와 동일한 불량 제품 필터링 체크
if (sensorData.PrevResult != "F")
{
NotifyError(testIndex, "불량 제품 투입 (이전 결과: " + sensorData.PrevResult + ")");
return;
}
NotifyProgress(testIndex, "ID 테스트 완료", "LightBlue"); NotifyProgress(testIndex, "ID 테스트 완료", "LightBlue");
} }
@ -160,7 +170,7 @@ namespace leak_test_project.Services
{ {
// 실패 시 NotifyError를 호출하지 않는 것도 실제 시퀀스와 동일 (센서 서비스가 이미 에러 이벤트 발생시킴) // 실패 시 NotifyError를 호출하지 않는 것도 실제 시퀀스와 동일 (센서 서비스가 이미 에러 이벤트 발생시킴)
// 다만 UI 갱신을 위해 빈 데이터는 전송 // 다만 UI 갱신을 위해 빈 데이터는 전송
sensorData = new SensorIdData { ID = "-", PrevResult = "F" }; sensorData = new SensorIdData { ID = "-" };
SensorReadComplete?.Invoke(this, (testIndex, sensorData)); SensorReadComplete?.Invoke(this, (testIndex, sensorData));
NotifyProgress(testIndex, "ID 테스트 실패", "Red"); NotifyProgress(testIndex, "ID 테스트 실패", "Red");
} }
@ -181,12 +191,14 @@ namespace leak_test_project.Services
_dioBoard.WriteOutput("LEFT_OK", false); _dioBoard.WriteOutput("LEFT_OK", false);
_dioBoard.WriteOutput("LEFT_NG", false); _dioBoard.WriteOutput("LEFT_NG", false);
_leftTestStart = true; _leftTestStart = true;
_leftStartEvent.Set();
} }
else if (e.PointName == "RIGHT_START" && e.NewValue) else if (e.PointName == "RIGHT_START" && e.NewValue)
{ {
_dioBoard.WriteOutput("RIGHT_OK", false); _dioBoard.WriteOutput("RIGHT_OK", false);
_dioBoard.WriteOutput("RIGHT_NG", false); _dioBoard.WriteOutput("RIGHT_NG", false);
_rightTestStart = true; _rightTestStart = true;
_rightStartEvent.Set();
} }
} }
@ -198,10 +210,14 @@ namespace leak_test_project.Services
{ {
bool isLeft = (testIndex == 0); bool isLeft = (testIndex == 0);
string side = isLeft ? "LEFT" : "RIGHT"; string side = isLeft ? "LEFT" : "RIGHT";
var startEvent = isLeft ? _leftStartEvent : _rightStartEvent;
while (_running) while (_running)
{ {
Thread.Sleep(2); // 이벤트 신호 대기 (2ms 폴링 대신 신호 수신 시까지 차단)
startEvent.WaitOne();
if (!_running) break;
// 시작 신호 대기 // 시작 신호 대기
bool shouldStart = isLeft ? _leftTestStart : _rightTestStart; bool shouldStart = isLeft ? _leftTestStart : _rightTestStart;
@ -237,7 +253,7 @@ namespace leak_test_project.Services
Debug.WriteLine($"[TestProcess-{side}] Phase 1: 센서 정보 읽기 시작"); Debug.WriteLine($"[TestProcess-{side}] Phase 1: 센서 정보 읽기 시작");
NotifyProgress(testIndex, "센서 정보 읽는 중", "LightSeaGreen"); NotifyProgress(testIndex, "센서 정보 읽는 중", "LightSeaGreen");
var sensor = isLeft ? _leftSensor : _rightSensor; var sensor = isLeft ? _leftSensor : _rightSensor;
var sensorData = sensor.ReadSensor(); var sensorData = sensor.ReadSensorAsync().GetAwaiter().GetResult();
if (sensorData != null) if (sensorData != null)
{ {
@ -245,19 +261,71 @@ namespace leak_test_project.Services
} }
else else
{ {
// 센서 읽기 실패해도 시험은 계속 진행 NotifyError(testIndex, "ID 읽기 실패");
sensorData = new SensorIdData { ID = "-", PrevResult = "F" }; _dioBoard.WriteOutput(side + "_NG", true);
SensorReadComplete?.Invoke(this, (testIndex, sensorData));
} 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);
// === 2단계: 불량 제품 필터링 === try
Debug.WriteLine($"[TestProcess-{side}] Phase 2: 센서 ID ({sensorData?.ID}), 이전결과 ({sensorData?.PrevResult}) 필터링 확인"); {
if (sensorData.PrevResult != "F") 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)
{ {
NotifyError(testIndex, "불량 제품 투입 (이전 결과: " + sensorData.PrevResult + ")"); Console.WriteLine("[DB ID Read Fail Integration Error] " + ex.Message);
}
NotifyProgress(testIndex, "시험 완료", "LightBlue");
continue; continue;
} }
// === 3단계: LEAK 시험 수행 === // === 3단계: LEAK 시험 수행 ===
Debug.WriteLine($"[TestProcess-{side}] Phase 3: LEAK 시험 대기 시작 (최대 30초)"); Debug.WriteLine($"[TestProcess-{side}] Phase 3: LEAK 시험 대기 시작 (최대 30초)");
NotifyProgress(testIndex, "LEAK 시험중", "LightYellow"); NotifyProgress(testIndex, "LEAK 시험중", "LightYellow");
@ -289,18 +357,38 @@ namespace leak_test_project.Services
InspectTime = DateTime.Now.ToString("HH:mm:ss"), InspectTime = DateTime.Now.ToString("HH:mm:ss"),
Channel = side, Channel = side,
ProductId = sensorData.ID, ProductId = sensorData.ID,
IcSn = sensorData.ID,
PcbBarcode = sensorData.ID,
MeasuredValue = "0.0000", MeasuredValue = "0.0000",
Judgment = "NG", Judgment = "NG",
SpecUL = ConfigManager.Current.SpecUL.ToString("F2"), 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); FileLogger.LogInspectData(timeoutData);
// === MSSQL 데이터베이스 비동기 저장 === // === MSSQL 데이터베이스 비동기 저장 ===
try try
{ {
var dbService = new EolDatabaseService(ConfigManager.Current.DbConnectionString); var dbIniPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Database.ini");
_ = Task.Run(async () => await dbService.SaveEolDataAsync(timeoutData)); 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) catch (Exception ex)
{ {
@ -349,18 +437,38 @@ namespace leak_test_project.Services
InspectTime = DateTime.Now.ToString("HH:mm:ss"), InspectTime = DateTime.Now.ToString("HH:mm:ss"),
Channel = side, Channel = side,
ProductId = sensorData.ID, ProductId = sensorData.ID,
IcSn = sensorData.ID,
PcbBarcode = sensorData.ID,
MeasuredValue = leakResult.MeasuredValueString, MeasuredValue = leakResult.MeasuredValueString,
Judgment = programJudgment, Judgment = programJudgment,
SpecUL = specUL.ToString("F2"), 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); FileLogger.LogInspectData(inspectData);
// === MSSQL 데이터베이스 비동기 저장 === // === MSSQL 데이터베이스 비동기 저장 ===
try try
{ {
var dbService = new EolDatabaseService(ConfigManager.Current.DbConnectionString); var dbIniPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Database.ini");
_ = Task.Run(async () => await dbService.SaveEolDataAsync(inspectData)); 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) catch (Exception ex)
{ {
@ -451,12 +559,16 @@ namespace leak_test_project.Services
_running = false; _running = false;
_leftResultReady.Set(); // 대기 중인 스레드 해제 _leftResultReady.Set(); // 대기 중인 스레드 해제
_rightResultReady.Set(); _rightResultReady.Set();
_leftStartEvent.Set(); // 시작 대기 해제
_rightStartEvent.Set();
_dioBoard.InputChanged -= OnDioInputChanged; _dioBoard.InputChanged -= OnDioInputChanged;
_sentinelService.OnFinalResultParsed -= OnSentinelFinalResult; _sentinelService.OnFinalResultParsed -= OnSentinelFinalResult;
_leftResultReady?.Dispose(); _leftResultReady?.Dispose();
_rightResultReady?.Dispose(); _rightResultReady?.Dispose();
_leftStartEvent?.Dispose();
_rightStartEvent?.Dispose();
Console.WriteLine("[TestProcess] Disposed."); Console.WriteLine("[TestProcess] Disposed.");
} }

36
leak_test_project/Utils/ConfigHelper.cs

@ -47,9 +47,21 @@ namespace leak_test_project.Utils
try try
{ {
XmlSerializer serializer = new XmlSerializer(typeof(AppConfig)); 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); ConfigChanged?.Invoke(null, EventArgs.Empty);
} }
@ -143,22 +155,28 @@ namespace leak_test_project.Utils
using (var sw = new StreamWriter(filePath)) using (var sw = new StreamWriter(filePath))
{ {
sw.WriteLine("; =====================================================================");
sw.WriteLine("; 하드웨어 디지털 입출력 설정 파일 (DioConfig.ini)");
sw.WriteLine("; =====================================================================");
sw.WriteLine();
sw.WriteLine("[DIO]"); sw.WriteLine("[DIO]");
sw.WriteLine($"CompanyName={CompanyName}"); sw.WriteLine($"CompanyName={CompanyName} ; 입출력 보드 제조사명 (예: ADLink)");
sw.WriteLine("BoardCnt=1"); sw.WriteLine("BoardCnt=1 ; 설치된 총 입출력 보드 개수");
sw.WriteLine("LogEnabled=False"); sw.WriteLine("LogEnabled=False ; DIO 동작 로그 기록 여부 (True / False)");
sw.WriteLine(); sw.WriteLine();
sw.WriteLine("[BOARD_1]"); sw.WriteLine("[BOARD_1]");
sw.WriteLine($"BoardType={BoardType}"); sw.WriteLine($"BoardType={BoardType} ; ADLINK DIO 보드 하드웨어 모델명");
sw.WriteLine($"BoardNumber={BoardNumber}"); sw.WriteLine($"BoardNumber={BoardNumber} ; 시스템 내 보드 식별 번호 (기본 0)");
sw.WriteLine($"InPortCnt={InputPoints.Count}"); sw.WriteLine($"InPortCnt={InputPoints.Count} ; 입력에 사용할 접점 포트 개수");
sw.WriteLine($"OutPortCnt={OutputPoints.Count}"); sw.WriteLine($"OutPortCnt={OutputPoints.Count} ; 출력에 사용할 접점 포트 개수");
sw.WriteLine(); sw.WriteLine();
sw.WriteLine("[BOARD_1_IN]"); sw.WriteLine("[BOARD_1_IN]");
sw.WriteLine("; [핀 번호]=[기능 이름],[신호 레벨],[설명]");
for (int i = 0; i < InputPoints.Count; i++) 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($"{i}={InputPoints[i].Name},1,{(string.IsNullOrEmpty(InputPoints[i].Description) ? "" : InputPoints[i].Description)}");
sw.WriteLine(); sw.WriteLine();
sw.WriteLine("[BOARD_1_OUT]"); sw.WriteLine("[BOARD_1_OUT]");
sw.WriteLine("; [핀 번호]=[기능 이름],[신호 레벨],[설명]");
for (int i = 0; i < OutputPoints.Count; i++) for (int i = 0; i < OutputPoints.Count; i++)
sw.WriteLine($"{i}={OutputPoints[i].Name},1,{(string.IsNullOrEmpty(OutputPoints[i].Description) ? "" : OutputPoints[i].Description)}"); sw.WriteLine($"{i}={OutputPoints[i].Name},1,{(string.IsNullOrEmpty(OutputPoints[i].Description) ? "" : OutputPoints[i].Description)}");
} }

23
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);
}
}
}

321
leak_test_project/Utils/LogUtils.cs

@ -4,7 +4,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Windows;
using leak_test_project.Models; using leak_test_project.Models;
namespace leak_test_project.Utils namespace leak_test_project.Utils
@ -18,7 +17,7 @@ namespace leak_test_project.Utils
private static readonly object _lock = new object(); private static readonly object _lock = new object();
/// <summary> /// <summary>
/// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함 /// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함 (16컬럼)
/// </summary> /// </summary>
public static void LogInspectData(InspectData data) public static void LogInspectData(InspectData data)
{ {
@ -34,14 +33,22 @@ namespace leak_test_project.Utils
bool isNewFile = !File.Exists(filePath); bool isNewFile = !File.Exists(filePath);
// CSV 헤더: Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment // 신규 CSV 헤더: DB 스키마 16개 컬럼과 일치
if (isNewFile) 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); 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}"; $"{Esc(data.SpecUL)},{Esc(data.SpecLL)},{Esc(data.MeasuredValue)},{Esc(data.Judgment)}{Environment.NewLine}";
File.AppendAllText(filePath, csvLine, Encoding.UTF8); 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"); private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
public static List<InspectData> ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "") public static List<InspectData> ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "", string opFilter = "", string lotFilter = "", string modelFilter = "")
{
var results = new List<InspectData>();
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<InspectData> ParseLogsFromCsv(DateTime start, DateTime end, string judgmentFilter, string serialFilter, string opFilter, string lotFilter, string modelFilter)
{ {
var results = new List<InspectData>(); var results = new List<InspectData>();
if (!Directory.Exists(LogDirectory)) if (!Directory.Exists(LogDirectory))
{ {
// 디렉토리가 없으면 빈 리스트 반환
return results; return results;
} }
// 날짜 범위 내의 모든 로그 파일 찾기 (.csv) // 파일명의 yyyy-MM-dd 날짜 파싱 시 시스템 로캘 영향을 막기 위해 TryParseExact 사용
var files = Directory.GetFiles(LogDirectory, "*.csv") var files = Directory.GetFiles(LogDirectory, "*.csv")
.Where(f => { .Where(f => {
string fileName = Path.GetFileNameWithoutExtension(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; return fileDate.Date >= start.Date && fileDate.Date <= end.Date;
} }
@ -118,11 +269,21 @@ namespace leak_test_project.Utils
{ {
try try
{ {
var lines = File.ReadAllLines(file); // 엑셀 등으로 파일이 열려 파일 락(Lock)이 걸려있어도 안전하게 읽기 위해 FileShare.ReadWrite 적용
if (lines.Length <= 1) continue; // Header only or empty var linesList = new List<string>();
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++) for (int i = 1; i < lines.Length; i++)
{ {
string line = lines[i]; string line = lines[i];
@ -130,23 +291,104 @@ namespace leak_test_project.Utils
var parts = line.Split(','); var parts = line.Split(',');
if (parts.Length < 8) continue; if (parts.Length < 8) continue;
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 date = parts[0].Trim();
string time = parts[1].Trim(); string time = parts[1].Trim();
// 하위호환성 제거: 신형 8컬럼 순서(Date,Time,ID,Channel,SpecUL,SpecLL,Value,Judgment)로 다이렉트 파싱
string id = parts[2].Trim(); string id = parts[2].Trim();
string channel = parts[3].Trim(); string channel = parts[3].Trim();
string specUl = parts[4].Trim(); string specUl = parts[4].Trim();
string specLl = parts[5].Trim(); string specLl = parts[5].Trim();
string val = parts[6].Trim(); string val = parts[6].Trim();
string judg = parts[7].Trim(); string judg = parts[7].Trim();
string retest = "N";
// 필터 적용 // 필터 적용 (구형 포맷에는 Operator, LotNo, Model이 없으므로 필터값이 있는 경우 결과에서 제외)
if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue; if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue;
if (!string.IsNullOrEmpty(serialFilter) && !id.Contains(serialFilter)) 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;
}
results.Add(new InspectData data = new InspectData
{ {
InspectDate = date, InspectDate = date,
InspectTime = time, InspectTime = time,
@ -156,17 +398,50 @@ namespace leak_test_project.Utils
Judgment = judg, Judgment = judg,
SpecUL = specUl, SpecUL = specUl,
SpecLL = specLl, SpecLL = specLl,
Retest = retest Retest = "N",
});
// 정합성을 위해 호환 필드 매핑
IcSn = id,
PcbBarcode = id,
ProductionDate = parsedProdDate
};
}
if (data != null)
{
results.Add(data);
}
} }
} }
catch (Exception ex) 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<InspectData>();
var seenIds = new HashSet<string>(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) catch (Exception ex)
{ {
MessageBox.Show($"CSV 저장 실패: {ex.Message}"); System.Diagnostics.Debug.WriteLine($"[CsvExporter ExportToCsv Error] {ex.Message}");
return false; return false;
} }
} }

31
leak_test_project/ViewModels/DataViewModel.cs

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using Microsoft.Win32; using Microsoft.Win32;
using leak_test_project.Models; using leak_test_project.Models;
@ -27,6 +26,15 @@ namespace leak_test_project.ViewModels
private string _idFilter = ""; private string _idFilter = "";
public string IdFilter { get => _idFilter; set => SetProperty(ref _idFilter, value); } 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<InspectData> _searchResults = new List<InspectData>(); private List<InspectData> _searchResults = new List<InspectData>();
public List<InspectData> SearchResults { get => _searchResults; set => SetProperty(ref _searchResults, value); } public List<InspectData> SearchResults { get => _searchResults; set => SetProperty(ref _searchResults, value); }
@ -37,14 +45,17 @@ namespace leak_test_project.ViewModels
private int _resultCount = 0; private int _resultCount = 0;
public int ResultCount { get => _resultCount; set => SetProperty(ref _resultCount, value); } public int ResultCount { get => _resultCount; set => SetProperty(ref _resultCount, value); }
private readonly IDialogService _dialogService;
public ICommand SearchCommand { get; } public ICommand SearchCommand { get; }
public ICommand SaveCsvCommand { get; } public ICommand SaveCsvCommand { get; }
/// <summary> Close 버튼 시 홈으로 돌아가는 커맨드 </summary> /// <summary> Close 버튼 시 홈으로 돌아가는 커맨드 </summary>
public ICommand CloseCommand { get; } public ICommand CloseCommand { get; }
public DataViewModel(Action navigateHome) public DataViewModel(Action navigateHome, IDialogService dialogService = null)
{ {
_dialogService = dialogService ?? new DefaultDialogService();
SearchCommand = new RelayCommand(o => ExecuteSearch()); SearchCommand = new RelayCommand(o => ExecuteSearch());
SaveCsvCommand = new RelayCommand(o => ExecuteSaveCsv()); SaveCsvCommand = new RelayCommand(o => ExecuteSaveCsv());
CloseCommand = new RelayCommand(o => navigateHome?.Invoke()); CloseCommand = new RelayCommand(o => navigateHome?.Invoke());
@ -74,7 +85,15 @@ namespace leak_test_project.ViewModels
? judgmentOptions[JudgmentIndex] ? 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; SearchResults = results;
ResultCount = results.Count; ResultCount = results.Count;
@ -82,7 +101,7 @@ namespace leak_test_project.ViewModels
if (results.Count == 0) if (results.Count == 0)
{ {
MessageBox.Show("해당 조건의 데이터가 없습니다."); _dialogService.ShowWarning("해당 조건의 데이터가 없습니다.");
} }
} }
@ -92,7 +111,7 @@ namespace leak_test_project.ViewModels
// 빈 행이 포함되지 않은 원본 검색 결과(SearchResults)만 저장 // 빈 행이 포함되지 않은 원본 검색 결과(SearchResults)만 저장
if (SearchResults == null || SearchResults.Count == 0) if (SearchResults == null || SearchResults.Count == 0)
{ {
MessageBox.Show("저장할 데이터가 없습니다."); _dialogService.ShowWarning("저장할 데이터가 없습니다.");
return; return;
} }
@ -106,7 +125,7 @@ namespace leak_test_project.ViewModels
{ {
if (CsvExporter.ExportToCsv(SearchResults, saveFileDialog.FileName)) if (CsvExporter.ExportToCsv(SearchResults, saveFileDialog.FileName))
{ {
MessageBox.Show("CSV 파일로 저장되었습니다."); _dialogService.ShowMessage("CSV 파일로 저장되었습니다.");
} }
} }
} }

14
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; _dioBoard = dioBoard;
_dispatcher = Dispatcher.CurrentDispatcher; _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(() => _testProcess.ProgressChanged += (s, e) => _dispatcher.Invoke(() =>
{ {
@ -332,11 +335,7 @@ namespace leak_test_project.ViewModels
if (e.TestIndex == 0) LeftError = msg; if (e.TestIndex == 0) LeftError = msg;
else RightError = msg; else RightError = msg;
MessageBox.Show( _dialogService.ShowWarning("프로그램 스팩과 센서의 스팩이 서로 맞지 않습니다.", "SPEC 교차 검증 경고");
"프로그램 스팩과 센서의 스팩이 서로 맞지 않습니다.",
"SPEC 교차 검증 경고",
MessageBoxButton.OK,
MessageBoxImage.Warning);
} }
}); });
@ -425,7 +424,6 @@ namespace leak_test_project.ViewModels
{ {
ConfigManager.ConfigChanged -= OnConfigChanged; ConfigManager.ConfigChanged -= OnConfigChanged;
CleanupAll(); CleanupAll();
_dioBoard?.Dispose();
} }
} }
} }

70
leak_test_project/Views/DataView.xaml

@ -88,8 +88,10 @@
<TextBlock Text="INSPECT DATA LIST (측정 기록 조회)" Foreground="White" FontWeight="SemiBold" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18"/> <TextBlock Text="INSPECT DATA LIST (측정 기록 조회)" Foreground="White" FontWeight="SemiBold" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="18"/>
</Border> </Border>
<!-- Filter Area (Centered) --> <!-- Filter Area (2-Row Layout) -->
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,10"> <StackPanel Grid.Row="1" Orientation="Vertical" HorizontalAlignment="Center" Margin="0,10">
<!-- Row 1: Common Basic Filters -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,8">
<!-- 기간 --> <!-- 기간 -->
<StackPanel Width="320"> <StackPanel Width="320">
<Border Style="{StaticResource FilterHeaderBorder}"> <Border Style="{StaticResource FilterHeaderBorder}">
@ -105,26 +107,59 @@
</StackPanel> </StackPanel>
<!-- 판정 --> <!-- 판정 -->
<StackPanel Width="120" Margin="10,0,0,0"> <StackPanel Width="150" Margin="10,0,0,0">
<Border Style="{StaticResource FilterHeaderBorder}"> <Border Style="{StaticResource FilterHeaderBorder}">
<TextBlock Text="판정 결과" Style="{StaticResource FilterHeaderLabel}"/> <TextBlock Text="판정 결과" Style="{StaticResource FilterHeaderLabel}"/>
</Border> </Border>
<Border Style="{StaticResource FilterContentBorder}"> <Border Style="{StaticResource FilterContentBorder}">
<ComboBox SelectedIndex="{Binding JudgmentIndex}" Width="100" Height="30" VerticalAlignment="Center" FontSize="13"> <ComboBox SelectedIndex="{Binding JudgmentIndex}" Width="130" Height="30" VerticalAlignment="Center" FontSize="13">
<ComboBoxItem Content="[전체]"/> <ComboBoxItem Content="[전체]"/>
<ComboBoxItem Content="OK"/> <ComboBoxItem Content="OK"/>
<ComboBoxItem Content="NG"/> <ComboBoxItem Content="NG"/>
</ComboBox> </ComboBox>
</Border> </Border>
</StackPanel> </StackPanel>
</StackPanel>
<!-- Row 2: Detail Database Columns Filters -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<!-- ID 검색 --> <!-- ID 검색 -->
<StackPanel Width="280" Margin="10,0,0,0"> <StackPanel Width="170">
<Border Style="{StaticResource FilterHeaderBorder}"> <Border Style="{StaticResource FilterHeaderBorder}">
<TextBlock Text="ID 검색" Style="{StaticResource FilterHeaderLabel}"/> <TextBlock Text="ID 검색" Style="{StaticResource FilterHeaderLabel}"/>
</Border> </Border>
<Border Style="{StaticResource FilterContentBorder}"> <Border Style="{StaticResource FilterContentBorder}">
<TextBox Text="{Binding IdFilter, UpdateSourceTrigger=PropertyChanged}" Width="250" Height="30" VerticalAlignment="Center" FontSize="14" FontWeight="SemiBold" Padding="5,0"/> <TextBox Text="{Binding IdFilter, UpdateSourceTrigger=PropertyChanged}" Width="150" Height="30" VerticalAlignment="Center" FontSize="13" FontWeight="SemiBold" Padding="5,0"/>
</Border>
</StackPanel>
<!-- 작업자 검색 -->
<StackPanel Width="140" Margin="10,0,0,0">
<Border Style="{StaticResource FilterHeaderBorder}">
<TextBlock Text="작업자" Style="{StaticResource FilterHeaderLabel}"/>
</Border>
<Border Style="{StaticResource FilterContentBorder}">
<TextBox Text="{Binding OperatorFilter, UpdateSourceTrigger=PropertyChanged}" Width="120" Height="30" VerticalAlignment="Center" FontSize="13" FontWeight="SemiBold" Padding="5,0"/>
</Border>
</StackPanel>
<!-- Lot No 검색 -->
<StackPanel Width="140" Margin="10,0,0,0">
<Border Style="{StaticResource FilterHeaderBorder}">
<TextBlock Text="Lot No" Style="{StaticResource FilterHeaderLabel}"/>
</Border>
<Border Style="{StaticResource FilterContentBorder}">
<TextBox Text="{Binding LotNoFilter, UpdateSourceTrigger=PropertyChanged}" Width="120" Height="30" VerticalAlignment="Center" FontSize="13" FontWeight="SemiBold" Padding="5,0"/>
</Border>
</StackPanel>
<!-- 모델 검색 -->
<StackPanel Width="140" Margin="10,0,0,0">
<Border Style="{StaticResource FilterHeaderBorder}">
<TextBlock Text="모델" Style="{StaticResource FilterHeaderLabel}"/>
</Border>
<Border Style="{StaticResource FilterContentBorder}">
<TextBox Text="{Binding ModelFilter, UpdateSourceTrigger=PropertyChanged}" Width="120" Height="30" VerticalAlignment="Center" FontSize="13" FontWeight="SemiBold" Padding="5,0"/>
</Border> </Border>
</StackPanel> </StackPanel>
@ -134,6 +169,7 @@
<Button Content="CSV" Style="{StaticResource ActionButton}" Command="{Binding SaveCsvCommand}" Background="#D4EFDF" BorderBrush="#27AE60" Click="Button_Click"/> <Button Content="CSV" Style="{StaticResource ActionButton}" Command="{Binding SaveCsvCommand}" Background="#D4EFDF" BorderBrush="#27AE60" Click="Button_Click"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel>
<!-- Main Grid --> <!-- Main Grid -->
<Border Grid.Row="2" BorderBrush="#BDC3C7" BorderThickness="1.2" Background="White"> <Border Grid.Row="2" BorderBrush="#BDC3C7" BorderThickness="1.2" Background="White">
@ -143,13 +179,22 @@
CanUserSortColumns="True" CanUserReorderColumns="False" CanUserSortColumns="True" CanUserReorderColumns="False"
BorderThickness="0" SelectionMode="Extended" SelectionChanged="DataGrid_SelectionChanged"> BorderThickness="0" SelectionMode="Extended" SelectionChanged="DataGrid_SelectionChanged">
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="날짜" Binding="{Binding InspectDate}" Width="130"/> <DataGridTextColumn Header="검사일자" Binding="{Binding InspectDate}" Width="110"/>
<DataGridTextColumn Header="시간" Binding="{Binding InspectTime}" Width="100"/> <DataGridTextColumn Header="검사시간" Binding="{Binding InspectTime}" Width="90"/>
<DataGridTextColumn Header="ID" Binding="{Binding ProductId}" Width="250"/> <DataGridTextColumn Header="IC SN (제품ID)" Binding="{Binding ProductId}" Width="150"/>
<DataGridTextColumn Header="좌/우" Binding="{Binding Channel}" Width="70"/> <DataGridTextColumn Header="PCB 바코드" Binding="{Binding PcbBarcode}" Width="150"/>
<DataGridTextColumn Header="메이커" Binding="{Binding Maker}" Width="90"/>
<DataGridTextColumn Header="모델" Binding="{Binding Model}" Width="90"/>
<DataGridTextColumn Header="변형 1" Binding="{Binding Variant1}" Width="90"/>
<DataGridTextColumn Header="변형 2" Binding="{Binding Variant2}" Width="90"/>
<DataGridTextColumn Header="작업자" Binding="{Binding Operator}" Width="90"/>
<DataGridTextColumn Header="작업라인" Binding="{Binding Line}" Width="80"/>
<DataGridTextColumn Header="Lot No" Binding="{Binding LotNo}" Width="95"/>
<DataGridTextColumn Header="지그 번호" Binding="{Binding JigNo}" Width="80"/>
<DataGridTextColumn Header="검사채널" Binding="{Binding Channel}" Width="80"/>
<DataGridTextColumn Header="SPEC UL" Binding="{Binding SpecUL}" Width="80"/> <DataGridTextColumn Header="SPEC UL" Binding="{Binding SpecUL}" Width="80"/>
<DataGridTextColumn Header="SPEC LL" Binding="{Binding SpecLL}" Width="80"/> <DataGridTextColumn Header="SPEC LL" Binding="{Binding SpecLL}" Width="80"/>
<DataGridTextColumn Header="측정값" Binding="{Binding MeasuredValue}" Width="100"> <DataGridTextColumn Header="측정값" Binding="{Binding MeasuredValue}" Width="100">
<DataGridTextColumn.ElementStyle> <DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock"> <Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right"/> <Setter Property="HorizontalAlignment" Value="Right"/>
@ -158,7 +203,7 @@
</Style> </Style>
</DataGridTextColumn.ElementStyle> </DataGridTextColumn.ElementStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="판정" Binding="{Binding Judgment}" Width="80"> <DataGridTextColumn Header="최종판정" Binding="{Binding Judgment}" Width="80">
<DataGridTextColumn.ElementStyle> <DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock"> <Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="HorizontalAlignment" Value="Center"/>
@ -174,7 +219,6 @@
</Style> </Style>
</DataGridTextColumn.ElementStyle> </DataGridTextColumn.ElementStyle>
</DataGridTextColumn> </DataGridTextColumn>
<DataGridTextColumn Header="비고" Width="*" MinWidth="300"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
</Border> </Border>

8
leak_test_project/Views/StartupLoginWindow.xaml

@ -397,7 +397,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="MakerTextBox" <TextBox x:Name="MakerTextBox"
Grid.Row="1" Grid.Row="1"
Text="MOBI"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -415,7 +414,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="ModelTextBox" <TextBox x:Name="ModelTextBox"
Grid.Row="1" Grid.Row="1"
Text="MOBI-EV-01"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -433,7 +431,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="Variant1TextBox" <TextBox x:Name="Variant1TextBox"
Grid.Row="1" Grid.Row="1"
Text="V1-01"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -451,7 +448,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="Variant2TextBox" <TextBox x:Name="Variant2TextBox"
Grid.Row="1" Grid.Row="1"
Text="V2-01"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -480,7 +476,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="OperatorTextBox" <TextBox x:Name="OperatorTextBox"
Grid.Row="1" Grid.Row="1"
Text="test"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -527,7 +522,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="LineNoTextBox" <TextBox x:Name="LineNoTextBox"
Grid.Row="1" Grid.Row="1"
Text="LINE-3"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -544,7 +538,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="LotNoTextBox" <TextBox x:Name="LotNoTextBox"
Grid.Row="1" Grid.Row="1"
Text="LOT-20260605"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>
@ -561,7 +554,6 @@
Style="{StaticResource FieldLabelStyle}"/> Style="{StaticResource FieldLabelStyle}"/>
<TextBox x:Name="JigNoTextBox" <TextBox x:Name="JigNoTextBox"
Grid.Row="1" Grid.Row="1"
Text="JIG-12"
Style="{StaticResource InputStyle}"/> Style="{StaticResource InputStyle}"/>
</Grid> </Grid>
</Border> </Border>

2
leak_test_project/Views/StartupLoginWindow.xaml.cs

@ -328,7 +328,7 @@ namespace Housing.Login
return Path.Combine( return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"Housing", "Housing",
"LoginHistory.json"); "LoginHistory.xml");
} }
} }
} }

24
leak_test_project/bin/Debug/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

BIN
leak_test_project/bin/Debug/app.publish/leak_test_project.exe

Binary file not shown.

2
leak_test_project/bin/Debug/leak_test_project.application

@ -14,7 +14,7 @@
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>l3ZX9B+uam5XK1m0sy2nwxnA38smbnh5KmVt6jFffEY=</dsig:DigestValue> <dsig:DigestValue>oBxYPjguQtv1EuRHadukWOmGPPqWPod5v+TZToduxNk=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/bin/Debug/leak_test_project.exe

Binary file not shown.

4
leak_test_project/bin/Debug/leak_test_project.exe.manifest

@ -42,14 +42,14 @@
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
<dependency> <dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="219136"> <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="237568">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" /> <assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash> <hash>
<dsig:Transforms> <dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>K7q6P0n/e7IBxUL2PiojluRdc1gfMK7T7k4cyvQZhss=</dsig:DigestValue> <dsig:DigestValue>NEvi6EY1Ysg2m7d04Ix80K81U0DF+ZZKnv+9QRT3SMc=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/bin/Debug/leak_test_project.pdb

Binary file not shown.

24
leak_test_project/bin/Release/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

2
leak_test_project/bin/Release/Logs/2026-06-18_system.log

@ -0,0 +1,2 @@
[11:13:29.052] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o
[11:13:30.394] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801

8
leak_test_project/bin/Release/Logs/2026-06-25_system.log

@ -0,0 +1,8 @@
[10:17:36.444] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o
[10:17:37.692] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801
[10:17:39.458] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o
[10:17:40.707] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801
[10:17:43.159] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o
[10:17:44.414] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801
[10:17:46.175] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o
[10:17:47.441] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801

BIN
leak_test_project/bin/Release/app.publish/leak_test_project.exe

Binary file not shown.

3
leak_test_project/bin/Release/config.xml

@ -6,6 +6,5 @@
<Board4251BaudRate>115200</Board4251BaudRate> <Board4251BaudRate>115200</Board4251BaudRate>
<Board4251Timeout>5000</Board4251Timeout> <Board4251Timeout>5000</Board4251Timeout>
<SpecUL>0.1</SpecUL> <SpecUL>0.1</SpecUL>
<SpecLL>-0.2</SpecLL> <SpecLL>-0.1</SpecLL>
<DbConnectionString>Server=192.168.0.20;Database=NE1aW_PT_SENSOR;User Id=sa;Password=sa1234;</DbConnectionString>
</AppConfig> </AppConfig>

2
leak_test_project/bin/Release/leak_test_project.application

@ -14,7 +14,7 @@
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>qNbiAc1kf0eiU+DGDJlSHaQrr7D5MVNH8vmLeW563VI=</dsig:DigestValue> <dsig:DigestValue>n7VmvcxpOf8Y+LRFjH5TmdoI+vXXgYov1j6fqW/Nbxs=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/bin/Release/leak_test_project.exe

Binary file not shown.

4
leak_test_project/bin/Release/leak_test_project.exe.manifest

@ -42,14 +42,14 @@
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
<dependency> <dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="190464"> <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="210432">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" /> <assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash> <hash>
<dsig:Transforms> <dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>oU1Vb+qDbIJPdoRRPncFp5ECBdIWWwNNrCazZR4lxdI=</dsig:DigestValue> <dsig:DigestValue>bIiBUDqHxOgJSZaDgyYguxKJoGO3oFxaqRp+FVa4a/A=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/bin/Release/leak_test_project.pdb

Binary file not shown.

12
leak_test_project/leak_test_project.csproj

@ -94,7 +94,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Views\DataView.xaml"> <Page Include="Views\DataView.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@ -129,15 +128,18 @@
<Compile Include="Services\DioBoardFactory.cs" /> <Compile Include="Services\DioBoardFactory.cs" />
<Compile Include="Services\TestProcessService.cs" /> <Compile Include="Services\TestProcessService.cs" />
<Compile Include="Services\EolDatabaseService.cs" /> <Compile Include="Services\EolDatabaseService.cs" />
<Compile Include="Services\LoginServicesStub.cs" /> <Compile Include="Services\DatabaseSettings.cs" />
<Compile Include="Services\LoginSettings.cs" />
<Compile Include="Services\LoginHistoryStore.cs" />
<Compile Include="Services\LoginAccountRepository.cs" />
<Compile Include="Utils\SentinelParser.cs" /> <Compile Include="Utils\SentinelParser.cs" />
<Compile Include="Utils\LogUtils.cs" /> <Compile Include="Utils\LogUtils.cs" />
<Compile Include="Utils\ConfigHelper.cs" /> <Compile Include="Utils\ConfigHelper.cs" />
<Compile Include="Utils\DialogService.cs" />
<Compile Include="Models\ProjectModels.cs" /> <Compile Include="Models\ProjectModels.cs" />
<Compile Include="Views\HomeView.xaml.cs"> <Compile Include="Views\HomeView.xaml.cs">
<DependentUpon>HomeView.xaml</DependentUpon> <DependentUpon>HomeView.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Views\DataView.xaml.cs"> <Compile Include="Views\DataView.xaml.cs">
<DependentUpon>DataView.xaml</DependentUpon> <DependentUpon>DataView.xaml</DependentUpon>
</Compile> </Compile>
@ -154,7 +156,6 @@
<Compile Include="ViewModels\Core\RelayCommand.cs" /> <Compile Include="ViewModels\Core\RelayCommand.cs" />
<Compile Include="ViewModels\DataViewModel.cs" /> <Compile Include="ViewModels\DataViewModel.cs" />
<Compile Include="ViewModels\HomeViewModel.cs" /> <Compile Include="ViewModels\HomeViewModel.cs" />
<Compile Include="ViewModels\MainViewModel.cs" /> <Compile Include="ViewModels\MainViewModel.cs" />
<Compile Include="ViewModels\ParametersViewModel.cs" /> <Compile Include="ViewModels\ParametersViewModel.cs" />
<Compile Include="MainWindow.xaml.cs"> <Compile Include="MainWindow.xaml.cs">
@ -189,6 +190,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="App.config" /> <None Include="App.config" />
<None Include="Database.ini">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2"> <BootstrapperPackage Include=".NETFramework,Version=v4.7.2">

BIN
leak_test_project/obj/Debug/App.baml

Binary file not shown.

BIN
leak_test_project/obj/Debug/MainWindow.baml

Binary file not shown.

BIN
leak_test_project/obj/Debug/Views/CommunicationWindow.baml

Binary file not shown.

BIN
leak_test_project/obj/Debug/Views/DataView.baml

Binary file not shown.

6
leak_test_project/obj/Debug/Views/DataView.g.cs

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\Views\DataView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "CF3D9FA0B3A93AD2D6C065F35DF39EBFC783D422DB5170F1B6A1280CD7D48859" #pragma checksum "..\..\..\Views\DataView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "E806D581E87844448860858BA46A605448A6BF6EEC5FF26FF0D442F616E4D3C7"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다. // 이 코드는 도구를 사용하여 생성되었습니다.
@ -71,7 +71,7 @@ namespace leak_test_project.Views {
{ {
case 1: case 1:
#line 134 "..\..\..\Views\DataView.xaml" #line 169 "..\..\..\Views\DataView.xaml"
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.Button_Click); ((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.Button_Click);
#line default #line default
@ -79,7 +79,7 @@ namespace leak_test_project.Views {
return; return;
case 2: case 2:
#line 144 "..\..\..\Views\DataView.xaml" #line 180 "..\..\..\Views\DataView.xaml"
((System.Windows.Controls.DataGrid)(target)).SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.DataGrid_SelectionChanged); ((System.Windows.Controls.DataGrid)(target)).SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.DataGrid_SelectionChanged);
#line default #line default

BIN
leak_test_project/obj/Debug/Views/HomeView.baml

Binary file not shown.

BIN
leak_test_project/obj/Debug/Views/ParametersWindow.baml

Binary file not shown.

BIN
leak_test_project/obj/Debug/Views/StartupLoginWindow.baml

Binary file not shown.

22
leak_test_project/obj/Debug/Views/StartupLoginWindow.g.cs

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\Views\StartupLoginWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "10236E62B98A3B25C61EAC333EF6CCD078A69767002979415693621924B5E4AC" #pragma checksum "..\..\..\Views\StartupLoginWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "E10917AAAC5C59A6940A4937C1165BBFED3D9CE02854042592965F3FCDF2EE1F"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다. // 이 코드는 도구를 사용하여 생성되었습니다.
@ -48,7 +48,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 416 "..\..\..\Views\StartupLoginWindow.xaml" #line 415 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox ModelTextBox; internal System.Windows.Controls.TextBox ModelTextBox;
@ -56,7 +56,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 434 "..\..\..\Views\StartupLoginWindow.xaml" #line 432 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox Variant1TextBox; internal System.Windows.Controls.TextBox Variant1TextBox;
@ -64,7 +64,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 452 "..\..\..\Views\StartupLoginWindow.xaml" #line 449 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox Variant2TextBox; internal System.Windows.Controls.TextBox Variant2TextBox;
@ -72,7 +72,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 481 "..\..\..\Views\StartupLoginWindow.xaml" #line 477 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox OperatorTextBox; internal System.Windows.Controls.TextBox OperatorTextBox;
@ -80,7 +80,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 498 "..\..\..\Views\StartupLoginWindow.xaml" #line 493 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.PasswordBox PasswordBox; internal System.Windows.Controls.PasswordBox PasswordBox;
@ -88,7 +88,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 528 "..\..\..\Views\StartupLoginWindow.xaml" #line 523 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox LineNoTextBox; internal System.Windows.Controls.TextBox LineNoTextBox;
@ -96,7 +96,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 545 "..\..\..\Views\StartupLoginWindow.xaml" #line 539 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox LotNoTextBox; internal System.Windows.Controls.TextBox LotNoTextBox;
@ -104,7 +104,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 562 "..\..\..\Views\StartupLoginWindow.xaml" #line 555 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox JigNoTextBox; internal System.Windows.Controls.TextBox JigNoTextBox;
@ -112,7 +112,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 570 "..\..\..\Views\StartupLoginWindow.xaml" #line 562 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.Button LoginButton; internal System.Windows.Controls.Button LoginButton;
@ -179,7 +179,7 @@ namespace Housing.Login {
case 10: case 10:
this.LoginButton = ((System.Windows.Controls.Button)(target)); this.LoginButton = ((System.Windows.Controls.Button)(target));
#line 574 "..\..\..\Views\StartupLoginWindow.xaml" #line 566 "..\..\..\Views\StartupLoginWindow.xaml"
this.LoginButton.Click += new System.Windows.RoutedEventHandler(this.LogInButton_Click); this.LoginButton.Click += new System.Windows.RoutedEventHandler(this.LogInButton_Click);
#line default #line default

2
leak_test_project/obj/Debug/leak_test_project.application

@ -14,7 +14,7 @@
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>l3ZX9B+uam5XK1m0sy2nwxnA38smbnh5KmVt6jFffEY=</dsig:DigestValue> <dsig:DigestValue>oBxYPjguQtv1EuRHadukWOmGPPqWPod5v+TZToduxNk=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

2
leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache

@ -1 +1 @@
f74477eeb98a106556f1b65e440f8cfa8362ac43ae60c03174d4f2bb878b7a02 628713ffbc5f55cd805898b1d31f7980abb2a5a8c3c1aad9e2f31b3c27ae4897

1
leak_test_project/obj/Debug/leak_test_project.csproj.FileListAbsolute.txt

@ -30,3 +30,4 @@ C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\le
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\leak_test_project.application C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\leak_test_project.application
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\leak_test_project.exe C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\leak_test_project.exe
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\leak_test_project.pdb C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Debug\leak_test_project.pdb
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\bin\Debug\Database.ini

BIN
leak_test_project/obj/Debug/leak_test_project.exe

Binary file not shown.

4
leak_test_project/obj/Debug/leak_test_project.exe.manifest

@ -42,14 +42,14 @@
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
<dependency> <dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="219136"> <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="237568">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" /> <assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash> <hash>
<dsig:Transforms> <dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>K7q6P0n/e7IBxUL2PiojluRdc1gfMK7T7k4cyvQZhss=</dsig:DigestValue> <dsig:DigestValue>NEvi6EY1Ysg2m7d04Ix80K81U0DF+ZZKnv+9QRT3SMc=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/obj/Debug/leak_test_project.g.resources

Binary file not shown.

BIN
leak_test_project/obj/Debug/leak_test_project.pdb

Binary file not shown.

8
leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache

@ -10,10 +10,10 @@ none
false false
DEBUG;TRACE DEBUG;TRACE
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml
6681427662 6-1954804774
1-698605034 1-1942997596
311518714309 35231504871
14-767506772 14188644636
Views\HomeView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;Views\StartupLoginWindow.xaml;MainWindow.xaml; Views\HomeView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;Views\StartupLoginWindow.xaml;MainWindow.xaml;
False False

BIN
leak_test_project/obj/Release/App.baml

Binary file not shown.

BIN
leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache

Binary file not shown.

62
leak_test_project/obj/Release/GeneratedInternalTypeHelper.g.cs

@ -1,62 +1,2 @@
//------------------------------------------------------------------------------ 
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
// 런타임 버전:4.0.30319.42000
//
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
// 이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
namespace XamlGeneratedNamespace {
/// <summary>
/// GeneratedInternalTypeHelper
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
public sealed class GeneratedInternalTypeHelper : System.Windows.Markup.InternalTypeHelper {
/// <summary>
/// CreateInstance
/// </summary>
protected override object CreateInstance(System.Type type, System.Globalization.CultureInfo culture) {
return System.Activator.CreateInstance(type, ((System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic)
| (System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.CreateInstance)), null, null, culture);
}
/// <summary>
/// GetPropertyValue
/// </summary>
protected override object GetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, System.Globalization.CultureInfo culture) {
return propertyInfo.GetValue(target, System.Reflection.BindingFlags.Default, null, null, culture);
}
/// <summary>
/// SetPropertyValue
/// </summary>
protected override void SetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, object value, System.Globalization.CultureInfo culture) {
propertyInfo.SetValue(target, value, System.Reflection.BindingFlags.Default, null, null, culture);
}
/// <summary>
/// CreateDelegate
/// </summary>
protected override System.Delegate CreateDelegate(System.Type delegateType, object target, string handler) {
return ((System.Delegate)(target.GetType().InvokeMember("_CreateDelegate", (System.Reflection.BindingFlags.InvokeMethod
| (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)), null, target, new object[] {
delegateType,
handler}, null)));
}
/// <summary>
/// AddEventHandler
/// </summary>
protected override void AddEventHandler(System.Reflection.EventInfo eventInfo, object target, System.Delegate handler) {
eventInfo.AddEventHandler(target, handler);
}
}
}

BIN
leak_test_project/obj/Release/MainWindow.baml

Binary file not shown.

BIN
leak_test_project/obj/Release/Views/CommunicationWindow.baml

Binary file not shown.

BIN
leak_test_project/obj/Release/Views/DataView.baml

Binary file not shown.

6
leak_test_project/obj/Release/Views/DataView.g.cs

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\Views\DataView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "CF3D9FA0B3A93AD2D6C065F35DF39EBFC783D422DB5170F1B6A1280CD7D48859" #pragma checksum "..\..\..\Views\DataView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "E806D581E87844448860858BA46A605448A6BF6EEC5FF26FF0D442F616E4D3C7"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다. // 이 코드는 도구를 사용하여 생성되었습니다.
@ -71,7 +71,7 @@ namespace leak_test_project.Views {
{ {
case 1: case 1:
#line 134 "..\..\..\Views\DataView.xaml" #line 169 "..\..\..\Views\DataView.xaml"
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.Button_Click); ((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.Button_Click);
#line default #line default
@ -79,7 +79,7 @@ namespace leak_test_project.Views {
return; return;
case 2: case 2:
#line 144 "..\..\..\Views\DataView.xaml" #line 180 "..\..\..\Views\DataView.xaml"
((System.Windows.Controls.DataGrid)(target)).SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.DataGrid_SelectionChanged); ((System.Windows.Controls.DataGrid)(target)).SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.DataGrid_SelectionChanged);
#line default #line default

6
leak_test_project/obj/Release/Views/DataView.g.i.cs

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\Views\DataView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "CF3D9FA0B3A93AD2D6C065F35DF39EBFC783D422DB5170F1B6A1280CD7D48859" #pragma checksum "..\..\..\Views\DataView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "E806D581E87844448860858BA46A605448A6BF6EEC5FF26FF0D442F616E4D3C7"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다. // 이 코드는 도구를 사용하여 생성되었습니다.
@ -71,7 +71,7 @@ namespace leak_test_project.Views {
{ {
case 1: case 1:
#line 134 "..\..\..\Views\DataView.xaml" #line 169 "..\..\..\Views\DataView.xaml"
((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.Button_Click); ((System.Windows.Controls.Button)(target)).Click += new System.Windows.RoutedEventHandler(this.Button_Click);
#line default #line default
@ -79,7 +79,7 @@ namespace leak_test_project.Views {
return; return;
case 2: case 2:
#line 144 "..\..\..\Views\DataView.xaml" #line 180 "..\..\..\Views\DataView.xaml"
((System.Windows.Controls.DataGrid)(target)).SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.DataGrid_SelectionChanged); ((System.Windows.Controls.DataGrid)(target)).SelectionChanged += new System.Windows.Controls.SelectionChangedEventHandler(this.DataGrid_SelectionChanged);
#line default #line default

BIN
leak_test_project/obj/Release/Views/HomeView.baml

Binary file not shown.

BIN
leak_test_project/obj/Release/Views/ParametersWindow.baml

Binary file not shown.

BIN
leak_test_project/obj/Release/Views/StartupLoginWindow.baml

Binary file not shown.

22
leak_test_project/obj/Release/Views/StartupLoginWindow.g.cs

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\Views\StartupLoginWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "10236E62B98A3B25C61EAC333EF6CCD078A69767002979415693621924B5E4AC" #pragma checksum "..\..\..\Views\StartupLoginWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "E10917AAAC5C59A6940A4937C1165BBFED3D9CE02854042592965F3FCDF2EE1F"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다. // 이 코드는 도구를 사용하여 생성되었습니다.
@ -48,7 +48,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 416 "..\..\..\Views\StartupLoginWindow.xaml" #line 415 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox ModelTextBox; internal System.Windows.Controls.TextBox ModelTextBox;
@ -56,7 +56,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 434 "..\..\..\Views\StartupLoginWindow.xaml" #line 432 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox Variant1TextBox; internal System.Windows.Controls.TextBox Variant1TextBox;
@ -64,7 +64,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 452 "..\..\..\Views\StartupLoginWindow.xaml" #line 449 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox Variant2TextBox; internal System.Windows.Controls.TextBox Variant2TextBox;
@ -72,7 +72,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 481 "..\..\..\Views\StartupLoginWindow.xaml" #line 477 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox OperatorTextBox; internal System.Windows.Controls.TextBox OperatorTextBox;
@ -80,7 +80,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 498 "..\..\..\Views\StartupLoginWindow.xaml" #line 493 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.PasswordBox PasswordBox; internal System.Windows.Controls.PasswordBox PasswordBox;
@ -88,7 +88,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 528 "..\..\..\Views\StartupLoginWindow.xaml" #line 523 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox LineNoTextBox; internal System.Windows.Controls.TextBox LineNoTextBox;
@ -96,7 +96,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 545 "..\..\..\Views\StartupLoginWindow.xaml" #line 539 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox LotNoTextBox; internal System.Windows.Controls.TextBox LotNoTextBox;
@ -104,7 +104,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 562 "..\..\..\Views\StartupLoginWindow.xaml" #line 555 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox JigNoTextBox; internal System.Windows.Controls.TextBox JigNoTextBox;
@ -112,7 +112,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 570 "..\..\..\Views\StartupLoginWindow.xaml" #line 562 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.Button LoginButton; internal System.Windows.Controls.Button LoginButton;
@ -179,7 +179,7 @@ namespace Housing.Login {
case 10: case 10:
this.LoginButton = ((System.Windows.Controls.Button)(target)); this.LoginButton = ((System.Windows.Controls.Button)(target));
#line 574 "..\..\..\Views\StartupLoginWindow.xaml" #line 566 "..\..\..\Views\StartupLoginWindow.xaml"
this.LoginButton.Click += new System.Windows.RoutedEventHandler(this.LogInButton_Click); this.LoginButton.Click += new System.Windows.RoutedEventHandler(this.LogInButton_Click);
#line default #line default

22
leak_test_project/obj/Release/Views/StartupLoginWindow.g.i.cs

@ -1,4 +1,4 @@
#pragma checksum "..\..\..\Views\StartupLoginWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "10236E62B98A3B25C61EAC333EF6CCD078A69767002979415693621924B5E4AC" #pragma checksum "..\..\..\Views\StartupLoginWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "E10917AAAC5C59A6940A4937C1165BBFED3D9CE02854042592965F3FCDF2EE1F"
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다. // 이 코드는 도구를 사용하여 생성되었습니다.
@ -48,7 +48,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 416 "..\..\..\Views\StartupLoginWindow.xaml" #line 415 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox ModelTextBox; internal System.Windows.Controls.TextBox ModelTextBox;
@ -56,7 +56,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 434 "..\..\..\Views\StartupLoginWindow.xaml" #line 432 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox Variant1TextBox; internal System.Windows.Controls.TextBox Variant1TextBox;
@ -64,7 +64,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 452 "..\..\..\Views\StartupLoginWindow.xaml" #line 449 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox Variant2TextBox; internal System.Windows.Controls.TextBox Variant2TextBox;
@ -72,7 +72,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 481 "..\..\..\Views\StartupLoginWindow.xaml" #line 477 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox OperatorTextBox; internal System.Windows.Controls.TextBox OperatorTextBox;
@ -80,7 +80,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 498 "..\..\..\Views\StartupLoginWindow.xaml" #line 493 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.PasswordBox PasswordBox; internal System.Windows.Controls.PasswordBox PasswordBox;
@ -88,7 +88,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 528 "..\..\..\Views\StartupLoginWindow.xaml" #line 523 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox LineNoTextBox; internal System.Windows.Controls.TextBox LineNoTextBox;
@ -96,7 +96,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 545 "..\..\..\Views\StartupLoginWindow.xaml" #line 539 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox LotNoTextBox; internal System.Windows.Controls.TextBox LotNoTextBox;
@ -104,7 +104,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 562 "..\..\..\Views\StartupLoginWindow.xaml" #line 555 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox JigNoTextBox; internal System.Windows.Controls.TextBox JigNoTextBox;
@ -112,7 +112,7 @@ namespace Housing.Login {
#line hidden #line hidden
#line 570 "..\..\..\Views\StartupLoginWindow.xaml" #line 562 "..\..\..\Views\StartupLoginWindow.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.Button LoginButton; internal System.Windows.Controls.Button LoginButton;
@ -179,7 +179,7 @@ namespace Housing.Login {
case 10: case 10:
this.LoginButton = ((System.Windows.Controls.Button)(target)); this.LoginButton = ((System.Windows.Controls.Button)(target));
#line 574 "..\..\..\Views\StartupLoginWindow.xaml" #line 566 "..\..\..\Views\StartupLoginWindow.xaml"
this.LoginButton.Click += new System.Windows.RoutedEventHandler(this.LogInButton_Click); this.LoginButton.Click += new System.Windows.RoutedEventHandler(this.LogInButton_Click);
#line default #line default

2
leak_test_project/obj/Release/leak_test_project.application

@ -14,7 +14,7 @@
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>qNbiAc1kf0eiU+DGDJlSHaQrr7D5MVNH8vmLeW563VI=</dsig:DigestValue> <dsig:DigestValue>n7VmvcxpOf8Y+LRFjH5TmdoI+vXXgYov1j6fqW/Nbxs=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/obj/Release/leak_test_project.csproj.AssemblyReference.cache

Binary file not shown.

2
leak_test_project/obj/Release/leak_test_project.csproj.CoreCompileInputs.cache

@ -1 +1 @@
cbc4c65e0c5c8abdc171ee59e1f11d54d6b7bd62192c87410037a9efab12cf46 8765edc9fc5fcfe1db8d41cec7744abfac5abd652e5521541b6bb33dc8fa7af8

1
leak_test_project/obj/Release/leak_test_project.csproj.FileListAbsolute.txt

@ -30,3 +30,4 @@ C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\leak_test_project.pdb C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\leak_test_project.pdb
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\Views\StartupLoginWindow.baml C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\Views\StartupLoginWindow.baml
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\Views\StartupLoginWindow.g.cs C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\Views\StartupLoginWindow.g.cs
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\bin\Release\Database.ini

BIN
leak_test_project/obj/Release/leak_test_project.exe

Binary file not shown.

4
leak_test_project/obj/Release/leak_test_project.exe.manifest

@ -42,14 +42,14 @@
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>
<dependency> <dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="190464"> <dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="210432">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" /> <assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash> <hash>
<dsig:Transforms> <dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" /> <dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms> </dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /> <dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>oU1Vb+qDbIJPdoRRPncFp5ECBdIWWwNNrCazZR4lxdI=</dsig:DigestValue> <dsig:DigestValue>bIiBUDqHxOgJSZaDgyYguxKJoGO3oFxaqRp+FVa4a/A=</dsig:DigestValue>
</hash> </hash>
</dependentAssembly> </dependentAssembly>
</dependency> </dependency>

BIN
leak_test_project/obj/Release/leak_test_project.g.resources

Binary file not shown.

BIN
leak_test_project/obj/Release/leak_test_project.pdb

Binary file not shown.

8
leak_test_project/obj/Release/leak_test_project_MarkupCompile.cache

@ -10,10 +10,10 @@ none
false false
TRACE TRACE
C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml
6681427662 6-1954804774
1-698605034 1-1942997596
311518714309 35231504871
14-767506772 14188644636
Views\HomeView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;Views\StartupLoginWindow.xaml;MainWindow.xaml; Views\HomeView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;Views\StartupLoginWindow.xaml;MainWindow.xaml;
False False

2
leak_test_project/obj/Release/leak_test_project_MarkupCompile.lref

@ -1,4 +1,4 @@
 C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\obj\Release\GeneratedInternalTypeHelper.g.cs
FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml;; FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml;;
FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\Views\HomeView.xaml;; FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\Views\HomeView.xaml;;
FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\MainWindow.xaml;; FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\MainWindow.xaml;;

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save