Browse Source

fix: C28 통신 연결

main
최승민 2 days ago
parent
commit
886eb0ad1d
  1. 12
      leak_test_project.Tests/Services/Board4251ServiceTests.cs
  2. 84
      leak_test_project.Tests/Utils/SentinelParserTests.cs
  3. 25
      leak_test_project.Tests/bin/Debug/net472/Logs/2026-04-24_system.log
  4. BIN
      leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.dll
  5. BIN
      leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.pdb
  6. 10
      leak_test_project.Tests/bin/Release/net472/Logs/2026-04-24_system.log
  7. 25
      leak_test_project.Tests/leak_test_project.Tests.csproj
  8. 11
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfo.cs
  9. 2
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfoInputs.cache
  10. BIN
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.assets.cache
  11. BIN
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.AssemblyReference.cache
  12. 0
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers
  13. 2
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache
  14. BIN
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.dll
  15. BIN
      leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.pdb
  16. 4
      leak_test_project.Tests/obj/Release/net472/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs
  17. 23
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfo.cs
  18. 1
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfoInputs.cache
  19. 8
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.GeneratedMSBuildEditorConfig.editorconfig
  20. BIN
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.assets.cache
  21. BIN
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.AssemblyReference.cache
  22. 0
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers
  23. 1
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache
  24. 54
      leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.FileListAbsolute.txt
  25. 6
      leak_test_project.Tests/obj/leak_test_project.Tests.csproj.nuget.dgspec.json
  26. 18
      leak_test_project.Tests/obj/project.assets.json
  27. 2
      leak_test_project.Tests/obj/project.nuget.cache
  28. 1
      leak_test_project.slnx
  29. 32
      leak_test_project/Infrastructure/CommunicationBase.cs
  30. 30
      leak_test_project/Infrastructure/DioBoardBase.cs
  31. 34
      leak_test_project/Infrastructure/ICommunication.cs
  32. 34
      leak_test_project/Infrastructure/IDioBoard.cs
  33. 27
      leak_test_project/Models/AppConfig.cs
  34. 38
      leak_test_project/Models/DioPoint.cs
  35. 26
      leak_test_project/Models/InOutItem.cs
  36. 18
      leak_test_project/Models/InspectData.cs
  37. 31
      leak_test_project/Models/ParsedData.cs
  38. 159
      leak_test_project/Models/ProjectModels.cs
  39. 39
      leak_test_project/Models/SensorIdData.cs
  40. 489
      leak_test_project/Services/Board4251.cs
  41. 97
      leak_test_project/Services/Board4253DioBoard.cs
  42. 144
      leak_test_project/Services/Board4253SensorService.cs
  43. 261
      leak_test_project/Services/Board4253Service.cs
  44. 70
      leak_test_project/Services/SentinelC28Service.cs
  45. 41
      leak_test_project/Services/TestProcessService.cs
  46. 348
      leak_test_project/Services/ZmdiSensorService.cs
  47. 55
      leak_test_project/Utils/ConfigHelper.cs
  48. 61
      leak_test_project/Utils/ConfigManager.cs
  49. 61
      leak_test_project/Utils/CsvExporter.cs
  50. 88
      leak_test_project/Utils/FileLogger.cs
  51. 93
      leak_test_project/Utils/LogParser.cs
  52. 228
      leak_test_project/Utils/LogUtils.cs
  53. 29
      leak_test_project/Utils/SentinelCrc8.cs
  54. 133
      leak_test_project/Utils/SentinelParser.cs
  55. 103
      leak_test_project/ViewModels/HomeViewModel.cs
  56. 69
      leak_test_project/Views/CommunicationWindow.xaml
  57. 50
      leak_test_project/Views/CommunicationWindow.xaml.cs
  58. BIN
      leak_test_project/bin/Debug/app.publish/leak_test_project.exe
  59. 6
      leak_test_project/bin/Debug/leak_test_project.application
  60. BIN
      leak_test_project/bin/Debug/leak_test_project.exe
  61. 6
      leak_test_project/bin/Debug/leak_test_project.exe.manifest
  62. BIN
      leak_test_project/bin/Debug/leak_test_project.pdb
  63. 12
      leak_test_project/bin/Release/Logs/2026-04-24_system.log
  64. 2
      leak_test_project/bin/Release/Logs/2026-04-27_system.log
  65. BIN
      leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/PCI-Dask.dll.deploy
  66. 6
      leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.config.deploy
  67. BIN
      leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.deploy
  68. 74
      leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.manifest
  69. 3
      leak_test_project/bin/Release/app.publish/autorun.inf
  70. 21
      leak_test_project/bin/Release/app.publish/leak_test_project.application
  71. BIN
      leak_test_project/bin/Release/app.publish/leak_test_project.exe
  72. BIN
      leak_test_project/bin/Release/app.publish/setup.exe
  73. 16
      leak_test_project/bin/Release/config.xml
  74. 6
      leak_test_project/bin/Release/leak_test_project.application
  75. BIN
      leak_test_project/bin/Release/leak_test_project.exe
  76. 6
      leak_test_project/bin/Release/leak_test_project.exe.manifest
  77. BIN
      leak_test_project/bin/Release/leak_test_project.pdb
  78. 30
      leak_test_project/leak_test_project.csproj
  79. BIN
      leak_test_project/obj/Debug/App.baml
  80. BIN
      leak_test_project/obj/Debug/MainWindow.baml
  81. BIN
      leak_test_project/obj/Debug/Views/CommunicationWindow.baml
  82. 10
      leak_test_project/obj/Debug/Views/CommunicationWindow.g.cs
  83. BIN
      leak_test_project/obj/Debug/Views/DataView.baml
  84. BIN
      leak_test_project/obj/Debug/Views/HomeView.baml
  85. 2
      leak_test_project/obj/Debug/Views/HomeView.g.cs
  86. BIN
      leak_test_project/obj/Debug/Views/InOutView.baml
  87. BIN
      leak_test_project/obj/Debug/Views/ParametersWindow.baml
  88. 6
      leak_test_project/obj/Debug/leak_test_project.application
  89. BIN
      leak_test_project/obj/Debug/leak_test_project.csproj.AssemblyReference.cache
  90. 2
      leak_test_project/obj/Debug/leak_test_project.csproj.CoreCompileInputs.cache
  91. BIN
      leak_test_project/obj/Debug/leak_test_project.exe
  92. 6
      leak_test_project/obj/Debug/leak_test_project.exe.manifest
  93. BIN
      leak_test_project/obj/Debug/leak_test_project.g.resources
  94. BIN
      leak_test_project/obj/Debug/leak_test_project.pdb
  95. 8
      leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache
  96. BIN
      leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache
  97. BIN
      leak_test_project/obj/Release/Views/CommunicationWindow.baml
  98. 74
      leak_test_project/obj/Release/Views/CommunicationWindow.g.cs
  99. 74
      leak_test_project/obj/Release/Views/CommunicationWindow.g.i.cs
  100. 6
      leak_test_project/obj/Release/leak_test_project.application

