diff --git a/leak_test_project.Tests/Services/Board4253ServiceTests.cs b/leak_test_project.Tests/Services/Board4251ServiceTests.cs similarity index 90% rename from leak_test_project.Tests/Services/Board4253ServiceTests.cs rename to leak_test_project.Tests/Services/Board4251ServiceTests.cs index e3e05b2..5c24ba5 100644 --- a/leak_test_project.Tests/Services/Board4253ServiceTests.cs +++ b/leak_test_project.Tests/Services/Board4251ServiceTests.cs @@ -7,7 +7,7 @@ using Xunit; namespace leak_test_project.Tests.Services { - public class Board4253ServiceTests + public class Board4251ServiceTests { [Fact] public async Task CheckStatusAsync_Success_ReturnsTrue() @@ -15,7 +15,7 @@ namespace leak_test_project.Tests.Services // Arrange var mockComm = new Mock(); mockComm.Setup(c => c.IsOpen).Returns(true); - var service = new Board4253Service(mockComm.Object); + var service = new Board4251Service(mockComm.Object); int channel = 1; string expectedCommand = "x00c_001101:owt28006727ea97c7801\r\n"; @@ -41,7 +41,7 @@ namespace leak_test_project.Tests.Services // Arrange var mockComm = new Mock(); mockComm.Setup(c => c.IsOpen).Returns(true); - var service = new Board4253Service(mockComm.Object); + var service = new Board4251Service(mockComm.Object); // Simulate receiving Fail message mockComm.Setup(c => c.Write(It.IsAny())) @@ -64,7 +64,7 @@ namespace leak_test_project.Tests.Services // Arrange var mockComm = new Mock(); mockComm.Setup(c => c.IsOpen).Returns(true); - var service = new Board4253Service(mockComm.Object); + var service = new Board4251Service(mockComm.Object); string expectedId = "ABC1234567890XYZ"; int channel = 2; string expectedCommand = "x00c_002101:ow2800326003e\r\n"; @@ -73,7 +73,7 @@ namespace leak_test_project.Tests.Services mockComm.Setup(c => c.Write(It.Is(s => s == expectedCommand))) .Callback(cmd => { Task.Run(() => { - mockComm.Raise(c => c.DataReceived += null, mockComm.Object, $"ID: {expectedId} "); + mockComm.Raise(c => c.DataReceived += null, mockComm.Object, $"ID: {expectedId}\r\n"); }); }); @@ -91,7 +91,7 @@ namespace leak_test_project.Tests.Services // Arrange var mockComm = new Mock(); mockComm.Setup(c => c.IsOpen).Returns(true); - var service = new Board4253Service(mockComm.Object); + var service = new Board4251Service(mockComm.Object); service.TimeoutMs = 100; // 타임아웃 테스트 속도를 위해 100ms로 설정 // Act diff --git a/leak_test_project.Tests/Utils/SentinelParserTests.cs b/leak_test_project.Tests/Utils/SentinelParserTests.cs index 4733f62..f805ca3 100644 --- a/leak_test_project.Tests/Utils/SentinelParserTests.cs +++ b/leak_test_project.Tests/Utils/SentinelParserTests.cs @@ -9,7 +9,7 @@ namespace leak_test_project.Tests.Utils [Theory] [InlineData("AABB010\tH\tLR 0.123456 sccm", 'H', "LR 0.123456 sccm")] [InlineData("AABB010 H LR 0.123456 sccm", 'H', "LR 0.123456 sccm")] - public void ExtractBody_ValidInput_ShouldExtractCorrectBodyAndTypeCode(string input, char expectedType, string expectedBody) + public void ExtractBody_ValidHeaderInput_ShouldExtractCorrectBodyAndTypeCode(string input, char expectedType, string expectedBody) { // Act string resultBody = SentinelParser.ExtractBody(input, out char resultType); @@ -19,6 +19,34 @@ namespace leak_test_project.Tests.Utils Assert.Equal(expectedBody, resultBody); } + [Fact] + public void ExtractBody_AutoResult_NoHeader_ShouldReturnResultType() + { + // Arrange (C28 매뉴얼 표 규격: 헤더 없이 탭으로 구분된 최소 8개의 필드) + string input = "C01\tN1\tP01\tR--\t16:15:14.123\t02/01/16\t0000098353\tA\t*\tPLR\tP\tLR 0.123456 sccm\tLR 0.123456 sccm\t\t\r\n"; + + // Act + string resultBody = SentinelParser.ExtractBody(input, out char resultType); + + // Assert + Assert.Equal('R', resultType); + Assert.Equal(input.Trim(), resultBody); + } + + [Fact] + public void ExtractBody_AutoStreaming_NoHeader_ShouldReturnStreamingType() + { + // Arrange (헤더 없는 단순 스트리밍 데이터) + string input = "LR 0.123456 sccm\r\n"; + + // Act + string resultBody = SentinelParser.ExtractBody(input, out char resultType); + + // Assert + Assert.Equal('S', resultType); + Assert.Equal(input.Trim(), resultBody); + } + [Fact] public void ParseStreamingValue_ValidInput_ShouldParseValueAndUnit() { @@ -33,12 +61,25 @@ namespace leak_test_project.Tests.Utils Assert.Equal("sccm", result.Unit); } + [Fact] + public void ParseStreamingValue_NoHeader_ShouldParseValueAndUnit() + { + // Arrange + string input = "LR -1.50 sccm\r\n"; + + // Act + ParsedData result = SentinelParser.ParseStreamingValue(input); + + // Assert + Assert.Equal(-1.50, result.MeasuredValue); + Assert.Equal("sccm", result.Unit); + } + [Fact] public void ParseFinalResult_ValidInput_ShouldParseAllFields() { // Arrange // Sample based on manual: XXYYZZZ \t H \t C## \t P## \t LL \t HH:MM:SS \t MM/DD/YY \t ID \t Eval ... - // We need a proper string that matches the tab-separated structure. string body = "C01\tPort1\tP05\tLink\t12:30:45\t03/26/24\t1234567890\tA\tFlag\tTest\tEval\tLR 0.5 sccm"; string input = "XXXXYYY\tH\t" + body; @@ -53,6 +94,45 @@ namespace leak_test_project.Tests.Utils Assert.Equal("sccm", result.Unit); } + [Fact] + public void ParseFinalResult_AutoResult_NoHeader_ShouldParseAllFields() + { + // Arrange (C28 매뉴얼 Appendix D 표와 정확히 동일한 형식의 Auto Result 데이터) + // Channel | Port | Prog | Link | Time | Date | UniqueId | Eval | SPC | Type | TestEval | TestData1 + string input = "C01\tN1\tP01\tR--\t16:15:14.123\t02/01/16\t0000098353\tA\t*\tPLR\tP\tLR 0.123456 sccm\tLR 0.123456 sccm\t\t\r\n"; + + // Act + ParsedData result = SentinelParser.ParseFinalResult(input); + + // Assert + Assert.Equal("C01", result.ChannelNo); + Assert.Equal("P01", result.ProgramNo); + Assert.Equal("16:15:14.123", result.TestTime); + Assert.Equal("02/01/16", result.TestDate); + Assert.Equal("0000098353", result.UniqueId); + Assert.Equal("98353", result.SerialNo); // 마지막 5자리 + Assert.Equal("OK", result.Judgment); // A -> OK + Assert.Equal("A", result.SensorJudgment); + Assert.Equal(0.123456, result.MeasuredValue); + Assert.Equal("sccm", result.Unit); + } + + [Fact] + public void ParseFinalResult_AutoResult_Reject_ShouldParseProperly() + { + // Arrange (불량 판정 시의 Auto Result 데이터 시뮬레이션) + string input = "C02\tN1\tP02\tR--\t16:15:15.000\t02/01/16\t1111111111\tR\t*\tPLR\tF\tLR 1.50 sccm\t\t\r\n"; + + // Act + ParsedData result = SentinelParser.ParseFinalResult(input); + + // Assert + Assert.Equal("C02", result.ChannelNo); + Assert.Equal("NG", result.Judgment); // R -> NG + Assert.Equal("R", result.SensorJudgment); + Assert.Equal(1.50, result.MeasuredValue); + } + [Theory] [InlineData(5.0, 10.0, 1.0, "OK")] [InlineData(15.0, 10.0, 1.0, "NG")] diff --git a/leak_test_project.Tests/bin/Debug/net472/Logs/2026-04-24_system.log b/leak_test_project.Tests/bin/Debug/net472/Logs/2026-04-24_system.log new file mode 100644 index 0000000..779638f --- /dev/null +++ b/leak_test_project.Tests/bin/Debug/net472/Logs/2026-04-24_system.log @@ -0,0 +1,25 @@ +[10:14:54.667] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:14:55.084] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:14:55.501] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:14:55.934] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:14:55.934] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e +[10:18:58.651] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:18:59.085] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:18:59.502] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:18:59.920] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:18:59.920] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e +[10:19:07.251] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:19:07.669] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:19:08.101] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:19:08.535] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:19:08.536] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e +[10:25:39.456] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:39.881] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:40.304] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:40.725] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:40.726] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e +[10:26:30.241] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:26:30.672] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:26:31.088] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:26:31.505] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:26:31.505] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e diff --git a/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.dll b/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.dll index 0249697..991ca8e 100644 Binary files a/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.dll and b/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.dll differ diff --git a/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.pdb b/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.pdb index 51ce6ae..c2bc0d6 100644 Binary files a/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.pdb and b/leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.pdb differ diff --git a/leak_test_project.Tests/bin/Release/net472/Logs/2026-04-24_system.log b/leak_test_project.Tests/bin/Release/net472/Logs/2026-04-24_system.log new file mode 100644 index 0000000..b34779f --- /dev/null +++ b/leak_test_project.Tests/bin/Release/net472/Logs/2026-04-24_system.log @@ -0,0 +1,10 @@ +[10:24:40.119] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:24:40.539] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:24:40.970] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:24:41.396] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:24:41.396] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e +[10:25:21.146] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:21.569] [WARNING] [Board4251] Timeout waiting for response (Retry 2/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:21.995] [WARNING] [Board4251] Timeout waiting for response (Retry 3/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:22.417] [WARNING] [Board4251] Timeout waiting for response (Retry 4/3). Command: x00c_001101:ow2800326003e, ReceivedSoFar: +[10:25:22.417] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:ow2800326003e diff --git a/leak_test_project.Tests/leak_test_project.Tests.csproj b/leak_test_project.Tests/leak_test_project.Tests.csproj index 72821a5..cefb6b1 100644 --- a/leak_test_project.Tests/leak_test_project.Tests.csproj +++ b/leak_test_project.Tests/leak_test_project.Tests.csproj @@ -20,15 +20,28 @@ + + + + + + + - + - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfo.cs b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfo.cs index 98c5479..84bce37 100644 --- a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfo.cs +++ b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfo.cs @@ -1,9 +1,10 @@ //------------------------------------------------------------------------------ // -// This code was generated by a tool. +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. // //------------------------------------------------------------------------------ @@ -13,10 +14,10 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("leak_test_project.Tests")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+adaada7b424b603702e4aede24a1a704b0ca4949")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+77d1c3aaba381d0c78380cabfaed51897a31edc1")] [assembly: System.Reflection.AssemblyProductAttribute("leak_test_project.Tests")] [assembly: System.Reflection.AssemblyTitleAttribute("leak_test_project.Tests")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] -// MSBuild WriteCodeFragment 클래스에서 생성되었습니다. +// Generated by the MSBuild WriteCodeFragment class. diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfoInputs.cache b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfoInputs.cache index c06203d..17735c3 100644 --- a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfoInputs.cache +++ b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfoInputs.cache @@ -1 +1 @@ -203a310cc48dd71b35ad4241fb5264cead816046c43c41fd4c03ab287b385409 +dfa1bcc3dd55c3f75b42b34a72f780d712c9e1147faf3e82c45e2f51cf41885f diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.assets.cache b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.assets.cache index c98d4b6..46358a1 100644 Binary files a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.assets.cache and b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.assets.cache differ diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.AssemblyReference.cache b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.AssemblyReference.cache index 160db68..df7e742 100644 Binary files a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.AssemblyReference.cache and b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.AssemblyReference.cache differ diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers new file mode 100644 index 0000000..e69de29 diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache index 1a706eb..b011d91 100644 --- a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache +++ b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -5388cf90cfa775894722698f4c7e037cd8714537da7bb0136faff35db6bbd820 +0c498c72e1af24f34d80cd6f85d77cb00370ba1742f248052ac2cf5deed9afa2 diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.dll b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.dll index 0249697..991ca8e 100644 Binary files a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.dll and b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.dll differ diff --git a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.pdb b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.pdb index 51ce6ae..c2bc0d6 100644 Binary files a/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.pdb and b/leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.pdb differ diff --git a/leak_test_project.Tests/obj/Release/net472/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs b/leak_test_project.Tests/obj/Release/net472/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs new file mode 100644 index 0000000..3871b18 --- /dev/null +++ b/leak_test_project.Tests/obj/Release/net472/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfo.cs b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfo.cs new file mode 100644 index 0000000..a0db4a3 --- /dev/null +++ b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfo.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("leak_test_project.Tests")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+77d1c3aaba381d0c78380cabfaed51897a31edc1")] +[assembly: System.Reflection.AssemblyProductAttribute("leak_test_project.Tests")] +[assembly: System.Reflection.AssemblyTitleAttribute("leak_test_project.Tests")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] + +// Generated by the MSBuild WriteCodeFragment class. + diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfoInputs.cache b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfoInputs.cache new file mode 100644 index 0000000..81539e2 --- /dev/null +++ b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfoInputs.cache @@ -0,0 +1 @@ +acbfea79027e4ab25c0f7b2b015d0f8ac8002d7760da81e077d21947bf758347 diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.GeneratedMSBuildEditorConfig.editorconfig b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.GeneratedMSBuildEditorConfig.editorconfig new file mode 100644 index 0000000..7ff5a65 --- /dev/null +++ b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.GeneratedMSBuildEditorConfig.editorconfig @@ -0,0 +1,8 @@ +is_global = true +build_property.RootNamespace = leak_test_project.Tests +build_property.ProjectDir = C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\ +build_property.EnableComHosting = +build_property.EnableGeneratedComInterfaceComImportInterop = +build_property.CsWinRTUseWindowsUIXamlProjections = false +build_property.EffectiveAnalysisLevelStyle = +build_property.EnableCodeStyleSeverity = diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.assets.cache b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.assets.cache new file mode 100644 index 0000000..0e7eb4e Binary files /dev/null and b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.assets.cache differ diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.AssemblyReference.cache b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.AssemblyReference.cache new file mode 100644 index 0000000..2756c6d Binary files /dev/null and b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.AssemblyReference.cache differ diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers new file mode 100644 index 0000000..e69de29 diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache new file mode 100644 index 0000000..faf40a0 --- /dev/null +++ b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache @@ -0,0 +1 @@ +00716152d706ef3c486fd25e9c4535b79d009a6ebc9ecbc7d98bc05e0e6dd29a diff --git a/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.FileListAbsolute.txt b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.FileListAbsolute.txt new file mode 100644 index 0000000..e0e4ff4 --- /dev/null +++ b/leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.FileListAbsolute.txt @@ -0,0 +1,54 @@ +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.runner.visualstudio.testadapter.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.abstractions.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.runner.reporters.net452.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.runner.utility.net452.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\leak_test_project.Tests.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\leak_test_project.Tests.pdb +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\Castle.Core.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\Microsoft.VisualStudio.CodeCoverage.Shim.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\Microsoft.TestPlatform.CoreUtilities.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\Microsoft.TestPlatform.PlatformAbstractions.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\Moq.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\NuGet.Frameworks.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\System.Collections.Immutable.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\System.Reflection.Metadata.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\System.Runtime.CompilerServices.Unsafe.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\System.Threading.Tasks.Extensions.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.assert.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.core.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\xunit.execution.desktop.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\cs\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\cs\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\de\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\de\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\es\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\es\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\fr\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\fr\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\it\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\it\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\ja\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\ja\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\ko\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\ko\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\pl\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\pl\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\pt-BR\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\pt-BR\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\ru\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\ru\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\tr\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\tr\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\zh-Hans\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\zh-Hans\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\zh-Hant\Microsoft.TestPlatform.CoreUtilities.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\bin\Release\net472\zh-Hant\Microsoft.VisualStudio.TestPlatform.ObjectModel.resources.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.csproj.AssemblyReference.cache +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.GeneratedMSBuildEditorConfig.editorconfig +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.AssemblyInfoInputs.cache +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.AssemblyInfo.cs +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.csproj.CoreCompileInputs.cache +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_tes.E02BB52F.Up2Date +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.dll +C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project.Tests\obj\Release\net472\leak_test_project.Tests.pdb diff --git a/leak_test_project.Tests/obj/leak_test_project.Tests.csproj.nuget.dgspec.json b/leak_test_project.Tests/obj/leak_test_project.Tests.csproj.nuget.dgspec.json index e182603..c0d1d23 100644 --- a/leak_test_project.Tests/obj/leak_test_project.Tests.csproj.nuget.dgspec.json +++ b/leak_test_project.Tests/obj/leak_test_project.Tests.csproj.nuget.dgspec.json @@ -31,6 +31,7 @@ }, "frameworks": { "net472": { + "framework": "net472", "targetAlias": "net472", "projectReferences": {} } @@ -45,10 +46,11 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "10.0.200" + "SdkAnalysisLevel": "10.0.300" }, "frameworks": { "net472": { + "framework": "net472", "targetAlias": "net472", "dependencies": { "Microsoft.NET.Test.Sdk": { @@ -76,7 +78,7 @@ "version": "[2.5.4, )" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.201\\RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300-preview.0.26177.108\\RuntimeIdentifierGraph.json" } } } diff --git a/leak_test_project.Tests/obj/project.assets.json b/leak_test_project.Tests/obj/project.assets.json index 55b74cb..c84af5e 100644 --- a/leak_test_project.Tests/obj/project.assets.json +++ b/leak_test_project.Tests/obj/project.assets.json @@ -1,7 +1,7 @@ { - "version": 3, + "version": 4, "targets": { - ".NETFramework,Version=v4.7.2": { + "net472": { "Castle.Core/5.1.1": { "type": "package", "frameworkAssemblies": [ @@ -914,7 +914,7 @@ } }, "projectFileDependencyGroups": { - ".NETFramework,Version=v4.7.2": [ + "net472": [ "Microsoft.NET.Test.Sdk >= 17.8.0", "Moq >= 4.20.70", "coverlet.collector >= 6.0.0", @@ -929,11 +929,11 @@ "project": { "version": "1.0.0", "restore": { - "projectUniqueName": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\leak_test_project.Tests.csproj", + "projectUniqueName": "c:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\leak_test_project.Tests.csproj", "projectName": "leak_test_project.Tests", - "projectPath": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\leak_test_project.Tests.csproj", + "projectPath": "c:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\leak_test_project.Tests.csproj", "packagesPath": "C:\\Users\\COMPUTER1\\.nuget\\packages\\", - "outputPath": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\obj\\", + "outputPath": "c:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\obj\\", "projectStyle": "PackageReference", "fallbackFolders": [ "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" @@ -953,6 +953,7 @@ }, "frameworks": { "net472": { + "framework": "net472", "targetAlias": "net472", "projectReferences": {} } @@ -967,10 +968,11 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "10.0.200" + "SdkAnalysisLevel": "10.0.300" }, "frameworks": { "net472": { + "framework": "net472", "targetAlias": "net472", "dependencies": { "Microsoft.NET.Test.Sdk": { @@ -998,7 +1000,7 @@ "version": "[2.5.4, )" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.201\\RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.300-preview.0.26177.108\\RuntimeIdentifierGraph.json" } } } diff --git a/leak_test_project.Tests/obj/project.nuget.cache b/leak_test_project.Tests/obj/project.nuget.cache index 43e1bbb..43381d5 100644 --- a/leak_test_project.Tests/obj/project.nuget.cache +++ b/leak_test_project.Tests/obj/project.nuget.cache @@ -1,6 +1,6 @@ { "version": 2, - "dgSpecHash": "9P+UePPu2QA=", + "dgSpecHash": "mKqjtWarivM=", "success": true, "projectFilePath": "C:\\Users\\COMPUTER1\\Desktop\\mobi\\leak_test_project\\leak_test_project.Tests\\leak_test_project.Tests.csproj", "expectedPackageFiles": [ diff --git a/leak_test_project.slnx b/leak_test_project.slnx index 5c33b2e..15d8c6d 100644 --- a/leak_test_project.slnx +++ b/leak_test_project.slnx @@ -1,3 +1,4 @@ + diff --git a/leak_test_project/Infrastructure/SerialProvider.cs b/leak_test_project/Infrastructure/CommunicationBase.cs similarity index 73% rename from leak_test_project/Infrastructure/SerialProvider.cs rename to leak_test_project/Infrastructure/CommunicationBase.cs index 9b7006f..ad448e8 100644 --- a/leak_test_project/Infrastructure/SerialProvider.cs +++ b/leak_test_project/Infrastructure/CommunicationBase.cs @@ -3,6 +3,38 @@ using System.IO.Ports; namespace leak_test_project.Infrastructure { + /// + /// 하드웨어 통신을 위한 추상화 인터페이스. + /// 시리얼(RS232), 이더넷(TCP/IP) 등 통신 방식에 상관없이 + /// 상위 서비스에서 동일한 방식으로 하드웨어에 접근할 수 있도록 정의함. + /// + public interface ICommunication + { + /// 통신 채널 식별 이름 (예: COM1, TCP_192.168.0.1) + string Name { get; } + + /// 현재 통신 채널이 열려 있는지 여부 + bool IsOpen { get; } + + /// 통신 채널을 연결함 + bool Open(); + + /// 통신 채널 연결을 해제함 + void Close(); + + /// 데이터를 하드웨어로 전송함 + bool Write(string data); + + /// 수신 버퍼를 비워 잔류 데이터를 제거함 + void ClearBuffer(); + + /// 하드웨어로부터 데이터를 수신했을 때 발생하는 이벤트 + event System.EventHandler DataReceived; + + /// 연결 상태가 변경되었을 때 발생하는 이벤트 (connected, disconnected) + event System.EventHandler ConnectionStatusChanged; + } + /// /// RS232 시리얼 통신을 담당하는 구현체. /// 연결 안정성 강화를 위한 예외 처리 및 상태 감지 로직 포함. diff --git a/leak_test_project/Infrastructure/RealDioBoard.cs b/leak_test_project/Infrastructure/DioBoardBase.cs similarity index 88% rename from leak_test_project/Infrastructure/RealDioBoard.cs rename to leak_test_project/Infrastructure/DioBoardBase.cs index 186c6bf..d68942f 100644 --- a/leak_test_project/Infrastructure/RealDioBoard.cs +++ b/leak_test_project/Infrastructure/DioBoardBase.cs @@ -7,6 +7,34 @@ using leak_test_project.Utils; namespace leak_test_project.Infrastructure { + /// + /// DIO 보드 추상화 인터페이스. + /// 실제 하드웨어(JunSystem DIO) 또는 시뮬레이션 보드를 교체 가능하게 설계. + /// + public interface IDioBoard : IDisposable + { + /// DIO 보드를 초기화합니다. + bool Initialize(); + + /// 지정된 입력 포인트의 현재 값을 읽습니다. + bool ReadInput(string pointName); + + /// 지정된 출력 포인트에 값을 씁니다. + void WriteOutput(string pointName, bool value); + + /// 모든 입력 포인트 목록을 반환합니다. + List GetInputPoints(); + + /// 모든 출력 포인트 목록을 반환합니다. + List GetOutputPoints(); + + /// 입력 포인트의 값이 OFF→ON으로 변경되었을 때 발생합니다. + event EventHandler InputChanged; + + /// 보드 동작 중 오류가 발생했을 때 발생합니다. + event EventHandler ErrorOccurred; + } + /// /// 실제 ADLINK 하드웨어와 통신하는 DIO 보드 구현체. /// Dask.cs 래퍼를 사용하여 PCIS-DASK API를 호출합니다. @@ -130,7 +158,7 @@ namespace leak_test_project.Infrastructure // 화면의 상태(불빛)는 하드웨어 연동 여부와 관계없이 항상 업데이트합니다. (원자적 연산) point.Value = value; - // 실제 하드웨어가 없으면 물리적 제어 부분만 생략합니다. + // 실제 하드웨어가 없ূপ면 물리적 제어 부분만 생략합니다. if (_cardNumber < 0) return; int index = point.BitIndex; diff --git a/leak_test_project/Infrastructure/ICommunication.cs b/leak_test_project/Infrastructure/ICommunication.cs deleted file mode 100644 index 45de89b..0000000 --- a/leak_test_project/Infrastructure/ICommunication.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace leak_test_project.Infrastructure -{ - /// - /// 하드웨어 통신을 위한 추상화 인터페이스. - /// 시리얼(RS232), 이더넷(TCP/IP) 등 통신 방식에 상관없이 - /// 상위 서비스에서 동일한 방식으로 하드웨어에 접근할 수 있도록 정의함. - /// - public interface ICommunication - { - /// 통신 채널 식별 이름 (예: COM1, TCP_192.168.0.1) - string Name { get; } - - /// 현재 통신 채널이 열려 있는지 여부 - bool IsOpen { get; } - - /// 통신 채널을 연결함 - bool Open(); - - /// 통신 채널 연결을 해제함 - void Close(); - - /// 데이터를 하드웨어로 전송함 - bool Write(string data); - - /// 수신 버퍼를 비워 잔류 데이터를 제거함 - void ClearBuffer(); - - /// 하드웨어로부터 데이터를 수신했을 때 발생하는 이벤트 - event System.EventHandler DataReceived; - - /// 연결 상태가 변경되었을 때 발생하는 이벤트 (connected, disconnected) - event System.EventHandler ConnectionStatusChanged; - } -} diff --git a/leak_test_project/Infrastructure/IDioBoard.cs b/leak_test_project/Infrastructure/IDioBoard.cs deleted file mode 100644 index cff5a91..0000000 --- a/leak_test_project/Infrastructure/IDioBoard.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Collections.Generic; -using leak_test_project.Models; - -namespace leak_test_project.Infrastructure -{ - /// - /// DIO 보드 추상화 인터페이스. - /// 실제 하드웨어(JunSystem DIO) 또는 시뮬레이션 보드를 교체 가능하게 설계. - /// - public interface IDioBoard : IDisposable - { - /// DIO 보드를 초기화합니다. - bool Initialize(); - - /// 지정된 입력 포인트의 현재 값을 읽습니다. - bool ReadInput(string pointName); - - /// 지정된 출력 포인트에 값을 씁니다. - void WriteOutput(string pointName, bool value); - - /// 모든 입력 포인트 목록을 반환합니다. - List GetInputPoints(); - - /// 모든 출력 포인트 목록을 반환합니다. - List GetOutputPoints(); - - /// 입력 포인트의 값이 OFF→ON으로 변경되었을 때 발생합니다. - event EventHandler InputChanged; - - /// 보드 동작 중 오류가 발생했을 때 발생합니다. - event EventHandler ErrorOccurred; - } -} diff --git a/leak_test_project/Models/AppConfig.cs b/leak_test_project/Models/AppConfig.cs deleted file mode 100644 index 969acd6..0000000 --- a/leak_test_project/Models/AppConfig.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace leak_test_project.Models -{ - public enum IdSensorType - { - ZMDI, // 기존 ZMDI 센서 - Board4253 // 신규 4253 보드 (ID 읽기용) - } - - public class AppConfig - { - public IdSensorType SelectedIdSensor { get; set; } = IdSensorType.Board4253; - - public string LeftPort { get; set; } = "COM9"; - public string RightPort { get; set; } = "COM8"; - public int ZmdiBaudRate { get; set; } = 19200; - - public string SensorPort { get; set; } = "COM1"; - public int SensorBaudRate { get; set; } = 9600; - - public string Board4253Port { get; set; } = "COM3"; - public int Board4253BaudRate { get; set; } = 115200; - public int Board4253Timeout { get; set; } = 5000; // 보드가 응답을 주는데 2초 이상 걸리므로 무조건 길게 대기 - - public double SpecUL { get; set; } = 1.00; - public double SpecLL { get; set; } = -1.00; - } -} diff --git a/leak_test_project/Models/DioPoint.cs b/leak_test_project/Models/DioPoint.cs deleted file mode 100644 index bd8940a..0000000 --- a/leak_test_project/Models/DioPoint.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace leak_test_project.Models -{ - /// - /// DIO 보드의 개별 포인트(입력 또는 출력)를 나타내는 모델. - /// - public class DioPoint - { - /// 포인트 이름 (예: LEFT_START, LEFT_OK, RIGHT_NG) - public string Name { get; set; } - - /// 설명 (예: 좌측 시작 신호) - public string Description { get; set; } - - /// true: 입력, false: 출력 - public bool IsInput { get; set; } - - /// 현재 값 (ON=true, OFF=false) - public bool Value { get; set; } - - /// 하드웨어 IO 포트의 실제 비트 인덱스 (0~31) - public int BitIndex { get; set; } - } - - /// - /// DIO 입력 변경 시 사용되는 이벤트 인자. - /// - public class DioEventArgs : System.EventArgs - { - public string PointName { get; set; } - public bool NewValue { get; set; } - - public DioEventArgs(string pointName, bool newValue) - { - PointName = pointName; - NewValue = newValue; - } - } -} diff --git a/leak_test_project/Models/InOutItem.cs b/leak_test_project/Models/InOutItem.cs deleted file mode 100644 index 86b51c3..0000000 --- a/leak_test_project/Models/InOutItem.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace leak_test_project.Models -{ - public class InOutItem : System.ComponentModel.INotifyPropertyChanged - { - public string Address { get; set; } - public string Name { get; set; } - public string Description { get; set; } - - private bool _value; - /// 현재 값 (ON=true, OFF=false). DIO 실시간 상태 표시용. - public bool Value - { - get => _value; - set - { - if (_value != value) - { - _value = value; - PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof(Value))); - } - } - } - - public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; - } -} diff --git a/leak_test_project/Models/InspectData.cs b/leak_test_project/Models/InspectData.cs deleted file mode 100644 index a21d155..0000000 --- a/leak_test_project/Models/InspectData.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace leak_test_project.Models -{ - public class InspectData - { - public string InspectDate { get; set; } - public string InspectTime { get; set; } - public string Retest { get; set; } - public string Mode { get; set; } // 예: 개발/양산 - public string LineNo { get; set; } - public string ProductType { get; set; } - public string ProductId { get; set; } - public string Channel { get; set; } // 좌/우 - public string SpecUL { get; set; } - public string SpecLL { get; set; } - public string MeasuredValue { get; set; } - public string Judgment { get; set; } - } -} diff --git a/leak_test_project/Models/ParsedData.cs b/leak_test_project/Models/ParsedData.cs deleted file mode 100644 index e5076a9..0000000 --- a/leak_test_project/Models/ParsedData.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; - -namespace leak_test_project.Models -{ - public class ParsedData - { - public string RawData { get; set; } - public double MeasuredValue { get; set; } - public string Unit { get; set; } - public string TestType { get; set; } - public string Judgment { get; set; } - public DateTime Timestamp { get; set; } - - // 스크린샷 추가 요구사항 필드 - public string ChannelNo { get; set; } - public string ProgramNo { get; set; } - public string UniqueId { get; set; } - public string TestDate { get; set; } - public string TestTime { get; set; } - public string SerialNo { get; set; } - public string LowID { get; set; } - - /// 센서 자체 판정 결과 (A=Accept, R=Reject). SPEC 교차 검증에 사용. - public string SensorJudgment { get; set; } - - public ParsedData() - { - Timestamp = DateTime.Now; - } - } -} diff --git a/leak_test_project/Models/ProjectModels.cs b/leak_test_project/Models/ProjectModels.cs new file mode 100644 index 0000000..d8e828e --- /dev/null +++ b/leak_test_project/Models/ProjectModels.cs @@ -0,0 +1,159 @@ +using System; + +namespace leak_test_project.Models +{ + public class AppConfig + { + + + public string SensorPort { get; set; } = "COM1"; + public int SensorBaudRate { get; set; } = 9600; + + public string Board4251Port { get; set; } = "COM3"; + public int Board4251BaudRate { get; set; } = 115200; + public int Board4251Timeout { get; set; } = 5000; // 보드가 응답을 주는데 2초 이상 걸리므로 무조건 길게 대기 + + public double SpecUL { get; set; } = 1.00; + public double SpecLL { get; set; } = -1.00; + } + + /// + /// DIO 보드의 개별 포인트(입력 또는 출력)를 나타내는 모델. + /// + public class DioPoint + { + /// 포인트 이름 (예: LEFT_START, LEFT_OK, RIGHT_NG) + public string Name { get; set; } + + /// 설명 (예: 좌측 시작 신호) + public string Description { get; set; } + + /// true: 입력, false: 출력 + public bool IsInput { get; set; } + + /// 현재 값 (ON=true, OFF=false) + public bool Value { get; set; } + + /// 하드웨어 IO 포트의 실제 비트 인덱스 (0~31) + public int BitIndex { get; set; } + } + + /// + /// DIO 입력 변경 시 사용되는 이벤트 인자. + /// + public class DioEventArgs : System.EventArgs + { + public string PointName { get; set; } + public bool NewValue { get; set; } + + public DioEventArgs(string pointName, bool newValue) + { + PointName = pointName; + NewValue = newValue; + } + } + + public class InOutItem : System.ComponentModel.INotifyPropertyChanged + { + public string Address { get; set; } + public string Name { get; set; } + public string Description { get; set; } + + private bool _value; + /// 현재 값 (ON=true, OFF=false). DIO 실시간 상태 표시용. + public bool Value + { + get => _value; + set + { + if (_value != value) + { + _value = value; + PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof(Value))); + } + } + } + + public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + } + + public class InspectData + { + public string InspectDate { get; set; } + public string InspectTime { get; set; } + public string Retest { get; set; } + public string Mode { get; set; } // 예: 개발/양산 + public string LineNo { get; set; } + public string ProductType { get; set; } + public string ProductId { get; set; } + public string Channel { get; set; } // 좌/우 + public string SpecUL { get; set; } + public string SpecLL { get; set; } + public string MeasuredValue { get; set; } + public string Judgment { get; set; } + } + + public class ParsedData + { + public string RawData { get; set; } + public double MeasuredValue { get; set; } + public string Unit { get; set; } + public string TestType { get; set; } + public string Judgment { get; set; } + public DateTime Timestamp { get; set; } + + // 스크린샷 추가 요구사항 필드 + public string ChannelNo { get; set; } + public string ProgramNo { get; set; } + public string UniqueId { get; set; } + public string TestDate { get; set; } + public string TestTime { get; set; } + public string SerialNo { get; set; } + public string LowID { get; set; } + + /// 센서 자체 판정 결과 (A=Accept, R=Reject). SPEC 교차 검증에 사용. + public string SensorJudgment { get; set; } + + public ParsedData() + { + Timestamp = DateTime.Now; + } + } + + /// + /// ZMDI 센서에서 읽은 제품 ID 정보. + /// 레거시 ClsSensorReader의 필드를 기반으로 구성. + /// + public class SensorIdData + { + /// 센서 메모리에서 읽은 원본 Low ID 문자열 + public string LowID { get; set; } = ""; + + /// 제조 년도 + public int Year { get; set; } + + /// 제조 월 + public int Month { get; set; } + + /// 제조 일 + public int Day { get; set; } + + /// 시리얼 번호 (5자리 패딩) + public string Serial { get; set; } = ""; + + /// MC Line (개발/양산 구분) + public string McLine { get; set; } = ""; + + /// 라인 번호 + public string LineNo { get; set; } = ""; + + /// 제품 구분 (Item) + public string Item { get; set; } = ""; + + /// 최종 조합 ID (년+월+일+시리얼+라인+항목) + public string ID { get; set; } = ""; + + /// 이전 검사 결과. F=미시험/불합격, P=합격. 불량 제품 필터링에 사용. + public string PrevResult { get; set; } = ""; + } +} diff --git a/leak_test_project/Models/SensorIdData.cs b/leak_test_project/Models/SensorIdData.cs deleted file mode 100644 index 800f017..0000000 --- a/leak_test_project/Models/SensorIdData.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace leak_test_project.Models -{ - /// - /// ZMDI 센서에서 읽은 제품 ID 정보. - /// 레거시 ClsSensorReader의 필드를 기반으로 구성. - /// - public class SensorIdData - { - /// 센서 메모리에서 읽은 원본 Low ID 문자열 - public string LowID { get; set; } = ""; - - /// 제조 년도 - public int Year { get; set; } - - /// 제조 월 - public int Month { get; set; } - - /// 제조 일 - public int Day { get; set; } - - /// 시리얼 번호 (5자리 패딩) - public string Serial { get; set; } = ""; - - /// MC Line (개발/양산 구분) - public string McLine { get; set; } = ""; - - /// 라인 번호 - public string LineNo { get; set; } = ""; - - /// 제품 구분 (Item) - public string Item { get; set; } = ""; - - /// 최종 조합 ID (년+월+일+시리얼+라인+항목) - public string ID { get; set; } = ""; - - /// 이전 검사 결과. F=미시험/불합격, P=합격. 불량 제품 필터링에 사용. - public string PrevResult { get; set; } = ""; - } -} diff --git a/leak_test_project/Services/Board4251.cs b/leak_test_project/Services/Board4251.cs new file mode 100644 index 0000000..d62c627 --- /dev/null +++ b/leak_test_project/Services/Board4251.cs @@ -0,0 +1,489 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using leak_test_project.Infrastructure; +using leak_test_project.Models; +using leak_test_project.Utils; + +namespace leak_test_project.Services +{ + /// + /// 신규 4251 보드와의 통신을 관리하는 서비스. + /// 키워드를 기준으로 데이터를 수신하며, 상태 확인 및 ID 읽기 기능을 제공함. + /// + public class Board4251Service : IDisposable + { + private readonly ICommunication _communication; + private readonly StringBuilder _receiveBuffer = new StringBuilder(); + private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); + private TaskCompletionSource _responseTcs; + public int TimeoutMs { get; set; } = 5000; // 보드가 응답을 주는데 2초 이상 걸리므로 무조건 길게 대기 + private bool _shouldBeConnected = false; + private System.Timers.Timer _reconnectTimer; + + public event EventHandler ErrorOccurred; + public event EventHandler ConnectionChanged; + + public string LastResponse { get; private set; } = ""; + + public string GetLastBuffer() + { + string currentBuf = _receiveBuffer.ToString().Trim(); + return string.IsNullOrEmpty(currentBuf) ? LastResponse : currentBuf; + } + + public Board4251Service(ICommunication communication) + { + _communication = communication; + _communication.DataReceived += OnDataReceived; + _communication.ConnectionStatusChanged += (s, isConnected) => { + ConnectionChanged?.Invoke(this, isConnected); + if (!isConnected && _shouldBeConnected) StartReconnectTimer(); + }; + + _reconnectTimer = new System.Timers.Timer(1000); + _reconnectTimer.AutoReset = false; + _reconnectTimer.Elapsed += (s, e) => { + if (_shouldBeConnected && !_communication.IsOpen) + { + if (!_communication.Open()) + { + if (_shouldBeConnected) _reconnectTimer.Start(); + } + } + else if (_shouldBeConnected) + { + _reconnectTimer.Start(); + } + }; + } + + private void StartReconnectTimer() + { + if (_reconnectTimer != null && !_reconnectTimer.Enabled) _reconnectTimer.Start(); + } + + public bool Connect() + { + _shouldBeConnected = true; + bool opened = _communication.Open(); + if (!opened) StartReconnectTimer(); + return opened; + } + + public void Disconnect() + { + _shouldBeConnected = false; + _reconnectTimer?.Stop(); + _communication.Close(); + } + + /// + /// 4251 보드의 상태를 확인합니다. + /// + /// 성공 여부 + public async Task CheckStatusAsync(int channel = 1) + { + string response = await SendCommandAsync($"x00c_00{channel}101:owt28006727ea97c7801\r\n"); + + if (response == null) + { + return false; + } + + if (response.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0) + { + return true; + } + + if (response.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) >= 0) + { + return false; + } + + // Success도 Fail도 없는 알 수 없는 응답인 경우 + return false; + } + + /// + /// 4251 보드로부터 16자리 ID를 읽어옵니다. + /// + /// 16자리 ID 문자열, 실패 시 null + public async Task ReadIdAsync(int channel = 1) + { + string response = await SendCommandAsync($"x00c_00{channel}101:ow2800326003e\r\n"); + + if (response == null) + { + return null; + } + + if (response.Contains("Fail")) + { + return null; + } + + string id = ExtractId(response); + if (string.IsNullOrEmpty(id)) + { + return null; + } + + return id; + } + + private string ExtractId(string response) + { + if (string.IsNullOrEmpty(response)) return null; + + // 보드 응답에는 보낸 명령어가 에코되어 포함되므로, 줄바꿈으로 나누어 실제 데이터 라인을 찾음 + string[] lines = response.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) + { + string trimmed = line.Trim(); + if (trimmed.Contains("x00c_") || trimmed.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0 || trimmed.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0) + continue; + + // 16자리 영숫자 ID 추출 + var match = System.Text.RegularExpressions.Regex.Match(trimmed, @"[A-Za-z0-9]{16}"); + if (match.Success) return match.Value; + } + + return null; + } + + public async Task SendCommandAsync(string command) + { + await _lock.WaitAsync(); + try + { + int retryCount = 0; + while (retryCount <= 3) + { + if (!_communication.IsOpen) + { + if (!_communication.Open()) + { + retryCount++; + await Task.Delay(300); + continue; + } + } + + _receiveBuffer.Clear(); + LastResponse = ""; + _responseTcs = new TaskCompletionSource(); + + _communication.ClearBuffer(); // 이전에 남아있던 패킷 조각 완벽히 제거 + + Debug.WriteLine($"[Board4251] Sending Command: {command.TrimEnd()}"); + _communication.Write(command); + + using (var cts = new CancellationTokenSource(TimeoutMs)) + { + cts.Token.Register(() => _responseTcs.TrySetCanceled()); + try + { + return await _responseTcs.Task; + } + catch (OperationCanceledException) + { + retryCount++; + string currentBuffer = _receiveBuffer.ToString().Trim(); + FileLogger.Log("WARNING", $"[Board4251] Timeout waiting for response (Retry {retryCount}/3). Command: {command.Trim()}, ReceivedSoFar: {currentBuffer}"); + _receiveBuffer.Clear(); + if (retryCount <= 3) await Task.Delay(300); + } + } + } + + FileLogger.Log("ERROR", $"[Board4251] Failed to receive response after 3 retries: {command.Trim()}"); + return null; + } + finally + { + _responseTcs = null; + _lock.Release(); + } + } + + private void OnDataReceived(object sender, string data) + { + Debug.WriteLine($"[Board4251] Raw Data Chunk Received: {data.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t")}"); + _receiveBuffer.Append(data); + string currentContent = _receiveBuffer.ToString(); + + bool isComplete = false; + + if (currentContent.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0 || + currentContent.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0 || + currentContent.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) >= 0 || + currentContent.IndexOf("OFF", StringComparison.OrdinalIgnoreCase) >= 0) + { + isComplete = true; + } + else + { + // 가 오지 않았더라도, 명령어 에코가 아닌 줄에서 16자리 ID를 발견하면 즉시 완료 처리 + string[] lines = currentContent.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); + foreach (var line in lines) + { + string trimmed = line.Trim(); + if (trimmed.Contains("x00c_")) continue; + + var match = System.Text.RegularExpressions.Regex.Match(trimmed, @"[A-Za-z0-9]{16}"); + if (match.Success) + { + isComplete = true; + break; + } + } + } + + if (isComplete) + { + if (_responseTcs != null && !_responseTcs.Task.IsCompleted) + { + LastResponse = currentContent.Trim(); + _responseTcs.TrySetResult(currentContent); + } + _receiveBuffer.Clear(); + } + } + + public void Dispose() + { + Disconnect(); + _communication.DataReceived -= OnDataReceived; + _reconnectTimer?.Dispose(); + _lock.Dispose(); + } + } + + /// + /// 신규 4251 보드를 사용하여 제품 ID를 읽는 센서 서비스. + /// ZmdiSensorService와 동일한 구조로 구현되어 교체가 용이함. + /// + public class Board4251SensorService : IIdSensorService + { + private readonly Board4251Service _service; + private readonly int _sensorIndex; + + public event EventHandler ProgressMessage; + public event EventHandler ErrorMessage; + public event EventHandler ConnectionChanged; + + public Board4251SensorService(Board4251Service service, int sensorIndex) + { + _service = service; + _sensorIndex = sensorIndex; + // 좌우 오류 간섭을 막기 위해 공용 에러 이벤트 구독 해제 + _service.ConnectionChanged += (s, isConnected) => ConnectionChanged?.Invoke(this, isConnected); + } + + public bool Connect() => _service.Connect(); + public void Disconnect() => _service.Disconnect(); + + public SensorIdData ReadSensor() + { + try + { + int channel = _sensorIndex + 1; + + // 0. 초기화 (x00o 전송) + ProgressMessage?.Invoke(this, "4251 보드 초기화 중..."); + // x00o 명령을 보내고 응답 내용에 상관없이 다음 단계 진행 (최대 5초 대기) + Task.Run(() => _service.SendCommandAsync("x00o\r\n")).Wait(5000); + Task.Delay(350).Wait(); // 보드 안정화 대기 + + // 1. 보드 상태 확인 (Fail인 경우 중단) + ProgressMessage?.Invoke(this, "4251 보드 데이터 읽는 중..."); + int extendedTimeout = 15000; + string statusCmd = $"x00c_00{channel}101:owt28006727ea97c7801"; + var statusTask = Task.Run(() => _service.CheckStatusAsync(channel)); + if (!statusTask.Wait(extendedTimeout)) + { + string buf = _service.GetLastBuffer(); + string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; + ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); + return null; + } + if (!statusTask.Result) + { + string buf = _service.GetLastBuffer(); + string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; + ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} 상태 이상 또는 Fail)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); + return null; + } + + // 2. ID 읽기 (끝자리가 F일 경우 최대 3회 시도) + string idCmd = $"x00c_00{channel}101:ow2800326003e"; + string rawId = null; + int maxAttempts = 3; + + for (int attempt = 1; attempt <= maxAttempts; attempt++) + { + var idTask = Task.Run(() => _service.ReadIdAsync(channel)); + if (!idTask.Wait(extendedTimeout)) + { + if (attempt == maxAttempts) + { + string buf = _service.GetLastBuffer(); + string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; + ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} ID 대기 타임아웃)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); + return null; + } + continue; + } + + rawId = idTask.Result; + if (string.IsNullOrEmpty(rawId)) + { + if (attempt == maxAttempts) + { + string buf = _service.GetLastBuffer(); + string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; + ErrorMessage?.Invoke(this, $"통신 실패 (4251 보드 CH{channel} ID 응답 없거나 파싱 오류)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); + return null; + } + continue; + } + + // 정상적으로 파싱된 경우, 끝자리가 'F'인지 확인 + if (rawId.EndsWith("F", StringComparison.OrdinalIgnoreCase)) + { + if (attempt < maxAttempts) + { + ProgressMessage?.Invoke(this, $"ID 읽기 재시도 중... ({attempt}/{maxAttempts})"); + Task.Delay(350).Wait(); // 재시도 전 약간의 딜레이 + continue; + } + else + { + ProgressMessage?.Invoke(this, $"최대 재시도(2회 추가) 초과. 끝자리가 F인 ID({rawId})를 사용합니다."); + } + } + + // 제대로 된 값을 얻었거나 최대 횟수에 도달하면 루프 탈출 + break; + } + + // 3. SensorIdData 객체 구성 (16자리 ID를 각 필드에 적절히 분배) + // 신규 보드는 16자리 전체가 ID이므로, 파싱 로직 없이 통째로 넣거나 + // 특정 규칙이 있다면 여기서 분할함. + var data = new SensorIdData + { + LowID = rawId, + ID = rawId, // 16자리 전체를 ID로 사용 + Serial = "", // 시리얼 번호는 현재 존재하지 않으므로 강제로 파싱하지 않음 + Item = "N/A", + PrevResult = "F" // '불량제품 투입' 필터를 통과하기 위한 강제 초기화 + }; + + ProgressMessage?.Invoke(this, "ID 읽기 성공"); + return data; + } + catch (Exception ex) + { + ErrorMessage?.Invoke(this, $"4251 보드 읽기 중 예외 발생: {ex.Message}"); + return null; + } + } + + public void Dispose() + { + // 공유 서비스(Board4251Service)는 외부(HomeViewModel)에서 관리하므로 여기서 해제하지 않음 + } + } + + /// + /// 시리얼 통신을 기반으로 동작하는 신규 4251 DIO 보드 구현체. + /// 기존 RealDioBoard(Legacy)와 교체 가능함. + /// + public class Board4251DioBoard : IDioBoard + { + private readonly Board4251Service _service; + private readonly List _inputs = new List(); + private readonly List _outputs = new List(); + private bool _isDisposed = false; + +#pragma warning disable 0067 + public event EventHandler InputChanged; +#pragma warning restore 0067 + + public event EventHandler ErrorOccurred; + + public Board4251DioBoard(Board4251Service service) + { + _service = service; + _service.ErrorOccurred += (s, msg) => ErrorOccurred?.Invoke(this, msg); + InitializePoints(); + } + + private void InitializePoints() + { + // 실제 보드 구성에 맞게 입출력 포인트 정의 (DioConfigParser 기반 혹은 하드코딩) + // 일단 기존 프로젝트 구성과 호환되도록 빈 리스트 혹은 기본값 설정 + var config = DioConfigParser.LoadDefault(); + _inputs.AddRange(config.InputPoints); + _outputs.AddRange(config.OutputPoints); + } + + public bool Initialize() + { + // 시리얼 연결 시도 + try { + if (!_service.Connect()) + { + ErrorOccurred?.Invoke(this, $"4251 Board: Failed to open serial port."); + return false; + } + + // 보드 상태 확인 + var statusTask = Task.Run(() => _service.CheckStatusAsync()); + if (!statusTask.Wait(5000)) + { + ErrorOccurred?.Invoke(this, "4251 Board: Initialization Timeout (CheckStatus)."); + return false; + } + + return statusTask.Result; + } catch (Exception ex) { + ErrorOccurred?.Invoke(this, $"4251 Board: Initialization Error - {ex.Message}"); + return false; + } + } + + public bool ReadInput(string pointName) + { + // 신규 보드의 입력 읽기 프로토콜이 필요한 부분 (현재 ReadId 등만 구현됨) + // 구현 계획에는 ID 읽기와 상태 확인만 있었으므로, + // 실제 DIO 기능을 위해선 추가적인 시리얼 명령이 필요할 수 있음. + // 일단 true/false 로직 구현 + return false; + } + + public void WriteOutput(string pointName, bool value) + { + // 보드 출력 제어 명령 전송 (예시 프로토콜 필요) + // _service.SendCommandAsync(...) 호출 형태가 될 것임. + } + + public List GetInputPoints() => _inputs; + public List GetOutputPoints() => _outputs; + + public void Dispose() + { + if (!_isDisposed) + { + _isDisposed = true; + _service.Disconnect(); + _service.Dispose(); + } + } + } +} diff --git a/leak_test_project/Services/Board4253DioBoard.cs b/leak_test_project/Services/Board4253DioBoard.cs deleted file mode 100644 index 2642784..0000000 --- a/leak_test_project/Services/Board4253DioBoard.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using leak_test_project.Infrastructure; -using leak_test_project.Models; -using leak_test_project.Utils; - -namespace leak_test_project.Services -{ - /// - /// 시리얼 통신을 기반으로 동작하는 신규 4253 DIO 보드 구현체. - /// 기존 RealDioBoard(Legacy)와 교체 가능함. - /// - public class Board4253DioBoard : IDioBoard - { - private readonly Board4253Service _service; - private readonly List _inputs = new List(); - private readonly List _outputs = new List(); - private bool _isDisposed = false; - -#pragma warning disable 0067 - public event EventHandler InputChanged; -#pragma warning restore 0067 - - public event EventHandler ErrorOccurred; - - public Board4253DioBoard(Board4253Service service) - { - _service = service; - _service.ErrorOccurred += (s, msg) => ErrorOccurred?.Invoke(this, msg); - InitializePoints(); - } - - private void InitializePoints() - { - // 실제 보드 구성에 맞게 입출력 포인트 정의 (DioConfigParser 기반 혹은 하드코딩) - // 일단 기존 프로젝트 구성과 호환되도록 빈 리스트 혹은 기본값 설정 - var config = DioConfigParser.LoadDefault(); - _inputs.AddRange(config.InputPoints); - _outputs.AddRange(config.OutputPoints); - } - - public bool Initialize() - { - // 시리얼 연결 시도 - try { - if (!_service.Connect()) - { - ErrorOccurred?.Invoke(this, $"4253 Board: Failed to open serial port."); - return false; - } - - // 보드 상태 확인 - var statusTask = Task.Run(() => _service.CheckStatusAsync()); - if (!statusTask.Wait(5000)) - { - ErrorOccurred?.Invoke(this, "4253 Board: Initialization Timeout (CheckStatus)."); - return false; - } - - return statusTask.Result; - } catch (Exception ex) { - ErrorOccurred?.Invoke(this, $"4253 Board: Initialization Error - {ex.Message}"); - return false; - } - } - - public bool ReadInput(string pointName) - { - // 신규 보드의 입력 읽기 프로토콜이 필요한 부분 (현재 ReadId 등만 구현됨) - // 구현 계획에는 ID 읽기와 상태 확인만 있었으므로, - // 실제 DIO 기능을 위해선 추가적인 시리얼 명령이 필요할 수 있음. - // 일단 true/false 로직 구현 - return false; - } - - public void WriteOutput(string pointName, bool value) - { - // 보드 출력 제어 명령 전송 (예시 프로토콜 필요) - // _service.SendCommandAsync(...) 호출 형태가 될 것임. - } - - public List GetInputPoints() => _inputs; - public List GetOutputPoints() => _outputs; - - public void Dispose() - { - if (!_isDisposed) - { - _isDisposed = true; - _service.Disconnect(); - _service.Dispose(); - } - } - } -} diff --git a/leak_test_project/Services/Board4253SensorService.cs b/leak_test_project/Services/Board4253SensorService.cs deleted file mode 100644 index e6f28a2..0000000 --- a/leak_test_project/Services/Board4253SensorService.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Threading.Tasks; -using leak_test_project.Infrastructure; -using leak_test_project.Models; -using leak_test_project.Utils; - -namespace leak_test_project.Services -{ - /// - /// 신규 4253 보드를 사용하여 제품 ID를 읽는 센서 서비스. - /// ZmdiSensorService와 동일한 구조로 구현되어 교체가 용이함. - /// - public class Board4253SensorService : IIdSensorService - { - private readonly Board4253Service _service; - private readonly int _sensorIndex; - - public event EventHandler ProgressMessage; - public event EventHandler ErrorMessage; - public event EventHandler ConnectionChanged; - - public Board4253SensorService(Board4253Service service, int sensorIndex) - { - _service = service; - _sensorIndex = sensorIndex; - // 좌우 오류 간섭을 막기 위해 공용 에러 이벤트 구독 해제 - _service.ConnectionChanged += (s, isConnected) => ConnectionChanged?.Invoke(this, isConnected); - } - - public bool Connect() => _service.Connect(); - public void Disconnect() => _service.Disconnect(); - - public SensorIdData ReadSensor() - { - try - { - int channel = _sensorIndex + 1; - - // 0. 초기화 (x00o 전송) - ProgressMessage?.Invoke(this, "4253 보드 초기화 중..."); - // x00o 명령을 보내고 응답 내용에 상관없이 다음 단계 진행 (최대 5초 대기) - Task.Run(() => _service.SendCommandAsync("x00o\r\n")).Wait(5000); - Task.Delay(350).Wait(); // 보드 안정화 대기 - - // 1. 보드 상태 확인 (Fail인 경우 중단) - ProgressMessage?.Invoke(this, "4253 보드 데이터 읽는 중..."); - int extendedTimeout = 15000; - string statusCmd = $"x00c_00{channel}101:owt28006727ea97c7801"; - var statusTask = Task.Run(() => _service.CheckStatusAsync(channel)); - if (!statusTask.Wait(extendedTimeout)) - { - string buf = _service.GetLastBuffer(); - string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; - ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} 상태 타임아웃)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); - return null; - } - if (!statusTask.Result) - { - string buf = _service.GetLastBuffer(); - string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; - ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} 상태 이상 또는 Fail)\r\n[송신값]: {statusCmd}\r\n[수신값]: {displayBuf}"); - return null; - } - - // 2. ID 읽기 (끝자리가 F일 경우 최대 3회 시도) - string idCmd = $"x00c_00{channel}101:ow2800326003e"; - string rawId = null; - int maxAttempts = 3; - - for (int attempt = 1; attempt <= maxAttempts; attempt++) - { - var idTask = Task.Run(() => _service.ReadIdAsync(channel)); - if (!idTask.Wait(extendedTimeout)) - { - if (attempt == maxAttempts) - { - string buf = _service.GetLastBuffer(); - string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; - ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} ID 대기 타임아웃)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); - return null; - } - continue; - } - - rawId = idTask.Result; - if (string.IsNullOrEmpty(rawId)) - { - if (attempt == maxAttempts) - { - string buf = _service.GetLastBuffer(); - string displayBuf = string.IsNullOrEmpty(buf) ? "수신된 데이터 없음" : buf; - ErrorMessage?.Invoke(this, $"통신 실패 (4253 보드 CH{channel} ID 응답 없거나 파싱 오류)\r\n[송신값]: {idCmd}\r\n[수신값]: {displayBuf}"); - return null; - } - continue; - } - - // 정상적으로 파싱된 경우, 끝자리가 'F'인지 확인 - if (rawId.EndsWith("F", StringComparison.OrdinalIgnoreCase)) - { - if (attempt < maxAttempts) - { - ProgressMessage?.Invoke(this, $"ID 읽기 재시도 중... ({attempt}/{maxAttempts})"); - Task.Delay(350).Wait(); // 재시도 전 약간의 딜레이 - continue; - } - else - { - ProgressMessage?.Invoke(this, $"최대 재시도(2회 추가) 초과. 끝자리가 F인 ID({rawId})를 사용합니다."); - } - } - - // 제대로 된 값을 얻었거나 최대 횟수에 도달하면 루프 탈출 - break; - } - - // 3. SensorIdData 객체 구성 (16자리 ID를 각 필드에 적절히 분배) - // 신규 보드는 16자리 전체가 ID이므로, 파싱 로직 없이 통째로 넣거나 - // 특정 규칙이 있다면 여기서 분할함. - var data = new SensorIdData - { - LowID = rawId, - ID = rawId, // 16자리 전체를 ID로 사용 - Serial = "", // 시리얼 번호는 현재 존재하지 않으므로 강제로 파싱하지 않음 - Item = "N/A", - PrevResult = "F" // '불량제품 투입' 필터를 통과하기 위한 강제 초기화 - }; - - ProgressMessage?.Invoke(this, "ID 읽기 성공"); - return data; - } - catch (Exception ex) - { - ErrorMessage?.Invoke(this, $"4253 보드 읽기 중 예외 발생: {ex.Message}"); - return null; - } - } - - public void Dispose() - { - // 공유 서비스(Board4253Service)는 외부(HomeViewModel)에서 관리하므로 여기서 해제하지 않음 - } - } -} diff --git a/leak_test_project/Services/Board4253Service.cs b/leak_test_project/Services/Board4253Service.cs deleted file mode 100644 index 81ef4e5..0000000 --- a/leak_test_project/Services/Board4253Service.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using leak_test_project.Infrastructure; -using leak_test_project.Utils; - -namespace leak_test_project.Services -{ - /// - /// 신규 4253 보드와의 통신을 관리하는 서비스. - /// 키워드를 기준으로 데이터를 수신하며, 상태 확인 및 ID 읽기 기능을 제공함. - /// - public class Board4253Service : IDisposable - { - private readonly ICommunication _communication; - private readonly StringBuilder _receiveBuffer = new StringBuilder(); - private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); - private TaskCompletionSource _responseTcs; - public int TimeoutMs { get; set; } = 5000; // 보드가 응답을 주는데 2초 이상 걸리므로 무조건 길게 대기 - private bool _shouldBeConnected = false; - private System.Timers.Timer _reconnectTimer; - - public event EventHandler ErrorOccurred; - public event EventHandler ConnectionChanged; - - public string LastResponse { get; private set; } = ""; - - public string GetLastBuffer() - { - string currentBuf = _receiveBuffer.ToString().Trim(); - return string.IsNullOrEmpty(currentBuf) ? LastResponse : currentBuf; - } - - public Board4253Service(ICommunication communication) - { - _communication = communication; - _communication.DataReceived += OnDataReceived; - _communication.ConnectionStatusChanged += (s, isConnected) => { - ConnectionChanged?.Invoke(this, isConnected); - if (!isConnected && _shouldBeConnected) StartReconnectTimer(); - }; - - _reconnectTimer = new System.Timers.Timer(1000); - _reconnectTimer.AutoReset = false; - _reconnectTimer.Elapsed += (s, e) => { - if (_shouldBeConnected && !_communication.IsOpen) - { - if (!_communication.Open()) - { - if (_shouldBeConnected) _reconnectTimer.Start(); - } - } - else if (_shouldBeConnected) - { - _reconnectTimer.Start(); - } - }; - } - - private void StartReconnectTimer() - { - if (_reconnectTimer != null && !_reconnectTimer.Enabled) _reconnectTimer.Start(); - } - - public bool Connect() - { - _shouldBeConnected = true; - bool opened = _communication.Open(); - if (!opened) StartReconnectTimer(); - return opened; - } - - public void Disconnect() - { - _shouldBeConnected = false; - _reconnectTimer?.Stop(); - _communication.Close(); - } - - /// - /// 4253 보드의 상태를 확인합니다. - /// - /// 성공 여부 - public async Task CheckStatusAsync(int channel = 1) - { - string response = await SendCommandAsync($"x00c_00{channel}101:owt28006727ea97c7801\r\n"); - - if (response == null) - { - return false; - } - - if (response.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0) - { - return true; - } - - if (response.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) >= 0) - { - return false; - } - - // Success도 Fail도 없는 알 수 없는 응답인 경우 - return false; - } - - /// - /// 4253 보드로부터 16자리 ID를 읽어옵니다. - /// - /// 16자리 ID 문자열, 실패 시 null - public async Task ReadIdAsync(int channel = 1) - { - string response = await SendCommandAsync($"x00c_00{channel}101:ow2800326003e\r\n"); - - if (response == null) - { - return null; - } - - if (response.Contains("Fail")) - { - return null; - } - - string id = ExtractId(response); - if (string.IsNullOrEmpty(id)) - { - return null; - } - - return id; - } - - private string ExtractId(string response) - { - if (string.IsNullOrEmpty(response)) return null; - - // 보드 응답에는 보낸 명령어가 에코되어 포함되므로, 줄바꿈으로 나누어 실제 데이터 라인을 찾음 - string[] lines = response.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); - - foreach (var line in lines) - { - string trimmed = line.Trim(); - if (trimmed.Contains("x00c_") || trimmed.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0 || trimmed.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0) - continue; - - // 16자리 영숫자 ID 추출 - var match = System.Text.RegularExpressions.Regex.Match(trimmed, @"[A-Za-z0-9]{16}"); - if (match.Success) return match.Value; - } - - return null; - } - - public async Task SendCommandAsync(string command) - { - await _lock.WaitAsync(); - try - { - int retryCount = 0; - while (retryCount <= 3) - { - if (!_communication.IsOpen) - { - if (!_communication.Open()) - { - retryCount++; - await Task.Delay(300); - continue; - } - } - - _receiveBuffer.Clear(); - LastResponse = ""; - _responseTcs = new TaskCompletionSource(); - - _communication.ClearBuffer(); // 이전에 남아있던 패킷 조각 완벽히 제거 - - _communication.Write(command); - - using (var cts = new CancellationTokenSource(TimeoutMs)) - { - cts.Token.Register(() => _responseTcs.TrySetCanceled()); - try - { - return await _responseTcs.Task; - } - catch (OperationCanceledException) - { - retryCount++; - string currentBuffer = _receiveBuffer.ToString().Trim(); - FileLogger.Log("WARNING", $"[Board4253] Timeout waiting for response (Retry {retryCount}/3). Command: {command.Trim()}, ReceivedSoFar: {currentBuffer}"); - _receiveBuffer.Clear(); - if (retryCount <= 3) await Task.Delay(300); - } - } - } - - FileLogger.Log("ERROR", $"[Board4253] Failed to receive response after 3 retries: {command.Trim()}"); - return null; - } - finally - { - _responseTcs = null; - _lock.Release(); - } - } - - private void OnDataReceived(object sender, string data) - { - _receiveBuffer.Append(data); - string currentContent = _receiveBuffer.ToString(); - - bool isComplete = false; - - if (currentContent.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0 || - currentContent.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0 || - currentContent.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) >= 0 || - currentContent.IndexOf("OFF", StringComparison.OrdinalIgnoreCase) >= 0) - { - isComplete = true; - } - else - { - // 가 오지 않았더라도, 명령어 에코가 아닌 줄에서 16자리 ID를 발견하면 즉시 완료 처리 - string[] lines = currentContent.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); - foreach (var line in lines) - { - string trimmed = line.Trim(); - if (trimmed.Contains("x00c_")) continue; - - var match = System.Text.RegularExpressions.Regex.Match(trimmed, @"[A-Za-z0-9]{16}"); - if (match.Success) - { - isComplete = true; - break; - } - } - } - - if (isComplete) - { - if (_responseTcs != null && !_responseTcs.Task.IsCompleted) - { - LastResponse = currentContent.Trim(); - _responseTcs.TrySetResult(currentContent); - } - _receiveBuffer.Clear(); - } - } - - public void Dispose() - { - Disconnect(); - _communication.DataReceived -= OnDataReceived; - _reconnectTimer?.Dispose(); - _lock.Dispose(); - } - } -} diff --git a/leak_test_project/Services/SentinelC28Service.cs b/leak_test_project/Services/SentinelC28Service.cs index f0cbb57..b6862a5 100644 --- a/leak_test_project/Services/SentinelC28Service.cs +++ b/leak_test_project/Services/SentinelC28Service.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Timers; using leak_test_project.Infrastructure; using leak_test_project.Utils; @@ -42,6 +43,7 @@ namespace leak_test_project.Services _reconnectTimer.Elapsed += (s, e) => { if (_shouldBeConnected && !_communication.IsOpen) { + Debug.WriteLine($"[SentinelC28 Service] Attempting to reconnect to {_communication.Name}..."); Console.WriteLine($"[Service] Attempting to reconnect to {_communication.Name}..."); if (!_communication.Open()) { @@ -86,7 +88,10 @@ namespace leak_test_project.Services string payload = $"{sequenceHex}{lengthHex} {dataTypeCode}\t{command}"; string crc = SentinelCrc8.CalculateHex(payload); - if (!_communication.Write($"{crc}{payload}\r\n")) + string fullCommand = $"{crc}{payload}\r\n"; + Debug.WriteLine($"[SentinelC28 Service] Sending Command: {fullCommand.TrimEnd()}"); + + if (!_communication.Write(fullCommand)) { FileLogger.Log("ERROR", "[SentinelC28] Failed to send command: Communication channel closed."); } @@ -97,33 +102,58 @@ namespace leak_test_project.Services } } + private string _receiveBuffer = string.Empty; + private void OnDataReceived(object sender, string rawData) { try { + Debug.WriteLine($"[SentinelC28 Service] Raw Data Received: {rawData.Replace("\r", "\\r").Replace("\n", "\\n").Replace("\t", "\\t")}"); RawDataReceived?.Invoke(this, rawData); - if (string.IsNullOrWhiteSpace(rawData)) return; - - // 헤더 분석 및 본문 추출 - string body = SentinelParser.ExtractBody(rawData, out char typeCode); + if (string.IsNullOrEmpty(rawData)) return; - switch (typeCode) + _receiveBuffer += rawData; + + // \n 또는 \r을 기준으로 프레임을 나눔 + while (_receiveBuffer.Contains("\n") || _receiveBuffer.Contains("\r")) { - case 'R': // 최종 결과 (Result Value) - ResultReceived?.Invoke(this, rawData); - var finalParsed = SentinelParser.ParseFinalResult(rawData); - OnFinalResultParsed?.Invoke(this, finalParsed); - break; - - case 'S': // 스트리밍 데이터 (Streaming Value) - StreamingReceived?.Invoke(this, rawData); - var streamParsed = SentinelParser.ParseStreamingValue(rawData); - OnStreamingParsed?.Invoke(this, streamParsed); - break; - - case 'M': // 일반 메시지 - FileLogger.Log("INFO", $"[SentinelC28 Message] {body}"); - break; + int indexN = _receiveBuffer.IndexOf('\n'); + int indexR = _receiveBuffer.IndexOf('\r'); + + int splitIndex = -1; + if (indexN >= 0 && indexR >= 0) splitIndex = Math.Min(indexN, indexR); + else if (indexN >= 0) splitIndex = indexN; + else splitIndex = indexR; + + string frame = _receiveBuffer.Substring(0, splitIndex); + + // 버퍼 갱신 (처리한 구분자까지 잘라냄) + _receiveBuffer = _receiveBuffer.Substring(splitIndex + 1); + + // 빈 문자열이거나 단순히 \r\n 이 연속되어 발생하는 빈 프레임이면 무시 + if (string.IsNullOrWhiteSpace(frame)) continue; + + // 헤더 분석 및 본문 추출 + string body = SentinelParser.ExtractBody(frame, out char typeCode); + + switch (typeCode) + { + case 'R': // 최종 결과 (Result Value) + ResultReceived?.Invoke(this, frame); + var finalParsed = SentinelParser.ParseFinalResult(frame); + OnFinalResultParsed?.Invoke(this, finalParsed); + break; + + case 'S': // 스트리밍 데이터 (Streaming Value) + StreamingReceived?.Invoke(this, frame); + var streamParsed = SentinelParser.ParseStreamingValue(frame); + OnStreamingParsed?.Invoke(this, streamParsed); + break; + + case 'M': // 일반 메시지 + FileLogger.Log("INFO", $"[SentinelC28 Message] {body}"); + break; + } } } catch (Exception ex) { FileLogger.Log("ERROR", $"[SentinelC28] Error parsing received data: {ex.Message}"); diff --git a/leak_test_project/Services/TestProcessService.cs b/leak_test_project/Services/TestProcessService.cs index b997248..c2a819e 100644 --- a/leak_test_project/Services/TestProcessService.cs +++ b/leak_test_project/Services/TestProcessService.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading; using leak_test_project.Infrastructure; using leak_test_project.Models; @@ -218,7 +219,14 @@ namespace leak_test_project.Services NotifyProgress(testIndex, "시험 시작", "LightSeaGreen"); ResultClearRequested?.Invoke(this, testIndex); + // C28 LEAK 센서 수신 이벤트를 초기화 (ZMDI 통신 중 일찍 날아오는 Auto Result를 놓치지 않기 위함) + var resultReady = isLeft ? _leftResultReady : _rightResultReady; + resultReady.Reset(); + if (isLeft) _leftResult = null; + else _rightResult = null; + // === 1단계: 센서 정보 읽기 === + Debug.WriteLine($"[TestProcess-{side}] Phase 1: 센서 정보 읽기 시작"); NotifyProgress(testIndex, "센서 정보 읽는 중", "LightSeaGreen"); var sensor = isLeft ? _leftSensor : _rightSensor; var sensorData = sensor.ReadSensor(); @@ -235,6 +243,7 @@ namespace leak_test_project.Services } // === 2단계: 불량 제품 필터링 === + Debug.WriteLine($"[TestProcess-{side}] Phase 2: 센서 ID ({sensorData?.ID}), 이전결과 ({sensorData?.PrevResult}) 필터링 확인"); if (sensorData.PrevResult != "F") { NotifyError(testIndex, "불량 제품 투입 (이전 결과: " + sensorData.PrevResult + ")"); @@ -242,20 +251,16 @@ namespace leak_test_project.Services } // === 3단계: LEAK 시험 수행 === + Debug.WriteLine($"[TestProcess-{side}] Phase 3: LEAK 시험 대기 시작 (최대 30초)"); NotifyProgress(testIndex, "LEAK 시험중", "LightYellow"); - // 결과 대기 리셋 - var resultReady = isLeft ? _leftResultReady : _rightResultReady; - resultReady.Reset(); - if (isLeft) _leftResult = null; - else _rightResult = null; - // C28에서 결과를 수신할 때까지 대기 (레거시와 동일하게 30초 설정) bool received = resultReady.Wait(TimeSpan.FromSeconds(30)); if (!received) { - NotifyError(testIndex, "LEAK 센서 통신 타임아웃"); + Debug.WriteLine($"[TestProcess-{side}] ERROR: Sentinel C28 통신 타임아웃 발생!"); + NotifyError(testIndex, "Sentinel C28 통신 타임아웃"); _dioBoard.WriteOutput(side + "_NG", true); // 타임아웃 시에도 UI(그리드)에 NG로 기록되도록 이벤트 발생 @@ -302,15 +307,18 @@ namespace leak_test_project.Services double specLL = ConfigManager.Current.SpecLL; string programJudgment = SentinelParser.EvaluateJudgment(leakResult.MeasuredValue, specUL, specLL); string sensorJudgment = leakResult.SensorJudgment ?? leakResult.Judgment ?? "-"; + + Debug.WriteLine($"[TestProcess-{side}] Phase 4: 프로그램 판정={programJudgment}, 센서자체판정={sensorJudgment}, 측정값={leakResult.MeasuredValue}"); // === 5단계: SPEC 교차 검증 === + Debug.WriteLine($"[TestProcess-{side}] Phase 5: SPEC 교차 검증 수행"); bool specMismatch = false; string normalizedProgram = (programJudgment == "OK") ? "A" : "R"; string normalizedSensor = sensorJudgment; - // 센서 판정이 Accept/Reject 형식인 경우 - if (sensorJudgment == "OK" || sensorJudgment == "A") normalizedSensor = "A"; - else if (sensorJudgment == "NG" || sensorJudgment == "R") normalizedSensor = "R"; + // 센서 판정이 Accept/Reject, Pass/Fail, OK/NG 형식인 경우 모두 A/R로 정규화 + if (sensorJudgment == "OK" || sensorJudgment == "A" || sensorJudgment == "P") normalizedSensor = "A"; + else if (sensorJudgment == "NG" || sensorJudgment == "R" || sensorJudgment == "F") normalizedSensor = "R"; if (normalizedProgram != normalizedSensor && normalizedSensor != "-") { @@ -368,6 +376,19 @@ namespace leak_test_project.Services private void OnSentinelFinalResult(object sender, ParsedData data) { + // 레거시 포맷 등 ChannelNo가 없는 경우, 현재 LEAK 대기 중인 채널로 할당 + if (string.IsNullOrEmpty(data.ChannelNo)) + { + if (!_leftResultReady.IsSet && _leftResult == null) + { + data.ChannelNo = "C01"; + } + else if (!_rightResultReady.IsSet && _rightResult == null) + { + data.ChannelNo = "C02"; + } + } + // ChannelNo(C01=LEFT, C02=RIGHT)에 따라 결과 라우팅 if (data.ChannelNo == "C01" || data.ChannelNo == "1") { diff --git a/leak_test_project/Services/ZmdiSensorService.cs b/leak_test_project/Services/ZmdiSensorService.cs deleted file mode 100644 index 4c53e39..0000000 --- a/leak_test_project/Services/ZmdiSensorService.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using System.Timers; -using leak_test_project.Infrastructure; -using leak_test_project.Models; -using leak_test_project.Utils; - -namespace leak_test_project.Services -{ - /// - /// ZMDI 센서와 시리얼 통신하여 제품 ID를 읽고 파싱하는 서비스. - /// 자동 재연결(Auto-Reconnect) 및 예외 처리 포함. - /// - public class ZmdiSensorService : IIdSensorService - { - private readonly ICommunication _comm; - private readonly object _commSync = new object(); - private readonly int _sensorIndex; // 0=LEFT, 1=RIGHT - - private System.Timers.Timer _reconnectTimer; - private bool _shouldBeConnected = false; - - // 레거시 명령 시퀀스 (ClsSensorReader의 commandList1~4) 그대로 유지 - private readonly string[] _commandList1 = { "V", "Pr_D7", "Pr_D6", "Pr_D5", "r" }; - private readonly string[] _commandList2 = { "tso31150" }; - private readonly string[] _commandList3 = { "os_10", "t11005", "OWT7800272D1", "OR_78002", - "OW_780038AA55A", "OW_780011A", "OR_78002", "OW_780038AFF00", "OW_78001CF", "OR_78004" }; - private readonly string[] _commandList4 = { "OW_7800140", "OR_78002", "OW_7800141", - "OR_78002", "OW_7800142", "OR_78002", "x9c_990:x" }; - - // 년/월/일 디코딩 테이블 (레거시 그대로) - private readonly string[] _yearHexList = { - "49","4A","4B","4C","4D","4E","4F","50","51","52","53","54","55","56","57","58","59","5A" - }; - private readonly string[] _yearIdList = { - "I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z" - }; - private readonly string[] _monthList = { "1","2","3","4","5","6","7","8","9","A","B","C" }; - private readonly string[] _dayHexList = { - "41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F","50", - "51","52","53","54","55","56","57","58","59","5A","31","32","33","34","35" - }; - private readonly string[] _dayIdList = { - "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P", - "Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5" - }; - - /// 진행 상황 메시지 이벤트 - public event EventHandler ProgressMessage; - - /// 오류 메시지 이벤트 - public event EventHandler ErrorMessage; - - /// 연결 상태 변경 이벤트 - public event EventHandler ConnectionChanged; - - public ZmdiSensorService(ICommunication communication, int sensorIndex) - { - _comm = communication; - _sensorIndex = sensorIndex; - - _comm.ConnectionStatusChanged += (s, isConnected) => { - ConnectionChanged?.Invoke(this, isConnected); - if (!isConnected && _shouldBeConnected) StartReconnectTimer(); - }; - - // 1초(1000ms)마다 연결 상태를 확인하고 재연결 시도 - _reconnectTimer = new System.Timers.Timer(1000); - _reconnectTimer.AutoReset = false; // 재진입 방지 - _reconnectTimer.Elapsed += (s, e) => { - if (_shouldBeConnected && !_comm.IsOpen) - { - if (!_comm.Open()) - { - if (_shouldBeConnected) _reconnectTimer.Start(); - } - } - else if (_shouldBeConnected) - { - _reconnectTimer.Start(); - } - }; - } - - public bool Connect() - { - _shouldBeConnected = true; - bool opened = _comm.Open(); - if (!opened) StartReconnectTimer(); - return opened; - } - - public void Disconnect() - { - _shouldBeConnected = false; - _reconnectTimer?.Stop(); - _comm.Close(); - } - - private void StartReconnectTimer() - { - if (_reconnectTimer != null && !_reconnectTimer.Enabled) _reconnectTimer.Start(); - } - - public void Dispose() - { - Disconnect(); - _reconnectTimer?.Dispose(); - _reconnectTimer = null; - } - - /// - /// ZMDI 센서에서 제품 ID를 읽고 파싱합니다. - /// 반드시 백그라운드 스레드에서 호출해야 합니다. - /// - /// 성공 시 SensorIdData, 실패 시 null - public SensorIdData ReadSensor() - { - string recvData = ""; - try - { - var data = new SensorIdData(); - - // 1단계: 초기 명령 실행 - ProgressMessage?.Invoke(this, "ZMDI 초기화 중..."); - for (int i = 0; i < _commandList1.Length; i++) - { - if (!ExecuteCommand(_commandList1[i], ref recvData)) - { - ErrorMessage?.Invoke(this, $"통신 실패 (cmd1: ZMDI 센서 초기화 및 통신 확인 단계)\r\n[송신값]: {_commandList1[i]}\r\n[수신값]: {recvData.Trim()}"); - return null; - } - } - - Thread.Sleep(1000); - - // 2단계 - ProgressMessage?.Invoke(this, "ZMDI 메모리 준비 중..."); - for (int i = 0; i < _commandList2.Length; i++) - { - if (!ExecuteCommand(_commandList2[i], ref recvData)) - { - ErrorMessage?.Invoke(this, $"통신 실패 (cmd2: ZMDI 메모리 접근 준비 단계)\r\n[송신값]: {_commandList2[i]}\r\n[수신값]: {recvData.Trim()}"); - return null; - } - } - - Thread.Sleep(200); - - // 3단계 - ProgressMessage?.Invoke(this, "ZMDI 데이터 수집 중..."); - for (int i = 0; i < _commandList3.Length; i++) - { - if (!ExecuteCommand(_commandList3[i], ref recvData)) - { - ErrorMessage?.Invoke(this, $"통신 실패 (cmd3: ZMDI 데이터 수집 설정 단계)\r\n[송신값]: {_commandList3[i]}\r\n[수신값]: {recvData.Trim()}"); - return null; - } - } - - // 4단계: ID 메모리 읽기 (3회) - ProgressMessage?.Invoke(this, "ZMDI ID 읽기 중..."); - // 첫 번째 ID 레지스터 - if (!ExecuteCommand(_commandList4[0], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 단계-1)\r\n[송신값]: {_commandList4[0]}\r\n[수신값]: {recvData.Trim()}"); return null; } - if (!ExecuteCommand(_commandList4[1], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 단계-2)\r\n[송신값]: {_commandList4[1]}\r\n[수신값]: {recvData.Trim()}"); return null; } - data.LowID = recvData.Length > 1 ? recvData.Substring(1).Replace("\r", "").Replace("\n", "") : ""; - - // 두 번째 ID 레지스터 - if (!ExecuteCommand(_commandList4[2], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 단계-3)\r\n[송신값]: {_commandList4[2]}\r\n[수신값]: {recvData.Trim()}"); return null; } - if (!ExecuteCommand(_commandList4[3], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 단계-4)\r\n[송신값]: {_commandList4[3]}\r\n[수신값]: {recvData.Trim()}"); return null; } - data.LowID += recvData.Length > 1 ? recvData.Substring(1).Replace("\r", "").Replace("\n", "") : ""; - - // 세 번째 ID 레지스터 - if (!ExecuteCommand(_commandList4[4], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 단계-5)\r\n[송신값]: {_commandList4[4]}\r\n[수신값]: {recvData.Trim()}"); return null; } - if (!ExecuteCommand(_commandList4[5], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 단계-6)\r\n[송신값]: {_commandList4[5]}\r\n[수신값]: {recvData.Trim()}"); return null; } - data.LowID += recvData.Length > 1 ? recvData.Substring(1).Replace("\r", "").Replace("\n", "") : ""; - - // 마지막 명령 실행 - if (!ExecuteCommand(_commandList4[6], ref recvData)) { ErrorMessage?.Invoke(this, $"통신 실패 (ID 읽기 종료 단계)\r\n[송신값]: {_commandList4[6]}\r\n[수신값]: {recvData.Trim()}"); return null; } - - // ID 파싱 - if (!ParseLowId(data)) - return null; - - return data; - } - catch (Exception ex) - { - FileLogger.Log("ERROR", $"[ZMDI] ReadSensor exception: {ex.Message}"); - ErrorMessage?.Invoke(this, $"센서 데이터 오류 (Exception: {ex.Message})\r\n[수신값]: {recvData.Trim()}"); - return null; - } - } - - /// LowID 문자열을 파싱하여 년/월/일/시리얼/라인/항목을 추출합니다. - private bool ParseLowId(SensorIdData data) - { - if (string.IsNullOrEmpty(data.LowID) || data.LowID.Length < 12) - { - ErrorMessage?.Invoke(this, $"센서 데이터 길이 부족 (길이: {data.LowID?.Length ?? 0})\r\n[수신값]: {data.LowID}"); - return false; - } - - string yearHex = data.LowID.Substring(0, 2); - string yearId = ""; - data.Year = 0; - for (int i = 0; i < _yearHexList.Length; i++) - { - if (yearHex == _yearHexList[i]) - { - data.Year = 2013 + i; - yearId = _yearIdList[i]; - break; - } - } - if (data.Year <= 0) { ErrorMessage?.Invoke(this, $"센서 데이터 오류(년도 파싱 불가)\r\n[수신값]: {data.LowID}"); return false; } - - string monthChar = data.LowID.Substring(2, 1); - string monthId = ""; - data.Month = 0; - for (int i = 0; i < _monthList.Length; i++) - { - if (monthChar == _monthList[i]) - { - data.Month = 1 + i; - monthId = monthChar; - break; - } - } - if (data.Month <= 0) { ErrorMessage?.Invoke(this, $"센서 데이터 오류(월 파싱 불가)\r\n[수신값]: {data.LowID}"); return false; } - - string dayHex = data.LowID.Substring(3, 2); - string dayId = ""; - data.Day = 0; - for (int i = 0; i < _dayHexList.Length; i++) - { - if (dayHex == _dayHexList[i]) - { - data.Day = 1 + i; - dayId = _dayIdList[i]; - break; - } - } - if (data.Day <= 0) { ErrorMessage?.Invoke(this, $"센서 데이터 오류(일 파싱 불가)\r\n[수신값]: {data.LowID}"); return false; } - - // 시리얼 번호 계산 (16진수 → 10진수) - string serialHigh = data.LowID.Substring(5, 2); - string serialLow = data.LowID.Substring(7, 2); - int high = Convert.ToInt32(serialHigh, 16) << 8; - int low = Convert.ToInt32(serialLow, 16); - data.Serial = (high + low).ToString().PadLeft(5, '0'); - - // MC Line 및 라인 번호 (비트 단위 파싱) - string nibble = data.LowID.Substring(9, 1); - string binary = Convert.ToString(Convert.ToInt32(nibble, 16), 2).PadLeft(4, '0'); - data.McLine = binary.Substring(0, 1); - string lineNoBits = binary.Substring(1, 3); - data.LineNo = Convert.ToInt32(lineNoBits, 2).ToString(); - - // PrevResult (이전 검사 결과) - data.PrevResult = data.LowID.Substring(10, 1); - - // 제품 Item - data.Item = data.LowID.Substring(11, 1); - - // 최종 ID 조합 - data.ID = yearId + monthId + dayId + data.Serial + data.LineNo + data.Item; - - return true; - } - - /// 명령을 전송하고 응답을 수신합니다. 최대 3회 재시도. - private bool ExecuteCommand(string sendCommand, ref string recvData) - { - lock (_commSync) - { - int retryCount = 0; - while (true) - { - if (SendCommandWaitResponse(sendCommand, ref recvData)) - return true; - - Thread.Sleep(300); - retryCount++; - if (retryCount > 3) - return false; - } - } - } - - /// 명령 전송 후 CR+LF 응답을 500ms 타임아웃으로 대기합니다. - private bool SendCommandWaitResponse(string sendCommand, ref string recvData) - { - string fullCommand = sendCommand + "\r\n"; - - if (!_comm.IsOpen) - { - if (!_comm.Open()) - return false; - } - - recvData = ""; - - // 핵심 버그 수정: 이전 명령어의 잔류 응답 데이터 비우기 - _comm.ClearBuffer(); - - // 500ms 타임아웃으로 응답 대기 (레거시 동일) - DateTime deadline = DateTime.Now.AddMilliseconds(500); - - // 동기식 수신을 위한 임시 버퍼 - string buffer = ""; - EventHandler handler = null; - handler = (s, data) => { buffer += data; }; - - // 핵심 버그 수정: 명령어를 쓰기 '전에' 이벤트 핸들러를 먼저 등록하여 아주 빠른 응답 유실 방지 - _comm.DataReceived += handler; - - bool success = false; - try - { - if (!_comm.Write(fullCommand)) - return false; - - while (DateTime.Now < deadline) - { - Thread.Sleep(2); - if (buffer.IndexOf("\r\n") >= 0) - { - recvData = buffer; - success = true; - break; - } - } - - // 타임아웃 발생 시에도 현재까지 수신된 버퍼 내용을 반환 (디버깅용) - if (!success) - recvData = buffer; - - return success; - } - finally - { - _comm.DataReceived -= handler; - } - } - } -} diff --git a/leak_test_project/Utils/DioConfigParser.cs b/leak_test_project/Utils/ConfigHelper.cs similarity index 76% rename from leak_test_project/Utils/DioConfigParser.cs rename to leak_test_project/Utils/ConfigHelper.cs index da83875..1ee3a9e 100644 --- a/leak_test_project/Utils/DioConfigParser.cs +++ b/leak_test_project/Utils/ConfigHelper.cs @@ -1,10 +1,65 @@ using System; using System.Collections.Generic; using System.IO; +using System.Xml.Serialization; using leak_test_project.Models; namespace leak_test_project.Utils { + public static class ConfigManager + { + private static readonly string ConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.xml"); + public static AppConfig Current { get; private set; } = new AppConfig(); + public static event EventHandler ConfigChanged; + + static ConfigManager() + { + Load(); + } + + public static void Load() + { + try + { + if (File.Exists(ConfigPath)) + { + XmlSerializer serializer = new XmlSerializer(typeof(AppConfig)); + using (FileStream fs = new FileStream(ConfigPath, FileMode.Open)) + { + Current = (AppConfig)serializer.Deserialize(fs); + } + } + else + { + Current = new AppConfig(); + Save(); // 초기 파일 생성 + } + } + catch (Exception ex) + { + Console.WriteLine($"[Config] Error loading config: {ex.Message}"); + Current = new AppConfig(); + } + } + + public static void Save() + { + try + { + XmlSerializer serializer = new XmlSerializer(typeof(AppConfig)); + using (FileStream fs = new FileStream(ConfigPath, FileMode.Create)) + { + serializer.Serialize(fs, Current); + } + ConfigChanged?.Invoke(null, EventArgs.Empty); + } + catch (Exception ex) + { + Console.WriteLine($"[Config] Error saving config: {ex.Message}"); + } + } + } + public class DioConfigParser { public string CompanyName { get; set; } = "ADLink"; diff --git a/leak_test_project/Utils/ConfigManager.cs b/leak_test_project/Utils/ConfigManager.cs deleted file mode 100644 index 39bebbe..0000000 --- a/leak_test_project/Utils/ConfigManager.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.IO; -using System.Xml.Serialization; -using leak_test_project.Models; - -namespace leak_test_project.Utils -{ - public static class ConfigManager - { - private static readonly string ConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.xml"); - public static AppConfig Current { get; private set; } = new AppConfig(); - public static event EventHandler ConfigChanged; - - static ConfigManager() - { - Load(); - } - - public static void Load() - { - try - { - if (File.Exists(ConfigPath)) - { - XmlSerializer serializer = new XmlSerializer(typeof(AppConfig)); - using (FileStream fs = new FileStream(ConfigPath, FileMode.Open)) - { - Current = (AppConfig)serializer.Deserialize(fs); - } - } - else - { - Current = new AppConfig(); - Save(); // 초기 파일 생성 - } - } - catch (Exception ex) - { - Console.WriteLine($"[Config] Error loading config: {ex.Message}"); - Current = new AppConfig(); - } - } - - public static void Save() - { - try - { - XmlSerializer serializer = new XmlSerializer(typeof(AppConfig)); - using (FileStream fs = new FileStream(ConfigPath, FileMode.Create)) - { - serializer.Serialize(fs, Current); - } - ConfigChanged?.Invoke(null, EventArgs.Empty); - } - catch (Exception ex) - { - Console.WriteLine($"[Config] Error saving config: {ex.Message}"); - } - } - } -} diff --git a/leak_test_project/Utils/CsvExporter.cs b/leak_test_project/Utils/CsvExporter.cs deleted file mode 100644 index 6e2e6c2..0000000 --- a/leak_test_project/Utils/CsvExporter.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text; -using System.Windows; - -namespace leak_test_project.Utils -{ - /// - /// 데이터를 CSV 파일로 내보내는 유틸리티 - /// - public static class CsvExporter - { - /// - /// 컬렉션 데이터를 CSV 파일로 저장함 - /// - /// 데이터 모델 클래스 - /// 내보낼 데이터 목록 - /// 저장할 파일 경로 - public static bool ExportToCsv(IEnumerable items, string filePath) - { - try - { - var sb = new StringBuilder(); - var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); - - // Header - foreach (var prop in props) - { - sb.Append(prop.Name).Append(","); - } - sb.AppendLine(); - - // Body - foreach (var item in items) - { - foreach (var prop in props) - { - var val = prop.GetValue(item, null); - var str = val?.ToString() ?? ""; - if (str.Contains(",") || str.Contains("\"") || str.Contains("\n")) - sb.Append($"\"{str.Replace("\"", "\"\"")}\""); - else - sb.Append(str); - sb.Append(","); - } - sb.AppendLine(); - } - - File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); - return true; - } - catch (Exception ex) - { - MessageBox.Show($"CSV 저장 실패: {ex.Message}"); - return false; - } - } - } -} diff --git a/leak_test_project/Utils/FileLogger.cs b/leak_test_project/Utils/FileLogger.cs deleted file mode 100644 index 53e2f54..0000000 --- a/leak_test_project/Utils/FileLogger.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.IO; -using System.Text; -using leak_test_project.Models; - -namespace leak_test_project.Utils -{ - /// - /// 통신 로그 및 시스템 이력을 파일로 저장하는 유틸리티 - /// - public static class FileLogger - { - private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); - private static readonly object _lock = new object(); - - /// - /// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함 - /// - public static void LogInspectData(InspectData data) - { - lock (_lock) - { - try - { - if (!Directory.Exists(LogDirectory)) - Directory.CreateDirectory(LogDirectory); - - string dateStr = DateTime.Now.ToString("yyyy-MM-dd"); - string filePath = Path.Combine(LogDirectory, $"{dateStr}.csv"); - - bool isNewFile = !File.Exists(filePath); - - // CSV 헤더: Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest - if (isNewFile) - { - string header = "Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest" + Environment.NewLine; - File.WriteAllText(filePath, header, Encoding.UTF8); - } - - string csvLine = $"{Esc(data.InspectDate)},{Esc(data.InspectTime)},{Esc(data.Channel)},{Esc(data.ProductId)}," + - $"{Esc(data.MeasuredValue)},{Esc(data.Judgment)},{Esc(data.Mode)},{Esc(data.LineNo)}," + - $"{Esc(data.ProductType)},{Esc(data.SpecUL)},{Esc(data.SpecLL)},{Esc(data.Retest)}{Environment.NewLine}"; - - File.AppendAllText(filePath, csvLine, Encoding.UTF8); - } - catch (Exception ex) - { - Console.WriteLine($"[FileLogger Error] {ex.Message}"); - } - } - } - - /// - /// 단순 텍스트 로그 (기존 호환성 유지용) - /// - public static void Log(string tag, string message) - { - lock (_lock) - { - try - { - if (!Directory.Exists(LogDirectory)) - Directory.CreateDirectory(LogDirectory); - - string dateStr = DateTime.Now.ToString("yyyy-MM-dd"); - string filePath = Path.Combine(LogDirectory, $"{dateStr}_system.log"); - - string logEntry = $"[{DateTime.Now:HH:mm:ss.fff}] [{tag}] {message}{Environment.NewLine}"; - File.AppendAllText(filePath, logEntry, Encoding.UTF8); - } - catch (Exception ex) - { - Console.WriteLine($"[FileLogger Error] {ex.Message}"); - } - } - } - /// - /// CSV 값 이스케이프: 쉼표, 따옴표, 줄바꿈이 포함된 값을 안전하게 감싸줌 - /// - private static string Esc(string value) - { - if (string.IsNullOrEmpty(value)) return ""; - if (value.Contains(",") || value.Contains("\"") || value.Contains("\n")) - return $"\"{value.Replace("\"", "\"\"")}\""; - return value; - } - } -} diff --git a/leak_test_project/Utils/LogParser.cs b/leak_test_project/Utils/LogParser.cs deleted file mode 100644 index 8b3770c..0000000 --- a/leak_test_project/Utils/LogParser.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; -using leak_test_project.Models; - -namespace leak_test_project.Utils -{ - public static class LogParser - { - private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); - - public static List ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "") - { - var results = new List(); - - if (!Directory.Exists(LogDirectory)) - { - // 디렉토리가 없으면 빈 리스트 반환 - return results; - } - - // 날짜 범위 내의 모든 로그 파일 찾기 (.csv) - var files = Directory.GetFiles(LogDirectory, "*.csv") - .Where(f => { - string fileName = Path.GetFileNameWithoutExtension(f); - if (DateTime.TryParse(fileName, out DateTime fileDate)) - { - return fileDate.Date >= start.Date && fileDate.Date <= end.Date; - } - return false; - }); - - foreach (var file in files) - { - try - { - var lines = File.ReadAllLines(file); - if (lines.Length <= 1) continue; // Header only or empty - - // CSV 헤더: Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest - for (int i = 1; i < lines.Length; i++) - { - string line = lines[i]; - if (string.IsNullOrWhiteSpace(line)) continue; - var parts = line.Split(','); - if (parts.Length < 12) continue; - - string date = parts[0].Trim(); - string time = parts[1].Trim(); - string channel = parts[2].Trim(); - string id = parts[3].Trim(); - string val = parts[4].Trim(); - string judg = parts[5].Trim(); - string mode = parts[6].Trim(); - string lineNo = parts[7].Trim(); - string prodType = parts[8].Trim(); - string specUl = parts[9].Trim(); - string specLl = parts[10].Trim(); - string retest = parts[11].Trim(); - - // 필터 적용 - if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue; - if (!string.IsNullOrEmpty(serialFilter) && !id.Contains(serialFilter)) continue; - - results.Add(new InspectData - { - InspectDate = date, - InspectTime = time, - Channel = channel, - ProductId = id, - MeasuredValue = val, - Judgment = judg, - Mode = mode, - LineNo = lineNo, - ProductType = prodType, - SpecUL = specUl, - SpecLL = specLl, - Retest = retest - }); - } - } - catch (Exception ex) - { - Console.WriteLine($"[LogParser] Error reading file {file}: {ex.Message}"); - } - } - - return results.OrderByDescending(r => r.InspectDate).ThenByDescending(r => r.InspectTime).ToList(); - } - } -} diff --git a/leak_test_project/Utils/LogUtils.cs b/leak_test_project/Utils/LogUtils.cs new file mode 100644 index 0000000..1093ecc --- /dev/null +++ b/leak_test_project/Utils/LogUtils.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows; +using leak_test_project.Models; + +namespace leak_test_project.Utils +{ + /// + /// 통신 로그 및 시스템 이력을 파일로 저장하는 유틸리티 + /// + public static class FileLogger + { + private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); + private static readonly object _lock = new object(); + + /// + /// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함 + /// + public static void LogInspectData(InspectData data) + { + lock (_lock) + { + try + { + if (!Directory.Exists(LogDirectory)) + Directory.CreateDirectory(LogDirectory); + + string dateStr = DateTime.Now.ToString("yyyy-MM-dd"); + string filePath = Path.Combine(LogDirectory, $"{dateStr}.csv"); + + bool isNewFile = !File.Exists(filePath); + + // CSV 헤더: Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest + if (isNewFile) + { + string header = "Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest" + Environment.NewLine; + File.WriteAllText(filePath, header, Encoding.UTF8); + } + + string csvLine = $"{Esc(data.InspectDate)},{Esc(data.InspectTime)},{Esc(data.Channel)},{Esc(data.ProductId)}," + + $"{Esc(data.MeasuredValue)},{Esc(data.Judgment)},{Esc(data.Mode)},{Esc(data.LineNo)}," + + $"{Esc(data.ProductType)},{Esc(data.SpecUL)},{Esc(data.SpecLL)},{Esc(data.Retest)}{Environment.NewLine}"; + + File.AppendAllText(filePath, csvLine, Encoding.UTF8); + } + catch (Exception ex) + { + Console.WriteLine($"[FileLogger Error] {ex.Message}"); + } + } + } + + /// + /// 단순 텍스트 로그 (기존 호환성 유지용) + /// + public static void Log(string tag, string message) + { + lock (_lock) + { + try + { + if (!Directory.Exists(LogDirectory)) + Directory.CreateDirectory(LogDirectory); + + string dateStr = DateTime.Now.ToString("yyyy-MM-dd"); + string filePath = Path.Combine(LogDirectory, $"{dateStr}_system.log"); + + string logEntry = $"[{DateTime.Now:HH:mm:ss.fff}] [{tag}] {message}{Environment.NewLine}"; + File.AppendAllText(filePath, logEntry, Encoding.UTF8); + } + catch (Exception ex) + { + Console.WriteLine($"[FileLogger Error] {ex.Message}"); + } + } + } + /// + /// CSV 값 이스케이프: 쉼표, 따옴표, 줄바꿈이 포함된 값을 안전하게 감싸줌 + /// + private static string Esc(string value) + { + if (string.IsNullOrEmpty(value)) return ""; + if (value.Contains(",") || value.Contains("\"") || value.Contains("\n")) + return $"\"{value.Replace("\"", "\"\"")}\""; + return value; + } + } + + public static class LogParser + { + private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); + + public static List ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "") + { + var results = new List(); + + if (!Directory.Exists(LogDirectory)) + { + // 디렉토리가 없으면 빈 리스트 반환 + return results; + } + + // 날짜 범위 내의 모든 로그 파일 찾기 (.csv) + var files = Directory.GetFiles(LogDirectory, "*.csv") + .Where(f => { + string fileName = Path.GetFileNameWithoutExtension(f); + if (DateTime.TryParse(fileName, out DateTime fileDate)) + { + return fileDate.Date >= start.Date && fileDate.Date <= end.Date; + } + return false; + }); + + foreach (var file in files) + { + try + { + var lines = File.ReadAllLines(file); + if (lines.Length <= 1) continue; // Header only or empty + + // CSV 헤더: Date,Time,Channel,ID,Value,Judgment,Mode,LineNo,ProductType,SpecUL,SpecLL,Retest + for (int i = 1; i < lines.Length; i++) + { + string line = lines[i]; + if (string.IsNullOrWhiteSpace(line)) continue; + var parts = line.Split(','); + if (parts.Length < 12) continue; + + string date = parts[0].Trim(); + string time = parts[1].Trim(); + string channel = parts[2].Trim(); + string id = parts[3].Trim(); + string val = parts[4].Trim(); + string judg = parts[5].Trim(); + string mode = parts[6].Trim(); + string lineNo = parts[7].Trim(); + string prodType = parts[8].Trim(); + string specUl = parts[9].Trim(); + string specLl = parts[10].Trim(); + string retest = parts[11].Trim(); + + // 필터 적용 + if (judgmentFilter != "[전체]" && judg != judgmentFilter) continue; + if (!string.IsNullOrEmpty(serialFilter) && !id.Contains(serialFilter)) continue; + + results.Add(new InspectData + { + InspectDate = date, + InspectTime = time, + Channel = channel, + ProductId = id, + MeasuredValue = val, + Judgment = judg, + Mode = mode, + LineNo = lineNo, + ProductType = prodType, + SpecUL = specUl, + SpecLL = specLl, + Retest = retest + }); + } + } + catch (Exception ex) + { + Console.WriteLine($"[LogParser] Error reading file {file}: {ex.Message}"); + } + } + + return results.OrderByDescending(r => r.InspectDate).ThenByDescending(r => r.InspectTime).ToList(); + } + } + + /// + /// 데이터를 CSV 파일로 내보내는 유틸리티 + /// + public static class CsvExporter + { + /// + /// 컬렉션 데이터를 CSV 파일로 저장함 + /// + /// 데이터 모델 클래스 + /// 내보낼 데이터 목록 + /// 저장할 파일 경로 + public static bool ExportToCsv(IEnumerable items, string filePath) + { + try + { + var sb = new StringBuilder(); + var props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + + // Header + foreach (var prop in props) + { + sb.Append(prop.Name).Append(","); + } + sb.AppendLine(); + + // Body + foreach (var item in items) + { + foreach (var prop in props) + { + var val = prop.GetValue(item, null); + var str = val?.ToString() ?? ""; + if (str.Contains(",") || str.Contains("\"") || str.Contains("\n")) + sb.Append($"\"{str.Replace("\"", "\"\"")}\""); + else + sb.Append(str); + sb.Append(","); + } + sb.AppendLine(); + } + + File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8); + return true; + } + catch (Exception ex) + { + MessageBox.Show($"CSV 저장 실패: {ex.Message}"); + return false; + } + } + } +} diff --git a/leak_test_project/Utils/SentinelCrc8.cs b/leak_test_project/Utils/SentinelCrc8.cs deleted file mode 100644 index 162e2a6..0000000 --- a/leak_test_project/Utils/SentinelCrc8.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace leak_test_project.Utils -{ - /// - /// Sentinel C28 전용 CRC-8 계산 유틸리티. - /// 문서 4페이지의 에러 체크 규격(8-Bit CRC in HEX)을 구현함. - /// - public static class SentinelCrc8 - { - /// - /// 입력된 데이터의 8비트 CRC 값을 HEX 문자열로 계산함. - /// - /// CRC를 계산할 문자열 - /// 2자리의 HEX 문자열 (예: "A5") - public static string CalculateHex(string data) - { - // PDF 4페이지: 8-Bit CRC in HEX. Used for error checking. - // 주의: 제조사에서 제공하는 특정 다항식(Polynomial)이 있는 경우, - // 아래의 간단한 XOR 방식이 아닌 해당 알고리즘으로 교체해야 함. - byte crc = 0; - foreach (char c in data) - { - crc ^= (byte)c; // 현재는 기본 XOR 기반의 간단한 체크섬 예시 - } - - // 2자리 16진수 문자열로 반환 (대문자) - return crc.ToString("X2"); - } - } -} diff --git a/leak_test_project/Utils/SentinelParser.cs b/leak_test_project/Utils/SentinelParser.cs index 0cd99d9..28e0a52 100644 --- a/leak_test_project/Utils/SentinelParser.cs +++ b/leak_test_project/Utils/SentinelParser.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Globalization; using System.Text.RegularExpressions; using leak_test_project.Models; @@ -17,44 +18,58 @@ namespace leak_test_project.Utils public static string ExtractBody(string input, out char typeCode) { typeCode = '\0'; - if (string.IsNullOrWhiteSpace(input) || input.Length < 8) return input; + if (string.IsNullOrWhiteSpace(input)) return input; // 정규식을 통해 "XXYYZZZ(7글자) + 스페이스/탭 + H(1글자) + 스페이스/탭 + 데이터" 패턴 매칭 - var match = Regex.Match(input.TrimStart(), @"^.{7}[\s\t]+([a-zA-Z])[\s\t]+(.*)$", RegexOptions.Singleline); - if (match.Success) + // (명령-응답 프로토콜을 사용할 때 들어오는 헤더) + if (input.Length >= 8) { - typeCode = char.ToUpper(match.Groups[1].Value[0]); - return match.Groups[2].Value.Trim(); + var match = Regex.Match(input.TrimStart(), @"^.{7}[\s\t]+([a-zA-Z])[\s\t]+(.*)$", RegexOptions.Singleline); + if (match.Success) + { + typeCode = char.ToUpper(match.Groups[1].Value[0]); + string body = match.Groups[2].Value.Trim(); + Debug.WriteLine($"[C28 Parser] Header Match: Type={typeCode}, Body={body}"); + return body; + } } - // 폴백(Fallback): 정규식 실패 시 첫 번째 탭 기준으로 파싱 - int firstTabIndex = input.IndexOf('\t'); - if (firstTabIndex != -1) + // 폴백(Fallback): 통신 규격 헤더 없이 'Auto Result' 설정으로 바로 데이터가 날아오는 경우 + + var tabs = input.Split('\t'); + + // 1. 탭(\t)이 7개 이상 포함되어 있다면, 이는 표 규격의 '최종 결과(Final Result)'임 + if (tabs.Length > 7) { - string beforeTab = input.Substring(0, firstTabIndex).TrimEnd(); - if (beforeTab.Length > 0) - { - // 탭 바로 앞의 문자를 Data Type Code(H)로 간주 - typeCode = char.ToUpper(beforeTab[beforeTab.Length - 1]); - } - - // 두 번째 탭이 있는지 확인 - int secondTabIndex = input.IndexOf('\t', firstTabIndex + 1); - if (secondTabIndex != -1 && (secondTabIndex - firstTabIndex) <= 2) + typeCode = 'R'; // Result + Debug.WriteLine($"[C28 Parser] Final Result pattern matched."); + return input.Trim(); + } + + // 2. 레거시 포맷인 경우 (탭 분할 후 인덱스 2에 유효 데이터가 있는 형태) + if (tabs.Length > 2) + { + string text2 = tabs[2]; + while (text2.IndexOf(" ") >= 0) text2 = text2.Replace(" ", " "); + if (text2.Trim().Split(' ').Length >= 4) { - // H 문자가 두 탭 사이에 있는 경우 (XXYYZZZ\tH\tDATA) - // typeCode는 두 번째 탭 바로 앞 문자가 됨 - string betweenTabs = input.Substring(firstTabIndex + 1, secondTabIndex - firstTabIndex - 1).Trim(); - if (betweenTabs.Length > 0) - typeCode = char.ToUpper(betweenTabs[betweenTabs.Length - 1]); - - return input.Substring(secondTabIndex + 1).Trim(); + typeCode = 'R'; // Result + Debug.WriteLine($"[C28 Parser] Legacy Final Result pattern matched."); + return input.Trim(); } + } - return input.Substring(firstTabIndex + 1).Trim(); + // 3. 탭이 적고 "LR 0.123 sccm" 형태의 스트리밍 값 패턴을 가진 경우 + if (BodyValueRegex.IsMatch(input)) + { + typeCode = 'S'; // Streaming + Debug.WriteLine($"[C28 Parser] Streaming pattern matched."); + return input.Trim(); } - return input; + // 그 외 판별 불가 데이터는 일반 메시지로 간주 + typeCode = 'M'; + return input.Trim(); } /// @@ -71,6 +86,7 @@ namespace leak_test_project.Utils { result.MeasuredValue = double.Parse(match.Groups["value"].Value, CultureInfo.InvariantCulture); result.Unit = match.Groups["unit"].Value; + Debug.WriteLine($"[C28 Parser] Streaming Value: {result.MeasuredValue} {result.Unit}"); } return result; } @@ -102,6 +118,8 @@ namespace leak_test_project.Utils string eval = fields[7].Trim(); result.Judgment = (eval == "A") ? "OK" : "NG"; result.SensorJudgment = eval; // 센서 원본 판정 코드 보존 (교차 검증용) + + Debug.WriteLine($"[C28 Parser] Parsed Final Result - CH: {result.ChannelNo}, Prog: {result.ProgramNo}, ID: {result.SerialNo}, Eval: {result.Judgment}"); // 측정값 필드 검색 (TestData 1, 2...) // 매뉴얼 예시: PLR, P, LR 0.123456 sccm @@ -112,10 +130,51 @@ namespace leak_test_project.Utils { result.MeasuredValue = double.Parse(match.Groups["value"].Value, CultureInfo.InvariantCulture); result.Unit = match.Groups["unit"].Value; + Debug.WriteLine($"[C28 Parser] Extracted Final Value: {result.MeasuredValue} {result.Unit}"); break; } } } + else + { + // 레거시 파싱 로직 (ClsLeakSensor.cs 대응) + // ExtractBody나 input.Trim() 등에 의해 앞선 탭(\t)이 제거되어 fields 배열의 길이가 짧을 수 있음. + // 원래 데이터가 fields[2]에 있었거나, 파싱되어 fields[0] 또는 마지막 요소에 남은 경우를 대비. + string text2 = fields.Length > 2 ? fields[2] : fields[fields.Length - 1]; + + while (text2.IndexOf(" ") >= 0) + { + text2 = text2.Replace(" ", " "); + } + text2 = text2.Trim(); + string[] array2 = text2.Split(' '); + + if (array2.Length >= 4) + { + result.SensorJudgment = array2[1]; + // 레거시는 보통 P/F 를 사용하거나 A/R 을 사용 (P, A 모두 OK 처리) + result.Judgment = (result.SensorJudgment == "P" || result.SensorJudgment == "A") ? "OK" : "NG"; + + if (double.TryParse(array2[3], NumberStyles.Any, CultureInfo.InvariantCulture, out double measuredVal)) + { + result.MeasuredValue = measuredVal; + } + + if (array2.Length > 4) + { + result.Unit = array2[4]; + } + + Debug.WriteLine($"[C28 Parser] Parsed Legacy Final Result - Eval: {result.Judgment}, Value: {result.MeasuredValue}"); + } + else + { + result.SensorJudgment = "F"; + result.Judgment = "NG"; + result.MeasuredValue = -999.9; + Debug.WriteLine($"[C28 Parser] Parsed Legacy Final Result (Fail Fallback) - Eval: {result.Judgment}"); + } + } } catch (Exception ex) { @@ -130,4 +189,26 @@ namespace leak_test_project.Utils return "NG"; } } + public static class SentinelCrc8 + { + /// + /// 입력된 데이터의 8비트 CRC 값을 HEX 문자열로 계산함. + /// + /// CRC를 계산할 문자열 + /// 2자리의 HEX 문자열 (예: "A5") + public static string CalculateHex(string data) + { + // PDF 4페이지: 8-Bit CRC in HEX. Used for error checking. + // 주의: 제조사에서 제공하는 특정 다항식(Polynomial)이 있는 경우, + // 아래의 간단한 XOR 방식이 아닌 해당 알고리즘으로 교체해야 함. + byte crc = 0; + foreach (char c in data) + { + crc ^= (byte)c; // 현재는 기본 XOR 기반의 간단한 체크섬 예시 + } + + // 2자리 16진수 문자열로 반환 (대문자) + return crc.ToString("X2"); + } + } } diff --git a/leak_test_project/ViewModels/HomeViewModel.cs b/leak_test_project/ViewModels/HomeViewModel.cs index 757aabc..a7e9b0c 100644 --- a/leak_test_project/ViewModels/HomeViewModel.cs +++ b/leak_test_project/ViewModels/HomeViewModel.cs @@ -228,7 +228,6 @@ namespace leak_test_project.ViewModels }; _sentinelService.OnStreamingParsed += (s, data) => UpdateMeasurement(data); - _sentinelService.OnFinalResultParsed += (s, data) => ProcessFinalResult(data); if (!_sentinelService.Connect()) { @@ -250,37 +249,24 @@ namespace leak_test_project.ViewModels AppendLog(true, $"[DIO Board Error] {msg}"); }); - // ID 센서 서비스 (LEFT/RIGHT) - if (config.SelectedIdSensor == IdSensorType.Board4253) - { - _leftZmdiSerial = new SerialProvider(config.Board4253Port, config.Board4253BaudRate); - var sharedService = new Board4253Service(_leftZmdiSerial) { TimeoutMs = config.Board4253Timeout }; - - _leftZmdi = new Board4253SensorService(sharedService, 0); - _rightZmdi = new Board4253SensorService(sharedService, 1); - } - else - { - _leftZmdiSerial = new SerialProvider(config.LeftPort, config.ZmdiBaudRate); - _rightZmdiSerial = new SerialProvider(config.RightPort, config.ZmdiBaudRate); - - _leftZmdi = new ZmdiSensorService(_leftZmdiSerial, 0); - _rightZmdi = new ZmdiSensorService(_rightZmdiSerial, 1); - } + // ID 센서 서비스 (4251 보드 단일 통신) + _leftZmdiSerial = new SerialProvider(config.Board4251Port, config.Board4251BaudRate); + var sharedService = new Board4251Service(_leftZmdiSerial) { TimeoutMs = config.Board4251Timeout }; + + _leftZmdi = new Board4251SensorService(sharedService, 0); + _rightZmdi = new Board4251SensorService(sharedService, 1); - string sensorName = config.SelectedIdSensor == IdSensorType.Board4253 ? "4253 보드" : "ZMDI 센서"; - string logPrefix = config.SelectedIdSensor == IdSensorType.Board4253 ? "[4253 Error]" : "[ZMDI Error]"; + string sensorName = "4251 보드"; + string logPrefix = "[4251 Error]"; if (!_leftZmdi.Connect()) { - string port = config.SelectedIdSensor == IdSensorType.Board4253 ? config.Board4253Port : config.LeftPort; - string msg = $"{sensorName} 포트 연결 실패 ({port})"; + string msg = $"{sensorName} 포트 연결 실패 ({config.Board4251Port})"; LeftError = string.IsNullOrEmpty(LeftError) ? msg : $"{LeftError}\n{msg}"; } if (!_rightZmdi.Connect()) { - string port = config.SelectedIdSensor == IdSensorType.Board4253 ? config.Board4253Port : config.RightPort; - string msg = $"{sensorName} 포트 연결 실패 ({port})"; + string msg = $"{sensorName} 포트 연결 실패 ({config.Board4251Port})"; RightError = string.IsNullOrEmpty(RightError) ? msg : $"{RightError}\n{msg}"; } @@ -421,78 +407,7 @@ namespace leak_test_project.ViewModels }); } - private void ProcessFinalResult(ParsedData data) - { - _dispatcher.Invoke(() => { - double.TryParse(SpecUL, out double ul); - double.TryParse(SpecLL, out double ll); - string judgment = SentinelParser.EvaluateJudgment(data.MeasuredValue, ul, ll); - bool isOk = judgment == "OK"; - - bool isLeft = (data.ChannelNo == "C01" || data.ChannelNo == "1"); - string side = isLeft ? "LEFT" : "RIGHT"; - if (isLeft) - { - LeftValue = data.MeasuredValue.ToString("F3"); - LeftJudgment = judgment; - IsLeftOk = isOk; - LeftStatus = "TEST COMPLETE"; - LeftStartTime = data.TestTime ?? ""; - LeftDate = data.TestDate ?? ""; - LeftId = data.UniqueId ?? ""; - LeftLowId = data.LowID ?? ""; - LeftSerialNo = data.SerialNo ?? ""; - LeftMcLine = data.ChannelNo ?? ""; - LeftItem = data.ProgramNo ?? ""; - } - else - { - RightValue = data.MeasuredValue.ToString("F3"); - RightJudgment = judgment; - IsRightOk = isOk; - RightStatus = "TEST COMPLETE"; - RightStartTime = data.TestTime ?? ""; - RightDate = data.TestDate ?? ""; - RightId = data.UniqueId ?? ""; - RightLowId = data.LowID ?? ""; - RightSerialNo = data.SerialNo ?? ""; - RightMcLine = data.ChannelNo ?? ""; - RightItem = data.ProgramNo ?? ""; - } - - // SPEC 교차 검증 (C28 수동 수신 시) - if (data.SensorJudgment != null) - { - string normalizedProgram = (judgment == "OK") ? "A" : "R"; - string normalizedSensor = data.SensorJudgment; - if (normalizedProgram != normalizedSensor) - { - string msg = $"SPEC 불일치 - 프로그램: {judgment}, 센서: {data.SensorJudgment}"; - if (isLeft) LeftError = msg; - else RightError = msg; - } - } - - // 로그 기록 (일일 CSV 파일) - var inspectData = new InspectData - { - InspectDate = data.TestDate ?? DateTime.Now.ToString("yyyy-MM-dd"), - InspectTime = data.TestTime ?? DateTime.Now.ToString("HH:mm:ss"), - Channel = isLeft ? "LEFT" : "RIGHT", - ProductId = data.UniqueId ?? "", - MeasuredValue = data.MeasuredValue.ToString("F3"), - Judgment = judgment, - Mode = "양산", // 기본값 - LineNo = data.ChannelNo ?? "", - ProductType = data.ProgramNo ?? "", - SpecUL = ul.ToString("F2"), - SpecLL = ll.ToString("F2"), - Retest = "N" - }; - FileLogger.LogInspectData(inspectData); - }); - } public async System.Threading.Tasks.Task TestReadIdAsync(int testIndex) { diff --git a/leak_test_project/Views/CommunicationWindow.xaml b/leak_test_project/Views/CommunicationWindow.xaml index 0f97037..78d104d 100644 --- a/leak_test_project/Views/CommunicationWindow.xaml +++ b/leak_test_project/Views/CommunicationWindow.xaml @@ -62,33 +62,17 @@ - + - + - + - + - - - - - - - - - - - - - - - - - + @@ -97,23 +81,13 @@ - + - + - - - - - - - - - - - + @@ -146,33 +120,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/leak_test_project/Views/CommunicationWindow.xaml.cs b/leak_test_project/Views/CommunicationWindow.xaml.cs index cea8790..11810b7 100644 --- a/leak_test_project/Views/CommunicationWindow.xaml.cs +++ b/leak_test_project/Views/CommunicationWindow.xaml.cs @@ -18,31 +18,15 @@ namespace leak_test_project.Views private void LoadSettings() { var config = ConfigManager.Current; - - // ID 센서 타입 선택 - cbIdSensorType.SelectedIndex = (int)config.SelectedIdSensor; // 사용 가능한 시리얼 포트 목록 조회 string[] ports = SerialPort.GetPortNames(); - cbLeftPort.ItemsSource = ports; - cbRightPort.ItemsSource = ports; cbSensorPort.ItemsSource = ports; - cbBoard4253Port.ItemsSource = ports; + cbBoard4251Port.ItemsSource = ports; // 기존 설정 포트 자동 선택 - cbLeftPort.SelectedItem = config.LeftPort; - cbRightPort.SelectedItem = config.RightPort; cbSensorPort.SelectedItem = config.SensorPort; - cbBoard4253Port.SelectedItem = config.Board4253Port; - - foreach (ComboBoxItem item in cbZmdiBaudRate.Items) - { - if (item.Content.ToString() == config.ZmdiBaudRate.ToString()) - { - cbZmdiBaudRate.SelectedItem = item; - break; - } - } + cbBoard4251Port.SelectedItem = config.Board4251Port; foreach (ComboBoxItem item in cbSensorBaudRate.Items) { @@ -53,11 +37,11 @@ namespace leak_test_project.Views } } - foreach (ComboBoxItem item in cbBoard4253BaudRate.Items) + foreach (ComboBoxItem item in cbBoard4251BaudRate.Items) { - if (item.Content.ToString() == config.Board4253BaudRate.ToString()) + if (item.Content.ToString() == config.Board4251BaudRate.ToString()) { - cbBoard4253BaudRate.SelectedItem = item; + cbBoard4251BaudRate.SelectedItem = item; break; } } @@ -67,37 +51,21 @@ namespace leak_test_project.Views { var config = ConfigManager.Current; - if (cbIdSensorType.SelectedIndex >= 0) - config.SelectedIdSensor = (IdSensorType)cbIdSensorType.SelectedIndex; - - if (cbLeftPort.SelectedItem != null) - config.LeftPort = cbLeftPort.SelectedItem.ToString(); - - if (cbRightPort.SelectedItem != null) - config.RightPort = cbRightPort.SelectedItem.ToString(); - if (cbSensorPort.SelectedItem != null) config.SensorPort = cbSensorPort.SelectedItem.ToString(); - if (cbBoard4253Port.SelectedItem != null) - config.Board4253Port = cbBoard4253Port.SelectedItem.ToString(); - - if (cbZmdiBaudRate.SelectedItem is ComboBoxItem zmdiBaud) - { - if (int.TryParse(zmdiBaud.Content.ToString(), out int baud)) - config.ZmdiBaudRate = baud; - } - + if (cbBoard4251Port.SelectedItem != null) + config.Board4251Port = cbBoard4251Port.SelectedItem.ToString(); if (cbSensorBaudRate.SelectedItem is ComboBoxItem sensorBaud) { if (int.TryParse(sensorBaud.Content.ToString(), out int baud)) config.SensorBaudRate = baud; } - if (cbBoard4253BaudRate.SelectedItem is ComboBoxItem boardBaud) + if (cbBoard4251BaudRate.SelectedItem is ComboBoxItem boardBaud) { if (int.TryParse(boardBaud.Content.ToString(), out int baud)) - config.Board4253BaudRate = baud; + config.Board4251BaudRate = baud; } ConfigManager.Save(); diff --git a/leak_test_project/bin/Debug/app.publish/leak_test_project.exe b/leak_test_project/bin/Debug/app.publish/leak_test_project.exe index a82f630..3eb1d86 100644 Binary files a/leak_test_project/bin/Debug/app.publish/leak_test_project.exe and b/leak_test_project/bin/Debug/app.publish/leak_test_project.exe differ diff --git a/leak_test_project/bin/Debug/leak_test_project.application b/leak_test_project/bin/Debug/leak_test_project.application index 2130e12..199b558 100644 --- a/leak_test_project/bin/Debug/leak_test_project.application +++ b/leak_test_project/bin/Debug/leak_test_project.application @@ -1,6 +1,6 @@  - + @@ -8,13 +8,13 @@ - + - 6+b0r1Gncm2nD/TjBHVCWtS6O3CAXOdxRTwtx09ap8E= + s3poAPfszmsLXNBQF9a37V3LMo83TC9ce3WkqXwtkWg= diff --git a/leak_test_project/bin/Debug/leak_test_project.exe b/leak_test_project/bin/Debug/leak_test_project.exe index f73bb6c..3eb1d86 100644 Binary files a/leak_test_project/bin/Debug/leak_test_project.exe and b/leak_test_project/bin/Debug/leak_test_project.exe differ diff --git a/leak_test_project/bin/Debug/leak_test_project.exe.manifest b/leak_test_project/bin/Debug/leak_test_project.exe.manifest index e4f39d0..4790c53 100644 --- a/leak_test_project/bin/Debug/leak_test_project.exe.manifest +++ b/leak_test_project/bin/Debug/leak_test_project.exe.manifest @@ -1,6 +1,6 @@  - + @@ -42,14 +42,14 @@ - + - WIlRaRAkyOL/i77TGkCYPDQrXlpbIq1b8rVePHEWnCQ= + yOydaKo18mXp1TtjAfyZ+gxKaCmqKNcv1EZd3u2MxW4= diff --git a/leak_test_project/bin/Debug/leak_test_project.pdb b/leak_test_project/bin/Debug/leak_test_project.pdb index b6bbe53..5adb700 100644 Binary files a/leak_test_project/bin/Debug/leak_test_project.pdb and b/leak_test_project/bin/Debug/leak_test_project.pdb differ diff --git a/leak_test_project/bin/Release/Logs/2026-04-24_system.log b/leak_test_project/bin/Release/Logs/2026-04-24_system.log new file mode 100644 index 0000000..1086dc2 --- /dev/null +++ b/leak_test_project/bin/Release/Logs/2026-04-24_system.log @@ -0,0 +1,12 @@ +[09:15:52.955] [ERROR] [Board4253] Failed to receive response after 3 retries: x00o +[09:15:54.567] [ERROR] [Board4253] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801 +[09:15:56.332] [ERROR] [Board4253] Failed to receive response after 3 retries: x00o +[09:15:57.938] [ERROR] [Board4253] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801 +[09:25:56.095] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o +[09:25:57.708] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801 +[09:26:03.642] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o +[09:26:05.253] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801 +[09:26:07.848] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o +[09:26:09.454] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_002101:owt28006727ea97c7801 +[09:26:20.684] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_002101:ow2800326003e, ReceivedSoFar: +[09:27:10.638] [WARNING] [Board4251] Timeout waiting for response (Retry 1/3). Command: x00c_002101:ow2800326003e, ReceivedSoFar: diff --git a/leak_test_project/bin/Release/Logs/2026-04-27_system.log b/leak_test_project/bin/Release/Logs/2026-04-27_system.log new file mode 100644 index 0000000..84c2a0d --- /dev/null +++ b/leak_test_project/bin/Release/Logs/2026-04-27_system.log @@ -0,0 +1,2 @@ +[11:07:28.075] [ERROR] [Board4251] Failed to receive response after 3 retries: x00o +[11:07:29.684] [ERROR] [Board4251] Failed to receive response after 3 retries: x00c_001101:owt28006727ea97c7801 diff --git a/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/PCI-Dask.dll.deploy b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/PCI-Dask.dll.deploy new file mode 100644 index 0000000..b22577f Binary files /dev/null and b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/PCI-Dask.dll.deploy differ diff --git a/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.config.deploy b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.config.deploy new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.config.deploy @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.deploy b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.deploy new file mode 100644 index 0000000..8c3595d Binary files /dev/null and b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.deploy differ diff --git a/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.manifest b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.manifest new file mode 100644 index 0000000..07d78b2 --- /dev/null +++ b/leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.manifest @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0Y9/ht3hpE+fHxvoEjUYeZTPBCNYvBFnq7mbbLVmzdQ= + + + + + + + + + + R+Wg8QGvQVHX8T0ta/qbhH1bXkqY0fRnS3wBV3J0bN8= + + + + + + + + + pDB54gSYV5M/tu2DZtDUj13HUp3HrRLSNr97K/nGHh4= + + + \ No newline at end of file diff --git a/leak_test_project/bin/Release/app.publish/autorun.inf b/leak_test_project/bin/Release/app.publish/autorun.inf new file mode 100644 index 0000000..e32cf9f --- /dev/null +++ b/leak_test_project/bin/Release/app.publish/autorun.inf @@ -0,0 +1,3 @@ +[autorun] +open=setup.exe +icon=setup.exe,0 diff --git a/leak_test_project/bin/Release/app.publish/leak_test_project.application b/leak_test_project/bin/Release/app.publish/leak_test_project.application new file mode 100644 index 0000000..f7fa986 --- /dev/null +++ b/leak_test_project/bin/Release/app.publish/leak_test_project.application @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + aS0o/xVk0fJXJbjmEymfDZePIzgCcCd8N6ZW9l5KEAM= + + + + \ No newline at end of file diff --git a/leak_test_project/bin/Release/app.publish/leak_test_project.exe b/leak_test_project/bin/Release/app.publish/leak_test_project.exe index 14d4f29..8c3595d 100644 Binary files a/leak_test_project/bin/Release/app.publish/leak_test_project.exe and b/leak_test_project/bin/Release/app.publish/leak_test_project.exe differ diff --git a/leak_test_project/bin/Release/app.publish/setup.exe b/leak_test_project/bin/Release/app.publish/setup.exe new file mode 100644 index 0000000..dbd6140 Binary files /dev/null and b/leak_test_project/bin/Release/app.publish/setup.exe differ diff --git a/leak_test_project/bin/Release/config.xml b/leak_test_project/bin/Release/config.xml index 28b6f76..140dc02 100644 --- a/leak_test_project/bin/Release/config.xml +++ b/leak_test_project/bin/Release/config.xml @@ -1,14 +1,14 @@ - Board4253 - COM1 - COM1 + Board4251 + COM9 + COM8 19200 COM1 9600 - COM11 - 115200 - 500 - 0.1 - -0.15 + COM11 + 115200 + 5000 + 1 + -1 \ No newline at end of file diff --git a/leak_test_project/bin/Release/leak_test_project.application b/leak_test_project/bin/Release/leak_test_project.application index 4d4a859..bfdd87d 100644 --- a/leak_test_project/bin/Release/leak_test_project.application +++ b/leak_test_project/bin/Release/leak_test_project.application @@ -1,6 +1,6 @@  - + @@ -8,13 +8,13 @@ - + - JHj2v9ObfsxRnMeInoMVS54QBnfDXrD+U8YIz4SZjn0= + aS0o/xVk0fJXJbjmEymfDZePIzgCcCd8N6ZW9l5KEAM= diff --git a/leak_test_project/bin/Release/leak_test_project.exe b/leak_test_project/bin/Release/leak_test_project.exe index 7a2759a..8c3595d 100644 Binary files a/leak_test_project/bin/Release/leak_test_project.exe and b/leak_test_project/bin/Release/leak_test_project.exe differ diff --git a/leak_test_project/bin/Release/leak_test_project.exe.manifest b/leak_test_project/bin/Release/leak_test_project.exe.manifest index ff538d1..07d78b2 100644 --- a/leak_test_project/bin/Release/leak_test_project.exe.manifest +++ b/leak_test_project/bin/Release/leak_test_project.exe.manifest @@ -1,6 +1,6 @@  - + @@ -42,14 +42,14 @@ - + - n0WfpmBUJxy+jy5UyVtMgokEqYG/uPakh4PhKDgvg7k= + 0Y9/ht3hpE+fHxvoEjUYeZTPBCNYvBFnq7mbbLVmzdQ= diff --git a/leak_test_project/bin/Release/leak_test_project.pdb b/leak_test_project/bin/Release/leak_test_project.pdb index ae4bdb9..2c4c1a7 100644 Binary files a/leak_test_project/bin/Release/leak_test_project.pdb and b/leak_test_project/bin/Release/leak_test_project.pdb differ diff --git a/leak_test_project/leak_test_project.csproj b/leak_test_project/leak_test_project.csproj index 3b6f267..e771090 100644 --- a/leak_test_project/leak_test_project.csproj +++ b/leak_test_project/leak_test_project.csproj @@ -28,7 +28,7 @@ false true true - 19 + 22 1.0.0.%2a false true @@ -63,7 +63,7 @@ true - true + false @@ -118,33 +118,19 @@ App.xaml Code - - + + - - - - - + - - - - - - - - - - - - - + + + HomeView.xaml diff --git a/leak_test_project/obj/Debug/App.baml b/leak_test_project/obj/Debug/App.baml index c9aed09..59ecfe2 100644 Binary files a/leak_test_project/obj/Debug/App.baml and b/leak_test_project/obj/Debug/App.baml differ diff --git a/leak_test_project/obj/Debug/MainWindow.baml b/leak_test_project/obj/Debug/MainWindow.baml index cb75ced..7cc6042 100644 Binary files a/leak_test_project/obj/Debug/MainWindow.baml and b/leak_test_project/obj/Debug/MainWindow.baml differ diff --git a/leak_test_project/obj/Debug/Views/CommunicationWindow.baml b/leak_test_project/obj/Debug/Views/CommunicationWindow.baml index d022021..ac74b74 100644 Binary files a/leak_test_project/obj/Debug/Views/CommunicationWindow.baml and b/leak_test_project/obj/Debug/Views/CommunicationWindow.baml differ diff --git a/leak_test_project/obj/Debug/Views/CommunicationWindow.g.cs b/leak_test_project/obj/Debug/Views/CommunicationWindow.g.cs index 247505c..3fd980c 100644 --- a/leak_test_project/obj/Debug/Views/CommunicationWindow.g.cs +++ b/leak_test_project/obj/Debug/Views/CommunicationWindow.g.cs @@ -1,4 +1,4 @@ -#pragma checksum "..\..\..\Views\CommunicationWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "AF544BD3712CB303CB302F9AA68E55C5B6921446AD339B379C071FBD012AAE60" +#pragma checksum "..\..\..\Views\CommunicationWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "0D1A5D2CE597F5EDC85C218A980295FF8EFF3C3BD5596502135BA8B9CB7BC0CF" //------------------------------------------------------------------------------ // // 이 코드는 도구를 사용하여 생성되었습니다. @@ -90,7 +90,7 @@ namespace leak_test_project.Views { #line 158 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbBoard4253BaudRate; + internal System.Windows.Controls.ComboBox cbBoard4251BaudRate; #line default #line hidden @@ -98,7 +98,7 @@ namespace leak_test_project.Views { #line 173 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbBoard4253Port; + internal System.Windows.Controls.ComboBox cbBoard4251Port; #line default #line hidden @@ -168,10 +168,10 @@ namespace leak_test_project.Views { this.cbSensorPort = ((System.Windows.Controls.ComboBox)(target)); return; case 7: - this.cbBoard4253BaudRate = ((System.Windows.Controls.ComboBox)(target)); + this.cbBoard4251BaudRate = ((System.Windows.Controls.ComboBox)(target)); return; case 8: - this.cbBoard4253Port = ((System.Windows.Controls.ComboBox)(target)); + this.cbBoard4251Port = ((System.Windows.Controls.ComboBox)(target)); return; case 9: this.btnSave = ((System.Windows.Controls.Button)(target)); diff --git a/leak_test_project/obj/Debug/Views/DataView.baml b/leak_test_project/obj/Debug/Views/DataView.baml index 4a2b47f..8c7863e 100644 Binary files a/leak_test_project/obj/Debug/Views/DataView.baml and b/leak_test_project/obj/Debug/Views/DataView.baml differ diff --git a/leak_test_project/obj/Debug/Views/HomeView.baml b/leak_test_project/obj/Debug/Views/HomeView.baml index 8f84030..172413c 100644 Binary files a/leak_test_project/obj/Debug/Views/HomeView.baml and b/leak_test_project/obj/Debug/Views/HomeView.baml differ diff --git a/leak_test_project/obj/Debug/Views/HomeView.g.cs b/leak_test_project/obj/Debug/Views/HomeView.g.cs index ade120d..26a0680 100644 --- a/leak_test_project/obj/Debug/Views/HomeView.g.cs +++ b/leak_test_project/obj/Debug/Views/HomeView.g.cs @@ -1,4 +1,4 @@ -#pragma checksum "..\..\..\Views\HomeView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "2921BB4A069CB2EEEC799AE758E9C631287817D22AABE0AE39C97B7738A1BC9F" +#pragma checksum "..\..\..\Views\HomeView.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "EF28D1F33A372FF8C07A2FA029508C46F6D83EE13FA1C4B341D2417DC19F1E43" //------------------------------------------------------------------------------ // // 이 코드는 도구를 사용하여 생성되었습니다. diff --git a/leak_test_project/obj/Debug/Views/InOutView.baml b/leak_test_project/obj/Debug/Views/InOutView.baml index 9a1cce4..772e1d9 100644 Binary files a/leak_test_project/obj/Debug/Views/InOutView.baml and b/leak_test_project/obj/Debug/Views/InOutView.baml differ diff --git a/leak_test_project/obj/Debug/Views/ParametersWindow.baml b/leak_test_project/obj/Debug/Views/ParametersWindow.baml index 17279ad..b27dce5 100644 Binary files a/leak_test_project/obj/Debug/Views/ParametersWindow.baml and b/leak_test_project/obj/Debug/Views/ParametersWindow.baml differ diff --git a/leak_test_project/obj/Debug/leak_test_project.application b/leak_test_project/obj/Debug/leak_test_project.application index 2130e12..199b558 100644 --- a/leak_test_project/obj/Debug/leak_test_project.application +++ b/leak_test_project/obj/Debug/leak_test_project.application @@ -1,6 +1,6 @@  - + @@ -8,13 +8,13 @@ - + - 6+b0r1Gncm2nD/TjBHVCWtS6O3CAXOdxRTwtx09ap8E= + s3poAPfszmsLXNBQF9a37V3LMo83TC9ce3WkqXwtkWg= diff --git a/leak_test_project/obj/Debug/leak_test_project.csproj.AssemblyReference.cache b/leak_test_project/obj/Debug/leak_test_project.csproj.AssemblyReference.cache index 5bc2e78..942f0eb 100644 Binary files a/leak_test_project/obj/Debug/leak_test_project.csproj.AssemblyReference.cache and b/leak_test_project/obj/Debug/leak_test_project.csproj.AssemblyReference.cache differ diff --git a/leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache b/leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache index e7a1d7e..accdb9c 100644 --- a/leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache +++ b/leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -e34b793bb8bcf09fd6cf0746c906a5cb6a41ebb454d1a15e8953c71a7b0a3a10 +5a72f1b3b73a2fa11c624e6d676bc467a15e8fd47fc6d1c698f6714d64884008 diff --git a/leak_test_project/obj/Debug/leak_test_project.exe b/leak_test_project/obj/Debug/leak_test_project.exe index f73bb6c..3eb1d86 100644 Binary files a/leak_test_project/obj/Debug/leak_test_project.exe and b/leak_test_project/obj/Debug/leak_test_project.exe differ diff --git a/leak_test_project/obj/Debug/leak_test_project.exe.manifest b/leak_test_project/obj/Debug/leak_test_project.exe.manifest index e4f39d0..4790c53 100644 --- a/leak_test_project/obj/Debug/leak_test_project.exe.manifest +++ b/leak_test_project/obj/Debug/leak_test_project.exe.manifest @@ -1,6 +1,6 @@  - + @@ -42,14 +42,14 @@ - + - WIlRaRAkyOL/i77TGkCYPDQrXlpbIq1b8rVePHEWnCQ= + yOydaKo18mXp1TtjAfyZ+gxKaCmqKNcv1EZd3u2MxW4= diff --git a/leak_test_project/obj/Debug/leak_test_project.g.resources b/leak_test_project/obj/Debug/leak_test_project.g.resources index 1adb6f4..286f046 100644 Binary files a/leak_test_project/obj/Debug/leak_test_project.g.resources and b/leak_test_project/obj/Debug/leak_test_project.g.resources differ diff --git a/leak_test_project/obj/Debug/leak_test_project.pdb b/leak_test_project/obj/Debug/leak_test_project.pdb index b6bbe53..5adb700 100644 Binary files a/leak_test_project/obj/Debug/leak_test_project.pdb and b/leak_test_project/obj/Debug/leak_test_project.pdb differ diff --git a/leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache b/leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache index fba2723..26af97a 100644 --- a/leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache +++ b/leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache @@ -10,10 +10,10 @@ none false DEBUG;TRACE C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml -6627611856 -1-698605034 -44464663643 -14-767506772 +6-1300028631 +1-1942997596 +31-1988825095 +14188644636 Views\HomeView.xaml;Views\InOutView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;MainWindow.xaml; False diff --git a/leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache b/leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache index c8d80e2..b6d618b 100644 Binary files a/leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache and b/leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/leak_test_project/obj/Release/Views/CommunicationWindow.baml b/leak_test_project/obj/Release/Views/CommunicationWindow.baml index 23ad6b7..e86c526 100644 Binary files a/leak_test_project/obj/Release/Views/CommunicationWindow.baml and b/leak_test_project/obj/Release/Views/CommunicationWindow.baml differ diff --git a/leak_test_project/obj/Release/Views/CommunicationWindow.g.cs b/leak_test_project/obj/Release/Views/CommunicationWindow.g.cs index c1a1885..b455898 100644 --- a/leak_test_project/obj/Release/Views/CommunicationWindow.g.cs +++ b/leak_test_project/obj/Release/Views/CommunicationWindow.g.cs @@ -1,4 +1,4 @@ -#pragma checksum "..\..\..\Views\CommunicationWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "35CBF1849D665746CF5E7299A7C73D46247207556426E1B30BE73CAFE7C37937" +#pragma checksum "..\..\..\Views\CommunicationWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "96F47D4E8790E6CF229D163DC91B208871B53E4CB300CB1B0A3EA3FA65A47B4A" //------------------------------------------------------------------------------ // // 이 코드는 도구를 사용하여 생성되었습니다. @@ -42,37 +42,21 @@ namespace leak_test_project.Views { #line 75 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbIdSensorType; + internal System.Windows.Controls.ComboBox cbBoard4251BaudRate; #line default #line hidden - #line 91 "..\..\..\Views\CommunicationWindow.xaml" + #line 90 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbZmdiBaudRate; + internal System.Windows.Controls.ComboBox cbBoard4251Port; #line default #line hidden - #line 106 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbLeftPort; - - #line default - #line hidden - - - #line 116 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbRightPort; - - #line default - #line hidden - - - #line 131 "..\..\..\Views\CommunicationWindow.xaml" + #line 105 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.ComboBox cbSensorBaudRate; @@ -80,7 +64,7 @@ namespace leak_test_project.Views { #line hidden - #line 146 "..\..\..\Views\CommunicationWindow.xaml" + #line 120 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.ComboBox cbSensorPort; @@ -88,23 +72,7 @@ namespace leak_test_project.Views { #line hidden - #line 158 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbBoard4253BaudRate; - - #line default - #line hidden - - - #line 173 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbBoard4253Port; - - #line default - #line hidden - - - #line 183 "..\..\..\Views\CommunicationWindow.xaml" + #line 130 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.Button btnSave; @@ -112,7 +80,7 @@ namespace leak_test_project.Views { #line hidden - #line 184 "..\..\..\Views\CommunicationWindow.xaml" + #line 131 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.Button btnClose; @@ -150,42 +118,30 @@ namespace leak_test_project.Views { switch (connectionId) { case 1: - this.cbIdSensorType = ((System.Windows.Controls.ComboBox)(target)); + this.cbBoard4251BaudRate = ((System.Windows.Controls.ComboBox)(target)); return; case 2: - this.cbZmdiBaudRate = ((System.Windows.Controls.ComboBox)(target)); + this.cbBoard4251Port = ((System.Windows.Controls.ComboBox)(target)); return; case 3: - this.cbLeftPort = ((System.Windows.Controls.ComboBox)(target)); - return; - case 4: - this.cbRightPort = ((System.Windows.Controls.ComboBox)(target)); - return; - case 5: this.cbSensorBaudRate = ((System.Windows.Controls.ComboBox)(target)); return; - case 6: + case 4: this.cbSensorPort = ((System.Windows.Controls.ComboBox)(target)); return; - case 7: - this.cbBoard4253BaudRate = ((System.Windows.Controls.ComboBox)(target)); - return; - case 8: - this.cbBoard4253Port = ((System.Windows.Controls.ComboBox)(target)); - return; - case 9: + case 5: this.btnSave = ((System.Windows.Controls.Button)(target)); - #line 183 "..\..\..\Views\CommunicationWindow.xaml" + #line 130 "..\..\..\Views\CommunicationWindow.xaml" this.btnSave.Click += new System.Windows.RoutedEventHandler(this.BtnSave_Click); #line default #line hidden return; - case 10: + case 6: this.btnClose = ((System.Windows.Controls.Button)(target)); - #line 184 "..\..\..\Views\CommunicationWindow.xaml" + #line 131 "..\..\..\Views\CommunicationWindow.xaml" this.btnClose.Click += new System.Windows.RoutedEventHandler(this.BtnClose_Click); #line default diff --git a/leak_test_project/obj/Release/Views/CommunicationWindow.g.i.cs b/leak_test_project/obj/Release/Views/CommunicationWindow.g.i.cs index c1a1885..b455898 100644 --- a/leak_test_project/obj/Release/Views/CommunicationWindow.g.i.cs +++ b/leak_test_project/obj/Release/Views/CommunicationWindow.g.i.cs @@ -1,4 +1,4 @@ -#pragma checksum "..\..\..\Views\CommunicationWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "35CBF1849D665746CF5E7299A7C73D46247207556426E1B30BE73CAFE7C37937" +#pragma checksum "..\..\..\Views\CommunicationWindow.xaml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "96F47D4E8790E6CF229D163DC91B208871B53E4CB300CB1B0A3EA3FA65A47B4A" //------------------------------------------------------------------------------ // // 이 코드는 도구를 사용하여 생성되었습니다. @@ -42,37 +42,21 @@ namespace leak_test_project.Views { #line 75 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbIdSensorType; + internal System.Windows.Controls.ComboBox cbBoard4251BaudRate; #line default #line hidden - #line 91 "..\..\..\Views\CommunicationWindow.xaml" + #line 90 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbZmdiBaudRate; + internal System.Windows.Controls.ComboBox cbBoard4251Port; #line default #line hidden - #line 106 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbLeftPort; - - #line default - #line hidden - - - #line 116 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbRightPort; - - #line default - #line hidden - - - #line 131 "..\..\..\Views\CommunicationWindow.xaml" + #line 105 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.ComboBox cbSensorBaudRate; @@ -80,7 +64,7 @@ namespace leak_test_project.Views { #line hidden - #line 146 "..\..\..\Views\CommunicationWindow.xaml" + #line 120 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.ComboBox cbSensorPort; @@ -88,23 +72,7 @@ namespace leak_test_project.Views { #line hidden - #line 158 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbBoard4253BaudRate; - - #line default - #line hidden - - - #line 173 "..\..\..\Views\CommunicationWindow.xaml" - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] - internal System.Windows.Controls.ComboBox cbBoard4253Port; - - #line default - #line hidden - - - #line 183 "..\..\..\Views\CommunicationWindow.xaml" + #line 130 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.Button btnSave; @@ -112,7 +80,7 @@ namespace leak_test_project.Views { #line hidden - #line 184 "..\..\..\Views\CommunicationWindow.xaml" + #line 131 "..\..\..\Views\CommunicationWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.Button btnClose; @@ -150,42 +118,30 @@ namespace leak_test_project.Views { switch (connectionId) { case 1: - this.cbIdSensorType = ((System.Windows.Controls.ComboBox)(target)); + this.cbBoard4251BaudRate = ((System.Windows.Controls.ComboBox)(target)); return; case 2: - this.cbZmdiBaudRate = ((System.Windows.Controls.ComboBox)(target)); + this.cbBoard4251Port = ((System.Windows.Controls.ComboBox)(target)); return; case 3: - this.cbLeftPort = ((System.Windows.Controls.ComboBox)(target)); - return; - case 4: - this.cbRightPort = ((System.Windows.Controls.ComboBox)(target)); - return; - case 5: this.cbSensorBaudRate = ((System.Windows.Controls.ComboBox)(target)); return; - case 6: + case 4: this.cbSensorPort = ((System.Windows.Controls.ComboBox)(target)); return; - case 7: - this.cbBoard4253BaudRate = ((System.Windows.Controls.ComboBox)(target)); - return; - case 8: - this.cbBoard4253Port = ((System.Windows.Controls.ComboBox)(target)); - return; - case 9: + case 5: this.btnSave = ((System.Windows.Controls.Button)(target)); - #line 183 "..\..\..\Views\CommunicationWindow.xaml" + #line 130 "..\..\..\Views\CommunicationWindow.xaml" this.btnSave.Click += new System.Windows.RoutedEventHandler(this.BtnSave_Click); #line default #line hidden return; - case 10: + case 6: this.btnClose = ((System.Windows.Controls.Button)(target)); - #line 184 "..\..\..\Views\CommunicationWindow.xaml" + #line 131 "..\..\..\Views\CommunicationWindow.xaml" this.btnClose.Click += new System.Windows.RoutedEventHandler(this.BtnClose_Click); #line default diff --git a/leak_test_project/obj/Release/leak_test_project.application b/leak_test_project/obj/Release/leak_test_project.application index 4d4a859..bfdd87d 100644 --- a/leak_test_project/obj/Release/leak_test_project.application +++ b/leak_test_project/obj/Release/leak_test_project.application @@ -1,6 +1,6 @@  - + @@ -8,13 +8,13 @@ - + - JHj2v9ObfsxRnMeInoMVS54QBnfDXrD+U8YIz4SZjn0= + aS0o/xVk0fJXJbjmEymfDZePIzgCcCd8N6ZW9l5KEAM= diff --git a/leak_test_project/obj/Release/leak_test_project.csproj.AssemblyReference.cache b/leak_test_project/obj/Release/leak_test_project.csproj.AssemblyReference.cache index 942f0eb..3fd9a91 100644 Binary files a/leak_test_project/obj/Release/leak_test_project.csproj.AssemblyReference.cache and b/leak_test_project/obj/Release/leak_test_project.csproj.AssemblyReference.cache differ diff --git a/leak_test_project/obj/Release/leak_test_project.csproj.CoreCompileInputs.cache b/leak_test_project/obj/Release/leak_test_project.csproj.CoreCompileInputs.cache index 988987d..a07914d 100644 --- a/leak_test_project/obj/Release/leak_test_project.csproj.CoreCompileInputs.cache +++ b/leak_test_project/obj/Release/leak_test_project.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -8e801ce7493642182a43583fe17b32e5aa821b386f992c7c1afbbc168b90d394 +d29d5e88a83e274cd1ba84e078e845c50e83710584f01c7fed12878e6fce3677 diff --git a/leak_test_project/obj/Release/leak_test_project.exe b/leak_test_project/obj/Release/leak_test_project.exe index 7a2759a..8c3595d 100644 Binary files a/leak_test_project/obj/Release/leak_test_project.exe and b/leak_test_project/obj/Release/leak_test_project.exe differ diff --git a/leak_test_project/obj/Release/leak_test_project.exe.manifest b/leak_test_project/obj/Release/leak_test_project.exe.manifest index ff538d1..07d78b2 100644 --- a/leak_test_project/obj/Release/leak_test_project.exe.manifest +++ b/leak_test_project/obj/Release/leak_test_project.exe.manifest @@ -1,6 +1,6 @@  - + @@ -42,14 +42,14 @@ - + - n0WfpmBUJxy+jy5UyVtMgokEqYG/uPakh4PhKDgvg7k= + 0Y9/ht3hpE+fHxvoEjUYeZTPBCNYvBFnq7mbbLVmzdQ= diff --git a/leak_test_project/obj/Release/leak_test_project.g.resources b/leak_test_project/obj/Release/leak_test_project.g.resources index ecd7ba7..2f93e54 100644 Binary files a/leak_test_project/obj/Release/leak_test_project.g.resources and b/leak_test_project/obj/Release/leak_test_project.g.resources differ diff --git a/leak_test_project/obj/Release/leak_test_project.pdb b/leak_test_project/obj/Release/leak_test_project.pdb index ae4bdb9..2c4c1a7 100644 Binary files a/leak_test_project/obj/Release/leak_test_project.pdb and b/leak_test_project/obj/Release/leak_test_project.pdb differ diff --git a/leak_test_project/obj/Release/leak_test_project_MarkupCompile.cache b/leak_test_project/obj/Release/leak_test_project_MarkupCompile.cache index 495e9c3..a6b7b98 100644 --- a/leak_test_project/obj/Release/leak_test_project_MarkupCompile.cache +++ b/leak_test_project/obj/Release/leak_test_project_MarkupCompile.cache @@ -12,7 +12,7 @@ TRACE C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml 6627611856 1-698605034 -44464663643 +30687793950 14-767506772 Views\HomeView.xaml;Views\InOutView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;MainWindow.xaml; diff --git a/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.cache b/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.cache index 7629669..f520ac9 100644 --- a/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.cache +++ b/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.cache @@ -12,9 +12,9 @@ TRACE C:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\App.xaml 6627611856 1-698605034 -452020398631 +31-2051438358 14-767506772 Views\HomeView.xaml;Views\InOutView.xaml;Views\DataView.xaml;Views\ParametersWindow.xaml;Views\CommunicationWindow.xaml;MainWindow.xaml; -True +False diff --git a/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.lref b/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.lref deleted file mode 100644 index ac3a45a..0000000 --- a/leak_test_project/obj/Release/leak_test_project_MarkupCompile.i.lref +++ /dev/null @@ -1,4 +0,0 @@ - - -FC:\Users\COMPUTER1\Desktop\mobi\leak_test_project\leak_test_project\MainWindow.xaml;; -