12
leak_test_project.Tests/Services/Board4253ServiceTests.cs → 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<ICommunication>();
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<ICommunication>();
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<string>()))
@ -64,7 +64,7 @@ namespace leak_test_project.Tests.Services
// Arrange
var mockComm = new Mock<ICommunication>();
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<string>(s => s == expectedCommand)))
.Callback<string>(cmd => {
Task.Run(() => {
mockComm.Raise(c => c.DataReceived += null, mockComm.Object, $"ID: {expectedId} <end>");
mockComm.Raise(c => c.DataReceived += null, mockComm.Object, $"ID: {expectedId}\r\n<end>");
});
});
@ -91,7 +91,7 @@ namespace leak_test_project.Tests.Services
// Arrange
var mockComm = new Mock<ICommunication>();
mockComm.Setup(c => c.IsOpen).Returns(true);
var service = new Board4253Service(mockComm.Object);
var service = new Board4251Service(mockComm.Object);
service.TimeoutMs = 100; // 타임아웃 테스트 속도를 위해 100ms로 설정
// Act

84
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")]

25
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

BIN
leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.dll

Binary file not shown.

BIN
leak_test_project.Tests/bin/Debug/net472/leak_test_project.Tests.pdb

Binary file not shown.

10
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

25
leak_test_project.Tests/leak_test_project.Tests.csproj

@ -20,15 +20,28 @@
<PackageReference Include="Moq" Version="4.20.70" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework" />
<Reference Include="PresentationCore" />
<Reference Include="WindowsBase" />
<Reference Include="System.Xaml" />
</ItemGroup>
<ItemGroup>
<!--<ProjectReference Include="..\leak_test_project\leak_test_project.csproj" />-->
<Compile Include="..\leak_test_project\Utils\SentinelCrc8.cs" Link="Utils\SentinelCrc8.cs" />
<Compile Include="..\leak_test_project\Utils\ConfigHelper.cs" Link="Utils\ConfigHelper.cs" />
<Compile Include="..\leak_test_project\Utils\SentinelParser.cs" Link="Utils\SentinelParser.cs" />
<Compile Include="..\leak_test_project\Utils\FileLogger.cs" Link="Utils\FileLogger.cs" />
<Compile Include="..\leak_test_project\Models\ParsedData.cs" Link="Models\ParsedData.cs" />
<Compile Include="..\leak_test_project\Models\InspectData.cs" Link="Models\InspectData.cs" />
<Compile Include="..\leak_test_project\Infrastructure\ICommunication.cs" Link="Infrastructure\ICommunication.cs" />
<Compile Include="..\leak_test_project\Services\Board4253Service.cs" Link="Services\Board4253Service.cs" />
<Compile Include="..\leak_test_project\Utils\LogUtils.cs" Link="Utils\LogUtils.cs" />
<Compile Include="..\leak_test_project\Models\ProjectModels.cs" Link="Models\ProjectModels.cs" />
<Compile Include="..\leak_test_project\Infrastructure\CommunicationBase.cs" Link="Infrastructure\CommunicationBase.cs" />
<Compile Include="..\leak_test_project\Infrastructure\DioBoardBase.cs" Link="Infrastructure\DioBoardBase.cs" />
<Compile Include="..\leak_test_project\Infrastructure\Dask.cs" Link="Infrastructure\Dask.cs" />
<Compile Include="..\leak_test_project\Services\IIdSensorService.cs" Link="Services\IIdSensorService.cs" />
<Compile Include="..\leak_test_project\Services\SentinelC28Service.cs" Link="Services\SentinelC28Service.cs" />
<Compile Include="..\leak_test_project\Services\IoBoardService.cs" Link="Services\IoBoardService.cs" />
<Compile Include="..\leak_test_project\Services\DioBoardFactory.cs" Link="Services\DioBoardFactory.cs" />
<Compile Include="..\leak_test_project\Services\TestProcessService.cs" Link="Services\TestProcessService.cs" />
<Compile Include="..\leak_test_project\Services\Board4251.cs" Link="Services\Board4251.cs" />
</ItemGroup>
</Project>

11
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfo.cs

@ -1,9 +1,10 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
// 이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
@ -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.

2
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.AssemblyInfoInputs.cache

@ -1 +1 @@
203a310cc48dd71b35ad4241fb5264cead816046c43c41fd4c03ab287b385409
dfa1bcc3dd55c3f75b42b34a72f780d712c9e1147faf3e82c45e2f51cf41885f

BIN
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.assets.cache

Binary file not shown.

BIN
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.AssemblyReference.cache

Binary file not shown.

0
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers

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

@ -1 +1 @@
5388cf90cfa775894722698f4c7e037cd8714537da7bb0136faff35db6bbd820
0c498c72e1af24f34d80cd6f85d77cb00370ba1742f248052ac2cf5deed9afa2

BIN
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.dll

Binary file not shown.

BIN
leak_test_project.Tests/obj/Debug/net472/leak_test_project.Tests.pdb

Binary file not shown.

4
leak_test_project.Tests/obj/Release/net472/.NETFramework,Version=v4.7.2.AssemblyAttributes.cs

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]

23
leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfo.cs

@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
// 런타임 버전:4.0.30319.42000
//
// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면
// 이러한 변경 내용이 손실됩니다.
// </auto-generated>
//------------------------------------------------------------------------------
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.

1
leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.AssemblyInfoInputs.cache

@ -0,0 +1 @@
acbfea79027e4ab25c0f7b2b015d0f8ac8002d7760da81e077d21947bf758347

8
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 =

BIN
leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.assets.cache

Binary file not shown.

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

Binary file not shown.

0
leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.BuildWithSkipAnalyzers

1
leak_test_project.Tests/obj/Release/net472/leak_test_project.Tests.csproj.CoreCompileInputs.cache

@ -0,0 +1 @@
00716152d706ef3c486fd25e9c4535b79d009a6ebc9ecbc7d98bc05e0e6dd29a

54
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

6
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"
}
}
}

18
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"
}
}
}

2
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": [

1
leak_test_project.slnx

@ -1,3 +1,4 @@
<Solution>
<Project Path="leak_test_project.Tests/leak_test_project.Tests.csproj" />
<Project Path="leak_test_project/leak_test_project.csproj" Id="e02e2608-51f9-4338-b1a6-e6ac94362aae" />
</Solution>

32
leak_test_project/Infrastructure/SerialProvider.cs → leak_test_project/Infrastructure/CommunicationBase.cs

@ -3,6 +3,38 @@ using System.IO.Ports;
namespace leak_test_project.Infrastructure
{
/// <summary>
/// 하드웨어 통신을 위한 추상화 인터페이스.
/// 시리얼(RS232), 이더넷(TCP/IP) 등 통신 방식에 상관없이
/// 상위 서비스에서 동일한 방식으로 하드웨어에 접근할 수 있도록 정의함.
/// </summary>
public interface ICommunication
{
/// <summary> 통신 채널 식별 이름 (예: COM1, TCP_192.168.0.1) </summary>
string Name { get; }
/// <summary> 현재 통신 채널이 열려 있는지 여부 </summary>
bool IsOpen { get; }
/// <summary> 통신 채널을 연결함 </summary>
bool Open();
/// <summary> 통신 채널 연결을 해제함 </summary>
void Close();
/// <summary> 데이터를 하드웨어로 전송함 </summary>
bool Write(string data);
/// <summary> 수신 버퍼를 비워 잔류 데이터를 제거함 </summary>
void ClearBuffer();
/// <summary> 하드웨어로부터 데이터를 수신했을 때 발생하는 이벤트 </summary>
event System.EventHandler<string> DataReceived;
/// <summary> 연결 상태가 변경되었을 때 발생하는 이벤트 (connected, disconnected) </summary>
event System.EventHandler<bool> ConnectionStatusChanged;
}
/// <summary>
/// RS232 시리얼 통신을 담당하는 구현체.
/// 연결 안정성 강화를 위한 예외 처리 및 상태 감지 로직 포함.

30
leak_test_project/Infrastructure/RealDioBoard.cs → leak_test_project/Infrastructure/DioBoardBase.cs

@ -7,6 +7,34 @@ using leak_test_project.Utils;
namespace leak_test_project.Infrastructure
{
/// <summary>
/// DIO 보드 추상화 인터페이스.
/// 실제 하드웨어(JunSystem DIO) 또는 시뮬레이션 보드를 교체 가능하게 설계.
/// </summary>
public interface IDioBoard : IDisposable
{
/// <summary>DIO 보드를 초기화합니다.</summary>
bool Initialize();
/// <summary>지정된 입력 포인트의 현재 값을 읽습니다.</summary>
bool ReadInput(string pointName);
/// <summary>지정된 출력 포인트에 값을 씁니다.</summary>
void WriteOutput(string pointName, bool value);
/// <summary>모든 입력 포인트 목록을 반환합니다.</summary>
List<DioPoint> GetInputPoints();
/// <summary>모든 출력 포인트 목록을 반환합니다.</summary>
List<DioPoint> GetOutputPoints();
/// <summary>입력 포인트의 값이 OFF→ON으로 변경되었을 때 발생합니다.</summary>
event EventHandler<DioEventArgs> InputChanged;
/// <summary>보드 동작 중 오류가 발생했을 때 발생합니다.</summary>
event EventHandler<string> ErrorOccurred;
}
/// <summary>
/// 실제 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;

34
leak_test_project/Infrastructure/ICommunication.cs

@ -1,34 +0,0 @@
namespace leak_test_project.Infrastructure
{
/// <summary>
/// 하드웨어 통신을 위한 추상화 인터페이스.
/// 시리얼(RS232), 이더넷(TCP/IP) 등 통신 방식에 상관없이
/// 상위 서비스에서 동일한 방식으로 하드웨어에 접근할 수 있도록 정의함.
/// </summary>
public interface ICommunication
{
/// <summary> 통신 채널 식별 이름 (예: COM1, TCP_192.168.0.1) </summary>
string Name { get; }
/// <summary> 현재 통신 채널이 열려 있는지 여부 </summary>
bool IsOpen { get; }
/// <summary> 통신 채널을 연결함 </summary>
bool Open();
/// <summary> 통신 채널 연결을 해제함 </summary>
void Close();
/// <summary> 데이터를 하드웨어로 전송함 </summary>
bool Write(string data);
/// <summary> 수신 버퍼를 비워 잔류 데이터를 제거함 </summary>
void ClearBuffer();
/// <summary> 하드웨어로부터 데이터를 수신했을 때 발생하는 이벤트 </summary>
event System.EventHandler<string> DataReceived;
/// <summary> 연결 상태가 변경되었을 때 발생하는 이벤트 (connected, disconnected) </summary>
event System.EventHandler<bool> ConnectionStatusChanged;
}
}

34
leak_test_project/Infrastructure/IDioBoard.cs

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using leak_test_project.Models;
namespace leak_test_project.Infrastructure
{
/// <summary>
/// DIO 보드 추상화 인터페이스.
/// 실제 하드웨어(JunSystem DIO) 또는 시뮬레이션 보드를 교체 가능하게 설계.
/// </summary>
public interface IDioBoard : IDisposable
{
/// <summary>DIO 보드를 초기화합니다.</summary>
bool Initialize();
/// <summary>지정된 입력 포인트의 현재 값을 읽습니다.</summary>
bool ReadInput(string pointName);
/// <summary>지정된 출력 포인트에 값을 씁니다.</summary>
void WriteOutput(string pointName, bool value);
/// <summary>모든 입력 포인트 목록을 반환합니다.</summary>
List<DioPoint> GetInputPoints();
/// <summary>모든 출력 포인트 목록을 반환합니다.</summary>
List<DioPoint> GetOutputPoints();
/// <summary>입력 포인트의 값이 OFF→ON으로 변경되었을 때 발생합니다.</summary>
event EventHandler<DioEventArgs> InputChanged;
/// <summary>보드 동작 중 오류가 발생했을 때 발생합니다.</summary>
event EventHandler<string> ErrorOccurred;
}
}

27
leak_test_project/Models/AppConfig.cs

@ -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;
}
}

38
leak_test_project/Models/DioPoint.cs

@ -1,38 +0,0 @@
namespace leak_test_project.Models
{
/// <summary>
/// DIO 보드의 개별 포인트(입력 또는 출력)를 나타내는 모델.
/// </summary>
public class DioPoint
{
/// <summary>포인트 이름 (예: LEFT_START, LEFT_OK, RIGHT_NG)</summary>
public string Name { get; set; }
/// <summary>설명 (예: 좌측 시작 신호)</summary>
public string Description { get; set; }
/// <summary>true: 입력, false: 출력</summary>
public bool IsInput { get; set; }
/// <summary>현재 값 (ON=true, OFF=false)</summary>
public bool Value { get; set; }
/// <summary>하드웨어 IO 포트의 실제 비트 인덱스 (0~31)</summary>
public int BitIndex { get; set; }
}
/// <summary>
/// DIO 입력 변경 시 사용되는 이벤트 인자.
/// </summary>
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;
}
}
}

26
leak_test_project/Models/InOutItem.cs

@ -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;
/// <summary>현재 값 (ON=true, OFF=false). DIO 실시간 상태 표시용.</summary>
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;
}
}

18
leak_test_project/Models/InspectData.cs

@ -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; }
}
}

31
leak_test_project/Models/ParsedData.cs

@ -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; }
/// <summary>센서 자체 판정 결과 (A=Accept, R=Reject). SPEC 교차 검증에 사용.</summary>
public string SensorJudgment { get; set; }
public ParsedData()
{
Timestamp = DateTime.Now;
}
}
}

159
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;
}
/// <summary>
/// DIO 보드의 개별 포인트(입력 또는 출력)를 나타내는 모델.
/// </summary>
public class DioPoint
{
/// <summary>포인트 이름 (예: LEFT_START, LEFT_OK, RIGHT_NG)</summary>
public string Name { get; set; }
/// <summary>설명 (예: 좌측 시작 신호)</summary>
public string Description { get; set; }
/// <summary>true: 입력, false: 출력</summary>
public bool IsInput { get; set; }
/// <summary>현재 값 (ON=true, OFF=false)</summary>
public bool Value { get; set; }
/// <summary>하드웨어 IO 포트의 실제 비트 인덱스 (0~31)</summary>
public int BitIndex { get; set; }
}
/// <summary>
/// DIO 입력 변경 시 사용되는 이벤트 인자.
/// </summary>
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;
/// <summary>현재 값 (ON=true, OFF=false). DIO 실시간 상태 표시용.</summary>
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; }
/// <summary>센서 자체 판정 결과 (A=Accept, R=Reject). SPEC 교차 검증에 사용.</summary>
public string SensorJudgment { get; set; }
public ParsedData()
{
Timestamp = DateTime.Now;
}
}
/// <summary>
/// ZMDI 센서에서 읽은 제품 ID 정보.
/// 레거시 ClsSensorReader의 필드를 기반으로 구성.
/// </summary>
public class SensorIdData
{
/// <summary>센서 메모리에서 읽은 원본 Low ID 문자열</summary>
public string LowID { get; set; } = "";
/// <summary>제조 년도</summary>
public int Year { get; set; }
/// <summary>제조 월</summary>
public int Month { get; set; }
/// <summary>제조 일</summary>
public int Day { get; set; }
/// <summary>시리얼 번호 (5자리 패딩)</summary>
public string Serial { get; set; } = "";
/// <summary>MC Line (개발/양산 구분)</summary>
public string McLine { get; set; } = "";
/// <summary>라인 번호</summary>
public string LineNo { get; set; } = "";
/// <summary>제품 구분 (Item)</summary>
public string Item { get; set; } = "";
/// <summary>최종 조합 ID (년+월+일+시리얼+라인+항목)</summary>
public string ID { get; set; } = "";
/// <summary>이전 검사 결과. F=미시험/불합격, P=합격. 불량 제품 필터링에 사용.</summary>
public string PrevResult { get; set; } = "";
}
}

39
leak_test_project/Models/SensorIdData.cs

@ -1,39 +0,0 @@
namespace leak_test_project.Models
{
/// <summary>
/// ZMDI 센서에서 읽은 제품 ID 정보.
/// 레거시 ClsSensorReader의 필드를 기반으로 구성.
/// </summary>
public class SensorIdData
{
/// <summary>센서 메모리에서 읽은 원본 Low ID 문자열</summary>
public string LowID { get; set; } = "";
/// <summary>제조 년도</summary>
public int Year { get; set; }
/// <summary>제조 월</summary>
public int Month { get; set; }
/// <summary>제조 일</summary>
public int Day { get; set; }
/// <summary>시리얼 번호 (5자리 패딩)</summary>
public string Serial { get; set; } = "";
/// <summary>MC Line (개발/양산 구분)</summary>
public string McLine { get; set; } = "";
/// <summary>라인 번호</summary>
public string LineNo { get; set; } = "";
/// <summary>제품 구분 (Item)</summary>
public string Item { get; set; } = "";
/// <summary>최종 조합 ID (년+월+일+시리얼+라인+항목)</summary>
public string ID { get; set; } = "";
/// <summary>이전 검사 결과. F=미시험/불합격, P=합격. 불량 제품 필터링에 사용.</summary>
public string PrevResult { get; set; } = "";
}
}

489
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
{
/// <summary>
/// 신규 4251 보드와의 통신을 관리하는 서비스.
/// <end> 키워드를 기준으로 데이터를 수신하며, 상태 확인 및 ID 읽기 기능을 제공함.
/// </summary>
public class Board4251Service : IDisposable
{
private readonly ICommunication _communication;
private readonly StringBuilder _receiveBuffer = new StringBuilder();
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
private TaskCompletionSource<string> _responseTcs;
public int TimeoutMs { get; set; } = 5000; // 보드가 응답을 주는데 2초 이상 걸리므로 무조건 길게 대기
private bool _shouldBeConnected = false;
private System.Timers.Timer _reconnectTimer;
public event EventHandler<string> ErrorOccurred;
public event EventHandler<bool> 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();
}
/// <summary>
/// 4251 보드의 상태를 확인합니다.
/// </summary>
/// <returns>성공 여부</returns>
public async Task<bool> 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;
}
/// <summary>
/// 4251 보드로부터 16자리 ID를 읽어옵니다.
/// </summary>
/// <returns>16자리 ID 문자열, 실패 시 null</returns>
public async Task<string> 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("<end>", 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<string> 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<string>();
_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("<end>", StringComparison.OrdinalIgnoreCase) >= 0 ||
currentContent.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0 ||
currentContent.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) >= 0 ||
currentContent.IndexOf("<ACK>OFF", StringComparison.OrdinalIgnoreCase) >= 0)
{
isComplete = true;
}
else
{
// <end>가 오지 않았더라도, 명령어 에코가 아닌 줄에서 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();
}
}
/// <summary>
/// 신규 4251 보드를 사용하여 제품 ID를 읽는 센서 서비스.
/// ZmdiSensorService와 동일한 구조로 구현되어 교체가 용이함.
/// </summary>
public class Board4251SensorService : IIdSensorService
{
private readonly Board4251Service _service;
private readonly int _sensorIndex;
public event EventHandler<string> ProgressMessage;
public event EventHandler<string> ErrorMessage;
public event EventHandler<bool> 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)에서 관리하므로 여기서 해제하지 않음
}
}
/// <summary>
/// 시리얼 통신을 기반으로 동작하는 신규 4251 DIO 보드 구현체.
/// 기존 RealDioBoard(Legacy)와 교체 가능함.
/// </summary>
public class Board4251DioBoard : IDioBoard
{
private readonly Board4251Service _service;
private readonly List<DioPoint> _inputs = new List<DioPoint>();
private readonly List<DioPoint> _outputs = new List<DioPoint>();
private bool _isDisposed = false;
#pragma warning disable 0067
public event EventHandler<DioEventArgs> InputChanged;
#pragma warning restore 0067
public event EventHandler<string> 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<DioPoint> GetInputPoints() => _inputs;
public List<DioPoint> GetOutputPoints() => _outputs;
public void Dispose()
{
if (!_isDisposed)
{
_isDisposed = true;
_service.Disconnect();
_service.Dispose();
}
}
}
}

97
leak_test_project/Services/Board4253DioBoard.cs

@ -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
{
/// <summary>
/// 시리얼 통신을 기반으로 동작하는 신규 4253 DIO 보드 구현체.
/// 기존 RealDioBoard(Legacy)와 교체 가능함.
/// </summary>
public class Board4253DioBoard : IDioBoard
{
private readonly Board4253Service _service;
private readonly List<DioPoint> _inputs = new List<DioPoint>();
private readonly List<DioPoint> _outputs = new List<DioPoint>();
private bool _isDisposed = false;
#pragma warning disable 0067
public event EventHandler<DioEventArgs> InputChanged;
#pragma warning restore 0067
public event EventHandler<string> 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<DioPoint> GetInputPoints() => _inputs;
public List<DioPoint> GetOutputPoints() => _outputs;
public void Dispose()
{
if (!_isDisposed)
{
_isDisposed = true;
_service.Disconnect();
_service.Dispose();
}
}
}
}

144
leak_test_project/Services/Board4253SensorService.cs

@ -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
{
/// <summary>
/// 신규 4253 보드를 사용하여 제품 ID를 읽는 센서 서비스.
/// ZmdiSensorService와 동일한 구조로 구현되어 교체가 용이함.
/// </summary>
public class Board4253SensorService : IIdSensorService
{
private readonly Board4253Service _service;
private readonly int _sensorIndex;
public event EventHandler<string> ProgressMessage;
public event EventHandler<string> ErrorMessage;
public event EventHandler<bool> 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)에서 관리하므로 여기서 해제하지 않음
}
}
}

261
leak_test_project/Services/Board4253Service.cs

@ -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
{
/// <summary>
/// 신규 4253 보드와의 통신을 관리하는 서비스.
/// <end> 키워드를 기준으로 데이터를 수신하며, 상태 확인 및 ID 읽기 기능을 제공함.
/// </summary>
public class Board4253Service : IDisposable
{
private readonly ICommunication _communication;
private readonly StringBuilder _receiveBuffer = new StringBuilder();
private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
private TaskCompletionSource<string> _responseTcs;
public int TimeoutMs { get; set; } = 5000; // 보드가 응답을 주는데 2초 이상 걸리므로 무조건 길게 대기
private bool _shouldBeConnected = false;
private System.Timers.Timer _reconnectTimer;
public event EventHandler<string> ErrorOccurred;
public event EventHandler<bool> 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();
}
/// <summary>
/// 4253 보드의 상태를 확인합니다.
/// </summary>
/// <returns>성공 여부</returns>
public async Task<bool> 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;
}
/// <summary>
/// 4253 보드로부터 16자리 ID를 읽어옵니다.
/// </summary>
/// <returns>16자리 ID 문자열, 실패 시 null</returns>
public async Task<string> 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("<end>", 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<string> 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<string>();
_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("<end>", StringComparison.OrdinalIgnoreCase) >= 0 ||
currentContent.IndexOf("Success", StringComparison.OrdinalIgnoreCase) >= 0 ||
currentContent.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) >= 0 ||
currentContent.IndexOf("<ACK>OFF", StringComparison.OrdinalIgnoreCase) >= 0)
{
isComplete = true;
}
else
{
// <end>가 오지 않았더라도, 명령어 에코가 아닌 줄에서 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();
}
}
}

70
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;
if (string.IsNullOrEmpty(rawData)) return;
// 헤더 분석 및 본문 추출
string body = SentinelParser.ExtractBody(rawData, out char typeCode);
_receiveBuffer += rawData;
switch (typeCode)
// \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}");

41
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로 기록되도록 이벤트 발생
@ -303,14 +308,17 @@ namespace leak_test_project.Services
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")
{

348
leak_test_project/Services/ZmdiSensorService.cs

@ -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
{
/// <summary>
/// ZMDI 센서와 시리얼 통신하여 제품 ID를 읽고 파싱하는 서비스.
/// 자동 재연결(Auto-Reconnect) 및 예외 처리 포함.
/// </summary>
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"
};
/// <summary>진행 상황 메시지 이벤트</summary>
public event EventHandler<string> ProgressMessage;
/// <summary>오류 메시지 이벤트</summary>
public event EventHandler<string> ErrorMessage;
/// <summary>연결 상태 변경 이벤트</summary>
public event EventHandler<bool> 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;
}
/// <summary>
/// ZMDI 센서에서 제품 ID를 읽고 파싱합니다.
/// 반드시 백그라운드 스레드에서 호출해야 합니다.
/// </summary>
/// <returns>성공 시 SensorIdData, 실패 시 null</returns>
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;
}
}
/// <summary>LowID 문자열을 파싱하여 년/월/일/시리얼/라인/항목을 추출합니다.</summary>
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;
}
/// <summary>명령을 전송하고 응답을 수신합니다. 최대 3회 재시도.</summary>
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;
}
}
}
/// <summary>명령 전송 후 CR+LF 응답을 500ms 타임아웃으로 대기합니다.</summary>
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<string> 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;
}
}
}
}

55
leak_test_project/Utils/DioConfigParser.cs → 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";

61
leak_test_project/Utils/ConfigManager.cs

@ -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}");
}
}
}
}

61
leak_test_project/Utils/CsvExporter.cs

@ -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
{
/// <summary>
/// 데이터를 CSV 파일로 내보내는 유틸리티
/// </summary>
public static class CsvExporter
{
/// <summary>
/// 컬렉션 데이터를 CSV 파일로 저장함
/// </summary>
/// <typeparam name="T">데이터 모델 클래스</typeparam>
/// <param name="items">내보낼 데이터 목록</param>
/// <param name="filePath">저장할 파일 경로</param>
public static bool ExportToCsv<T>(IEnumerable<T> 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;
}
}
}
}

88
leak_test_project/Utils/FileLogger.cs

@ -1,88 +0,0 @@
using System;
using System.IO;
using System.Text;
using leak_test_project.Models;
namespace leak_test_project.Utils
{
/// <summary>
/// 통신 로그 및 시스템 이력을 파일로 저장하는 유틸리티
/// </summary>
public static class FileLogger
{
private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
private static readonly object _lock = new object();
/// <summary>
/// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함
/// </summary>
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}");
}
}
}
/// <summary>
/// 단순 텍스트 로그 (기존 호환성 유지용)
/// </summary>
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}");
}
}
}
/// <summary>
/// CSV 값 이스케이프: 쉼표, 따옴표, 줄바꿈이 포함된 값을 안전하게 감싸줌
/// </summary>
private static string Esc(string value)
{
if (string.IsNullOrEmpty(value)) return "";
if (value.Contains(",") || value.Contains("\"") || value.Contains("\n"))
return $"\"{value.Replace("\"", "\"\"")}\"";
return value;
}
}
}

93
leak_test_project/Utils/LogParser.cs

@ -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<InspectData> ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "")
{
var results = new List<InspectData>();
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();
}
}
}

228
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
{
/// <summary>
/// 통신 로그 및 시스템 이력을 파일로 저장하는 유틸리티
/// </summary>
public static class FileLogger
{
private static readonly string LogDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
private static readonly object _lock = new object();
/// <summary>
/// 검사 데이터를 Logs/yyyy-MM-dd.csv 파일에 저장함
/// </summary>
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}");
}
}
}
/// <summary>
/// 단순 텍스트 로그 (기존 호환성 유지용)
/// </summary>
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}");
}
}
}
/// <summary>
/// CSV 값 이스케이프: 쉼표, 따옴표, 줄바꿈이 포함된 값을 안전하게 감싸줌
/// </summary>
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<InspectData> ParseLogs(DateTime start, DateTime end, string judgmentFilter = "[전체]", string serialFilter = "")
{
var results = new List<InspectData>();
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();
}
}
/// <summary>
/// 데이터를 CSV 파일로 내보내는 유틸리티
/// </summary>
public static class CsvExporter
{
/// <summary>
/// 컬렉션 데이터를 CSV 파일로 저장함
/// </summary>
/// <typeparam name="T">데이터 모델 클래스</typeparam>
/// <param name="items">내보낼 데이터 목록</param>
/// <param name="filePath">저장할 파일 경로</param>
public static bool ExportToCsv<T>(IEnumerable<T> 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;
}
}
}
}

29
leak_test_project/Utils/SentinelCrc8.cs

@ -1,29 +0,0 @@
namespace leak_test_project.Utils
{
/// <summary>
/// Sentinel C28 전용 CRC-8 계산 유틸리티.
/// 문서 4페이지의 에러 체크 규격(8-Bit CRC in HEX)을 구현함.
/// </summary>
public static class SentinelCrc8
{
/// <summary>
/// 입력된 데이터의 8비트 CRC 값을 HEX 문자열로 계산함.
/// </summary>
/// <param name="data">CRC를 계산할 문자열</param>
/// <returns>2자리의 HEX 문자열 (예: "A5")</returns>
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");
}
}
}

133
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]);
}
typeCode = 'R'; // Result
Debug.WriteLine($"[C28 Parser] Final Result pattern matched.");
return input.Trim();
}
// 두 번째 탭이 있는지 확인
int secondTabIndex = input.IndexOf('\t', firstTabIndex + 1);
if (secondTabIndex != -1 && (secondTabIndex - firstTabIndex) <= 2)
// 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();
}
/// <summary>
@ -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;
}
@ -103,6 +119,8 @@ namespace leak_test_project.Utils
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
for (int i = 9; i < fields.Length; i++)
@ -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
{
/// <summary>
/// 입력된 데이터의 8비트 CRC 값을 HEX 문자열로 계산함.
/// </summary>
/// <param name="data">CRC를 계산할 문자열</param>
/// <returns>2자리의 HEX 문자열 (예: "A5")</returns>
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");
}
}
}

103
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);
// ID 센서 서비스 (4251 보드 단일 통신)
_leftZmdiSerial = new SerialProvider(config.Board4251Port, config.Board4251BaudRate);
var sharedService = new Board4251Service(_leftZmdiSerial) { TimeoutMs = config.Board4251Timeout };
_leftZmdi = new ZmdiSensorService(_leftZmdiSerial, 0);
_rightZmdi = new ZmdiSensorService(_rightZmdiSerial, 1);
}
_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)
{

69
leak_test_project/Views/CommunicationWindow.xaml

@ -62,33 +62,17 @@
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- LEFT COLUMN: SETTINGS -->
<!-- LEFT COLUMN: 4251 BOARD SETTINGS -->
<StackPanel Grid.Column="0" Margin="10,0">
<TextBlock Text="[ 하드웨어 보드 선택 ]" FontSize="16" FontWeight="Bold" Foreground="#2E86C1" Margin="0,0,0,15"/>
<TextBlock Text="[ 4251 보드 설정 ]" FontSize="16" FontWeight="Bold" Foreground="#2E86C1" Margin="0,0,0,15"/>
<!-- ID Sensor Type Selection -->
<!-- Board 4251 BaudRate -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<Border Style="{StaticResource ParamHeaderBorder}">
<TextBlock Text="ID Sensor Type" Style="{StaticResource ParamHeaderLabel}"/>
<TextBlock Text="4251 BaudRate" Style="{StaticResource ParamHeaderLabel}"/>
</Border>
<Border Style="{StaticResource ParamValueBorder}">
<ComboBox x:Name="cbIdSensorType" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold">
<ComboBoxItem Content="ZMDI Sensor"/>
<ComboBoxItem Content="Board 4253"/>
</ComboBox>
</Border>
</StackPanel>
<TextBlock Text="[ ZMDI ID 센서 설정 ]" FontSize="16" FontWeight="Bold" Foreground="#2E86C1" Margin="0,20,0,15"/>
<!-- ZMDI BaudRate -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<Border Style="{StaticResource ParamHeaderBorder}">
<TextBlock Text="ZMDI BaudRate" Style="{StaticResource ParamHeaderLabel}"/>
</Border>
<Border Style="{StaticResource ParamValueBorder}">
<ComboBox x:Name="cbZmdiBaudRate" SelectedIndex="1" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold">
<ComboBox x:Name="cbBoard4251BaudRate" SelectedIndex="3" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold">
<ComboBoxItem Content="9600"/>
<ComboBoxItem Content="19200"/>
<ComboBoxItem Content="38400"/>
@ -97,23 +81,13 @@
</Border>
</StackPanel>
<!-- Left Port -->
<!-- Board 4251 Port -->
<StackPanel Orientation="Horizontal" Margin="0,5">
<Border Style="{StaticResource ParamHeaderBorder}">
<TextBlock Text="Left Port" Style="{StaticResource ParamHeaderLabel}"/>
<TextBlock Text="4251 Port" Style="{StaticResource ParamHeaderLabel}"/>
</Border>
<Border Style="{StaticResource ParamValueBorder}">
<ComboBox x:Name="cbLeftPort" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold"/>
</Border>
</StackPanel>
<!-- Right Port -->
<StackPanel Orientation="Horizontal" Margin="0,5">
<Border Style="{StaticResource ParamHeaderBorder}">
<TextBlock Text="Right Port" Style="{StaticResource ParamHeaderLabel}"/>
</Border>
<Border Style="{StaticResource ParamValueBorder}">
<ComboBox x:Name="cbRightPort" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold"/>
<ComboBox x:Name="cbBoard4251Port" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold"/>
</Border>
</StackPanel>
</StackPanel>
@ -146,33 +120,6 @@
<ComboBox x:Name="cbSensorPort" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold"/>
</Border>
</StackPanel>
<TextBlock Text="[ 4253 보드 설정 ]" FontSize="16" FontWeight="Bold" Foreground="#2E86C1" Margin="0,20,0,15"/>
<!-- Board 4253 BaudRate -->
<StackPanel Orientation="Horizontal" Margin="0,8">
<Border Style="{StaticResource ParamHeaderBorder}">
<TextBlock Text="4253 BaudRate" Style="{StaticResource ParamHeaderLabel}"/>
</Border>
<Border Style="{StaticResource ParamValueBorder}">
<ComboBox x:Name="cbBoard4253BaudRate" SelectedIndex="3" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold">
<ComboBoxItem Content="9600"/>
<ComboBoxItem Content="19200"/>
<ComboBoxItem Content="38400"/>
<ComboBoxItem Content="115200"/>
</ComboBox>
</Border>
</StackPanel>
<!-- Board 4253 Port -->
<StackPanel Orientation="Horizontal" Margin="0,5">
<Border Style="{StaticResource ParamHeaderBorder}">
<TextBlock Text="4253 Port" Style="{StaticResource ParamHeaderLabel}"/>
</Border>
<Border Style="{StaticResource ParamValueBorder}">
<ComboBox x:Name="cbBoard4253Port" Width="178" BorderThickness="0" VerticalContentAlignment="Center" FontSize="16" FontWeight="Bold"/>
</Border>
</StackPanel>
</StackPanel>
</Grid>
</ScrollViewer>

50
leak_test_project/Views/CommunicationWindow.xaml.cs

@ -19,30 +19,14 @@ namespace leak_test_project.Views
{
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();

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

Binary file not shown.

6
leak_test_project/bin/Debug/leak_test_project.application

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="leak_test_project.application" version="1.0.0.13" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<assemblyIdentity name="leak_test_project.application" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="leak_test_project" asmv2:product="leak_test_project" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" />
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
@ -8,13 +8,13 @@
</compatibleFrameworks>
<dependency>
<dependentAssembly dependencyType="install" codebase="leak_test_project.exe.manifest" size="3936">
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.13" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>6+b0r1Gncm2nD/TjBHVCWtS6O3CAXOdxRTwtx09ap8E=</dsig:DigestValue>
<dsig:DigestValue>s3poAPfszmsLXNBQF9a37V3LMo83TC9ce3WkqXwtkWg=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

BIN
leak_test_project/bin/Debug/leak_test_project.exe

Binary file not shown.

6
leak_test_project/bin/Debug/leak_test_project.exe.manifest

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.13" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<application />
<entryPoint>
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
@ -42,14 +42,14 @@
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="206800">
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="209920">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>WIlRaRAkyOL/i77TGkCYPDQrXlpbIq1b8rVePHEWnCQ=</dsig:DigestValue>
<dsig:DigestValue>yOydaKo18mXp1TtjAfyZ+gxKaCmqKNcv1EZd3u2MxW4=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

BIN
leak_test_project/bin/Debug/leak_test_project.pdb

Binary file not shown.

12
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:

2
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

BIN
leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/PCI-Dask.dll.deploy

Binary file not shown.

6
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 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
</startup>
</configuration>

BIN
leak_test_project/bin/Release/app.publish/Application Files/leak_test_project_1_0_0_22/leak_test_project.exe.deploy

Binary file not shown.

74
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 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<application />
<entryPoint>
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<commandLine file="leak_test_project.exe" parameters="" />
</entryPoint>
<trustInfo>
<security>
<applicationRequestMinimum>
<PermissionSet Unrestricted="true" ID="Custom" SameSite="site" />
<defaultAssemblyRequest permissionSetReference="Custom" />
</applicationRequestMinimum>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!--
UAC 매니페스트 옵션
Windows 사용자 계정 컨트롤 수준을 변경하려면
requestedExecutionLevel 노드를 다음 중 하나로 바꾸세요.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
이전 버전과의 호환성을 위해 파일 및 레지스트리 가상화를 사용하려면
requestedExecutionLevel 노드를 삭제하세요.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<dependency>
<dependentOS>
<osVersionInfo>
<os majorVersion="5" minorVersion="1" buildNumber="2600" servicePackMajor="0" />
</osVersionInfo>
</dependentOS>
</dependency>
<dependency>
<dependentAssembly dependencyType="preRequisite" allowDelayedBinding="true">
<assemblyIdentity name="Microsoft.Windows.CommonLanguageRuntime" version="4.0.30319.0" />
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="173568">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>0Y9/ht3hpE+fHxvoEjUYeZTPBCNYvBFnq7mbbLVmzdQ=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>
<file name="leak_test_project.exe.config" size="189">
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>R+Wg8QGvQVHX8T0ta/qbhH1bXkqY0fRnS3wBV3J0bN8=</dsig:DigestValue>
</hash>
</file>
<file name="PCI-Dask.dll" size="335872">
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>pDB54gSYV5M/tu2DZtDUj13HUp3HrRLSNr97K/nGHh4=</dsig:DigestValue>
</hash>
</file>
</asmv1:assembly>

3
leak_test_project/bin/Release/app.publish/autorun.inf

@ -0,0 +1,3 @@
[autorun]
open=setup.exe
icon=setup.exe,0

21
leak_test_project/bin/Release/app.publish/leak_test_project.application

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="leak_test_project.application" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="leak_test_project" asmv2:product="leak_test_project" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" />
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
<framework targetVersion="4.7.2" profile="Full" supportedRuntime="4.0.30319" />
</compatibleFrameworks>
<dependency>
<dependentAssembly dependencyType="install" codebase="Application Files\leak_test_project_1_0_0_22\leak_test_project.exe.manifest" size="3936">
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>aS0o/xVk0fJXJbjmEymfDZePIzgCcCd8N6ZW9l5KEAM=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>
</asmv1:assembly>

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

Binary file not shown.

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

Binary file not shown.

16
leak_test_project/bin/Release/config.xml

@ -1,14 +1,14 @@
<?xml version="1.0"?>
<AppConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SelectedIdSensor>Board4253</SelectedIdSensor>
<LeftPort>COM1</LeftPort>
<RightPort>COM1</RightPort>
<SelectedIdSensor>Board4251</SelectedIdSensor>
<LeftPort>COM9</LeftPort>
<RightPort>COM8</RightPort>
<ZmdiBaudRate>19200</ZmdiBaudRate>
<SensorPort>COM1</SensorPort>
<SensorBaudRate>9600</SensorBaudRate>
<Board4253Port>COM11</Board4253Port>
<Board4253BaudRate>115200</Board4253BaudRate>
<Board4253Timeout>500</Board4253Timeout>
<SpecUL>0.1</SpecUL>
<SpecLL>-0.15</SpecLL>
<Board4251Port>COM11</Board4251Port>
<Board4251BaudRate>115200</Board4251BaudRate>
<Board4251Timeout>5000</Board4251Timeout>
<SpecUL>1</SpecUL>
<SpecLL>-1</SpecLL>
</AppConfig>

6
leak_test_project/bin/Release/leak_test_project.application

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="leak_test_project.application" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<assemblyIdentity name="leak_test_project.application" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="leak_test_project" asmv2:product="leak_test_project" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" />
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
@ -8,13 +8,13 @@
</compatibleFrameworks>
<dependency>
<dependentAssembly dependencyType="install" codebase="leak_test_project.exe.manifest" size="3936">
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>JHj2v9ObfsxRnMeInoMVS54QBnfDXrD+U8YIz4SZjn0=</dsig:DigestValue>
<dsig:DigestValue>aS0o/xVk0fJXJbjmEymfDZePIzgCcCd8N6ZW9l5KEAM=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

BIN
leak_test_project/bin/Release/leak_test_project.exe

Binary file not shown.

6
leak_test_project/bin/Release/leak_test_project.exe.manifest

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<application />
<entryPoint>
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
@ -42,14 +42,14 @@
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="185296">
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="173568">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>n0WfpmBUJxy+jy5UyVtMgokEqYG/uPakh4PhKDgvg7k=</dsig:DigestValue>
<dsig:DigestValue>0Y9/ht3hpE+fHxvoEjUYeZTPBCNYvBFnq7mbbLVmzdQ=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

BIN
leak_test_project/bin/Release/leak_test_project.pdb

Binary file not shown.

30
leak_test_project/leak_test_project.csproj

@ -28,7 +28,7 @@
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<AutorunEnabled>true</AutorunEnabled>
<ApplicationRevision>19</ApplicationRevision>
<ApplicationRevision>22</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
@ -63,7 +63,7 @@
<GenerateManifests>true</GenerateManifests>
</PropertyGroup>
<PropertyGroup>
<SignManifests>true</SignManifests>
<SignManifests>false</SignManifests>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.Setup.Configuration.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
@ -118,33 +118,19 @@
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Infrastructure\ICommunication.cs" />
<Compile Include="Infrastructure\IDioBoard.cs" />
<Compile Include="Infrastructure\CommunicationBase.cs" />
<Compile Include="Infrastructure\DioBoardBase.cs" />
<Compile Include="Infrastructure\Dask.cs" />
<Compile Include="Infrastructure\RealDioBoard.cs" />
<Compile Include="Infrastructure\SerialProvider.cs" />
<Compile Include="Services\SentinelC28Service.cs" />
<Compile Include="Services\Board4253Service.cs" />
<Compile Include="Services\Board4253DioBoard.cs" />
<Compile Include="Services\Board4253SensorService.cs" />
<Compile Include="Services\Board4251.cs" />
<Compile Include="Services\IIdSensorService.cs" />
<Compile Include="Services\IoBoardService.cs" />
<Compile Include="Services\DioBoardFactory.cs" />
<Compile Include="Services\ZmdiSensorService.cs" />
<Compile Include="Services\TestProcessService.cs" />
<Compile Include="Utils\SentinelCrc8.cs" />
<Compile Include="Utils\SentinelParser.cs" />
<Compile Include="Utils\FileLogger.cs" />
<Compile Include="Utils\ConfigManager.cs" />
<Compile Include="Utils\LogParser.cs" />
<Compile Include="Utils\CsvExporter.cs" />
<Compile Include="Utils\DioConfigParser.cs" />
<Compile Include="Models\ParsedData.cs" />
<Compile Include="Models\InOutItem.cs" />
<Compile Include="Models\AppConfig.cs" />
<Compile Include="Models\InspectData.cs" />
<Compile Include="Models\DioPoint.cs" />
<Compile Include="Models\SensorIdData.cs" />
<Compile Include="Utils\LogUtils.cs" />
<Compile Include="Utils\ConfigHelper.cs" />
<Compile Include="Models\ProjectModels.cs" />
<Compile Include="Views\HomeView.xaml.cs">
<DependentUpon>HomeView.xaml</DependentUpon>
</Compile>

BIN
leak_test_project/obj/Debug/App.baml

Binary file not shown.

BIN
leak_test_project/obj/Debug/MainWindow.baml

Binary file not shown.

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

Binary file not shown.

10
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"
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
@ -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));

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

Binary file not shown.

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

Binary file not shown.

2
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"
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.

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

Binary file not shown.

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

Binary file not shown.

6
leak_test_project/obj/Debug/leak_test_project.application

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="leak_test_project.application" version="1.0.0.13" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<assemblyIdentity name="leak_test_project.application" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="leak_test_project" asmv2:product="leak_test_project" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" />
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
@ -8,13 +8,13 @@
</compatibleFrameworks>
<dependency>
<dependentAssembly dependencyType="install" codebase="leak_test_project.exe.manifest" size="3936">
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.13" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>6+b0r1Gncm2nD/TjBHVCWtS6O3CAXOdxRTwtx09ap8E=</dsig:DigestValue>
<dsig:DigestValue>s3poAPfszmsLXNBQF9a37V3LMo83TC9ce3WkqXwtkWg=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

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

Binary file not shown.

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

@ -1 +1 @@
e34b793bb8bcf09fd6cf0746c906a5cb6a41ebb454d1a15e8953c71a7b0a3a10
5a72f1b3b73a2fa11c624e6d676bc467a15e8fd47fc6d1c698f6714d64884008

BIN
leak_test_project/obj/Debug/leak_test_project.exe

Binary file not shown.

6
leak_test_project/obj/Debug/leak_test_project.exe.manifest

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.13" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<asmv1:assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<application />
<entryPoint>
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
@ -42,14 +42,14 @@
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="206800">
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="leak_test_project.exe" size="209920">
<assemblyIdentity name="leak_test_project" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>WIlRaRAkyOL/i77TGkCYPDQrXlpbIq1b8rVePHEWnCQ=</dsig:DigestValue>
<dsig:DigestValue>yOydaKo18mXp1TtjAfyZ+gxKaCmqKNcv1EZd3u2MxW4=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

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

Binary file not shown.

BIN
leak_test_project/obj/Debug/leak_test_project.pdb

Binary file not shown.

8
leak_test_project/obj/Debug/leak_test_project_MarkupCompile.cache

@ -10,10 +10,10 @@ none
false
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

BIN
leak_test_project/obj/Release/DesignTimeResolveAssemblyReferencesInput.cache

Binary file not shown.

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

Binary file not shown.

74
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"
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
@ -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

74
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"
//------------------------------------------------------------------------------
// <auto-generated>
// 이 코드는 도구를 사용하여 생성되었습니다.
@ -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

6
leak_test_project/obj/Release/leak_test_project.application

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
<assemblyIdentity name="leak_test_project.application" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<assemblyIdentity name="leak_test_project.application" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
<description asmv2:publisher="leak_test_project" asmv2:product="leak_test_project" xmlns="urn:schemas-microsoft-com:asm.v1" />
<deployment install="true" mapFileExtensions="true" />
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
@ -8,13 +8,13 @@
</compatibleFrameworks>
<dependency>
<dependentAssembly dependencyType="install" codebase="leak_test_project.exe.manifest" size="3936">
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.19" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<assemblyIdentity name="leak_test_project.exe" version="1.0.0.22" publicKeyToken="0000000000000000" language="neutral" processorArchitecture="msil" type="win32" />
<hash>
<dsig:Transforms>
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
</dsig:Transforms>
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
<dsig:DigestValue>JHj2v9ObfsxRnMeInoMVS54QBnfDXrD+U8YIz4SZjn0=</dsig:DigestValue>
<dsig:DigestValue>aS0o/xVk0fJXJbjmEymfDZePIzgCcCd8N6ZW9l5KEAM=</dsig:DigestValue>
</hash>
</dependentAssembly>
</dependency>

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

Loading…
Cancel
Save