commit
428099b20e
202 changed files with 10412 additions and 0 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,37 @@ |
|||
{ |
|||
"Version": 1, |
|||
"WorkspaceRootPath": "C:\\Users\\temp\\Desktop\\Housing\\", |
|||
"Documents": [ |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:MainWindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
} |
|||
], |
|||
"DocumentGroupContainers": [ |
|||
{ |
|||
"Orientation": 0, |
|||
"VerticalTabListWidth": 256, |
|||
"DocumentGroups": [ |
|||
{ |
|||
"DockedWidth": 200, |
|||
"SelectedChildIndex": 0, |
|||
"Children": [ |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 0, |
|||
"Title": "MainWindow.xaml.cs", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs", |
|||
"RelativeDocumentMoniker": "MainWindow.xaml.cs", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs", |
|||
"RelativeToolTip": "MainWindow.xaml.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-06-05T02:47:48.556Z", |
|||
"EditorCaption": "" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,103 @@ |
|||
{ |
|||
"Version": 1, |
|||
"WorkspaceRootPath": "C:\\Users\\temp\\Desktop\\Housing\\", |
|||
"Documents": [ |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\login\\startuploginwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:login\\startuploginwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\mainwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:mainwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\database.ini||{3B902123-F8A7-4915-9F01-361F908088D0}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:database.ini||{3B902123-F8A7-4915-9F01-361F908088D0}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\housing.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:housing.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|" |
|||
} |
|||
], |
|||
"DocumentGroupContainers": [ |
|||
{ |
|||
"Orientation": 0, |
|||
"VerticalTabListWidth": 256, |
|||
"DocumentGroups": [ |
|||
{ |
|||
"DockedWidth": 200, |
|||
"SelectedChildIndex": 2, |
|||
"Children": [ |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 3, |
|||
"Title": "Database.ini", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Database.ini", |
|||
"RelativeDocumentMoniker": "Database.ini", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Database.ini", |
|||
"RelativeToolTip": "Database.ini", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.002768|", |
|||
"WhenOpened": "2026-06-05T04:51:49.357Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 1, |
|||
"Title": "StartupLoginWindow.xaml", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Login\\StartupLoginWindow.xaml", |
|||
"RelativeDocumentMoniker": "Login\\StartupLoginWindow.xaml", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Login\\StartupLoginWindow.xaml", |
|||
"RelativeToolTip": "Login\\StartupLoginWindow.xaml", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|", |
|||
"WhenOpened": "2026-06-05T03:47:35.646Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 0, |
|||
"Title": "MainWindow.xaml", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml", |
|||
"RelativeDocumentMoniker": "MainWindow.xaml", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml", |
|||
"RelativeToolTip": "MainWindow.xaml", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|", |
|||
"WhenOpened": "2026-06-05T03:47:24.928Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 2, |
|||
"Title": "MainWindow.xaml.cs", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs", |
|||
"RelativeDocumentMoniker": "MainWindow.xaml.cs", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs", |
|||
"RelativeToolTip": "MainWindow.xaml.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvwIAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-06-05T03:47:23.114Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 4, |
|||
"Title": "Housing", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Housing.csproj", |
|||
"RelativeDocumentMoniker": "Housing.csproj", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Housing.csproj", |
|||
"RelativeToolTip": "Housing.csproj", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvxkAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|", |
|||
"WhenOpened": "2026-06-05T03:47:09.586Z", |
|||
"EditorCaption": "" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
{ |
|||
"Version": 1, |
|||
"WorkspaceRootPath": "C:\\Users\\temp\\Desktop\\Housing\\", |
|||
"Documents": [ |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\database.ini||{3B902123-F8A7-4915-9F01-361F908088D0}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:database.ini||{3B902123-F8A7-4915-9F01-361F908088D0}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\hardware.ini||{3B902123-F8A7-4915-9F01-361F908088D0}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:hardware.ini||{3B902123-F8A7-4915-9F01-361F908088D0}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:mainwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\login\\startuploginwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:login\\startuploginwindow.xaml||{F11ACC28-31D1-4C80-A34B-F4E09D3D753C}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\mainwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:mainwindow.xaml.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" |
|||
}, |
|||
{ |
|||
"AbsoluteMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|c:\\users\\temp\\desktop\\housing\\housing.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|", |
|||
"RelativeMoniker": "D:0:0:{7364A567-F115-4ACE-9207-C6FDEEF0A394}|Housing.csproj|solutionrelative:housing.csproj||{FA3CD31E-987B-443A-9B81-186104E8DAC1}|" |
|||
} |
|||
], |
|||
"DocumentGroupContainers": [ |
|||
{ |
|||
"Orientation": 0, |
|||
"VerticalTabListWidth": 256, |
|||
"DocumentGroups": [ |
|||
{ |
|||
"DockedWidth": 200, |
|||
"SelectedChildIndex": 1, |
|||
"Children": [ |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 1, |
|||
"Title": "Hardware.ini", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Hardware.ini", |
|||
"RelativeDocumentMoniker": "Hardware.ini", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Hardware.ini", |
|||
"RelativeToolTip": "Hardware.ini", |
|||
"ViewState": "AgIAAAMAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.002768|", |
|||
"WhenOpened": "2026-06-08T00:03:23.189Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 0, |
|||
"Title": "Database.ini", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Database.ini", |
|||
"RelativeDocumentMoniker": "Database.ini", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Database.ini", |
|||
"RelativeToolTip": "Database.ini", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.002768|", |
|||
"WhenOpened": "2026-06-05T04:51:49.357Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 3, |
|||
"Title": "StartupLoginWindow.xaml", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Login\\StartupLoginWindow.xaml", |
|||
"RelativeDocumentMoniker": "Login\\StartupLoginWindow.xaml", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Login\\StartupLoginWindow.xaml", |
|||
"RelativeToolTip": "Login\\StartupLoginWindow.xaml", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|", |
|||
"WhenOpened": "2026-06-05T03:47:35.646Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 2, |
|||
"Title": "MainWindow.xaml", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml", |
|||
"RelativeDocumentMoniker": "MainWindow.xaml", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml", |
|||
"RelativeToolTip": "MainWindow.xaml", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003549|", |
|||
"WhenOpened": "2026-06-05T03:47:24.928Z", |
|||
"EditorCaption": "" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 4, |
|||
"Title": "MainWindow.xaml.cs", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs", |
|||
"RelativeDocumentMoniker": "MainWindow.xaml.cs", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\MainWindow.xaml.cs", |
|||
"RelativeToolTip": "MainWindow.xaml.cs", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvwIAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", |
|||
"WhenOpened": "2026-06-05T03:47:23.114Z" |
|||
}, |
|||
{ |
|||
"$type": "Document", |
|||
"DocumentIndex": 5, |
|||
"Title": "Housing.csproj", |
|||
"DocumentMoniker": "C:\\Users\\temp\\Desktop\\Housing\\Housing.csproj", |
|||
"RelativeDocumentMoniker": "Housing.csproj", |
|||
"ToolTip": "C:\\Users\\temp\\Desktop\\Housing\\Housing.csproj", |
|||
"RelativeToolTip": "Housing.csproj", |
|||
"ViewState": "AgIAAAAAAAAAAAAAAADwvxkAAAAAAAAAAAAAAA==", |
|||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|", |
|||
"WhenOpened": "2026-06-05T03:47:09.586Z" |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,3 @@ |
|||
{ |
|||
"CurrentProjectSetting": null |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"ExpandedNodes": [ |
|||
"" |
|||
], |
|||
"SelectedNode": "\\MainWindow.xaml.cs", |
|||
"PreviewInSolutionExplorer": false |
|||
} |
|||
Binary file not shown.
@ -0,0 +1,6 @@ |
|||
<Application x:Class="Housing.App" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
|||
<Application.Resources> |
|||
</Application.Resources> |
|||
</Application> |
|||
@ -0,0 +1,16 @@ |
|||
using System.Windows; |
|||
|
|||
namespace Housing; |
|||
|
|||
public partial class App : Application |
|||
{ |
|||
protected override void OnStartup(StartupEventArgs e) |
|||
{ |
|||
base.OnStartup(e); |
|||
ShutdownMode = ShutdownMode.OnMainWindowClose; |
|||
|
|||
var mainWindow = new MainWindow(); |
|||
MainWindow = mainWindow; |
|||
mainWindow.Show(); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
[Database] |
|||
IP=127.0.0.1 |
|||
Database=Housing |
|||
DbId=sa |
|||
DbPw=your_password |
|||
Encrypt=True |
|||
TrustServerCertificate=True |
|||
Timeout=5 |
|||
@ -0,0 +1,44 @@ |
|||
[Board] |
|||
PortName=Auto |
|||
BaudRate=115200 |
|||
ReadTimeout=5000 |
|||
ConnectCommand=x00c_001101:owt28006727ea97c7801 |
|||
ReadIdCommand=x00c_001101:ow2800326003e |
|||
CalDefaultCommand=x00c_001001 |
|||
EndToken=<end> |
|||
|
|||
[Equipment] |
|||
; 목표: 장비 화면에 표시되는 현재 측정값을 읽습니다. |
|||
; V 출력: Keysight 34465A DMM 값 |
|||
; A 출력: Keysight E36233A Power Supply CH1 Current 값 |
|||
; USB 장비는 Keysight Connection Expert 또는 NI MAX에서 보이는 VISA 주소를 Resource에 넣으세요. |
|||
; 예: USB0::0x2A8D::0x1301::MY12345678::INSTR |
|||
Timeout=5000 |
|||
SettleMilliseconds=300 |
|||
|
|||
; 34465A DMM |
|||
VoltageConnection=Visa |
|||
VoltageResource= |
|||
VoltageIdnMatch=34460,34461,34465,34470,3446,3447 |
|||
VoltageSetupCommand= |
|||
VoltageReadCommand=MEAS:VOLT:DC? |
|||
|
|||
; E36233A Power Supply |
|||
CurrentConnection=Visa |
|||
CurrentResource= |
|||
CurrentIdnMatch=E362 |
|||
CurrentSetupCommand= |
|||
CurrentReadCommand=MEAS:CURR? CH1 |
|||
|
|||
; COM 포트로 잡히는 장비 예시: |
|||
; VoltageConnection=Serial |
|||
; VoltagePortName=Auto |
|||
; VoltageBaudRate=115200 |
|||
|
|||
; LAN 장비 예시: |
|||
; VoltageConnection=Tcp |
|||
; VoltageHost=192.168.0.10 |
|||
; VoltagePort=5025 |
|||
|
|||
; E36233A 파워서플라이에서 CH1 전압까지 읽고 싶을 때 예시: |
|||
; VoltageReadCommand=MEAS:VOLT? CH1 |
|||
@ -0,0 +1,25 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<OutputType>WinExe</OutputType> |
|||
<TargetFramework>net9.0-windows</TargetFramework> |
|||
<UseWPF>true</UseWPF> |
|||
<RootNamespace>Housing</RootNamespace> |
|||
<Nullable>enable</Nullable> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.3" /> |
|||
<PackageReference Include="System.IO.Ports" Version="9.0.10" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<None Update="Database.ini"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
<None Update="Hardware.ini"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
<None Update="InspectionSettings.ini"> |
|||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |
|||
</None> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,34 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio Version 17 |
|||
VisualStudioVersion = 17.0.31903.59 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Housing", "Housing.csproj", "{7364A567-F115-4ACE-9207-C6FDEEF0A394}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Debug|x64 = Debug|x64 |
|||
Debug|x86 = Debug|x86 |
|||
Release|Any CPU = Release|Any CPU |
|||
Release|x64 = Release|x64 |
|||
Release|x86 = Release|x86 |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Debug|x64.ActiveCfg = Debug|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Debug|x64.Build.0 = Debug|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Debug|x86.ActiveCfg = Debug|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Debug|x86.Build.0 = Debug|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Release|x64.ActiveCfg = Release|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Release|x64.Build.0 = Release|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Release|x86.ActiveCfg = Release|Any CPU |
|||
{7364A567-F115-4ACE-9207-C6FDEEF0A394}.Release|x86.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
EndGlobal |
|||
@ -0,0 +1,5 @@ |
|||
[Judgement] |
|||
VMin=0.00 |
|||
VMax=9.99 |
|||
AMin=0.00 |
|||
AMax=9.99 |
|||
@ -0,0 +1,188 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Window x:Class="Housing.Login.StartupLoginWindow" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
mc:Ignorable="d" |
|||
Title="Housing Login" |
|||
Width="760" |
|||
Height="860" |
|||
MinWidth="560" |
|||
MinHeight="640" |
|||
d:DesignWidth="760" |
|||
d:DesignHeight="860" |
|||
WindowStartupLocation="CenterScreen" |
|||
Background="#141414" |
|||
UseLayoutRounding="True" |
|||
SnapsToDevicePixels="True"> |
|||
<Window.Resources> |
|||
<Style x:Key="FieldLabelStyle" TargetType="TextBlock"> |
|||
<Setter Property="Foreground" Value="White"/> |
|||
<Setter Property="FontSize" Value="24"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
<Setter Property="VerticalAlignment" Value="Center"/> |
|||
</Style> |
|||
|
|||
<Style x:Key="InputStyle" TargetType="TextBox"> |
|||
<Setter Property="Height" Value="52"/> |
|||
<Setter Property="MinWidth" Value="180"/> |
|||
<Setter Property="Background" Value="Black"/> |
|||
<Setter Property="Foreground" Value="White"/> |
|||
<Setter Property="BorderBrush" Value="#DADADA"/> |
|||
<Setter Property="BorderThickness" Value="2"/> |
|||
<Setter Property="FontSize" Value="24"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
<Setter Property="HorizontalContentAlignment" Value="Center"/> |
|||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
|||
<Setter Property="Padding" Value="8,0"/> |
|||
</Style> |
|||
|
|||
<Style x:Key="CommandButtonStyle" TargetType="Button"> |
|||
<Setter Property="Height" Value="76"/> |
|||
<Setter Property="Foreground" Value="White"/> |
|||
<Setter Property="Background" Value="#666666"/> |
|||
<Setter Property="BorderBrush" Value="#DADADA"/> |
|||
<Setter Property="BorderThickness" Value="2"/> |
|||
<Setter Property="FontSize" Value="30"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
</Style> |
|||
</Window.Resources> |
|||
|
|||
<ScrollViewer VerticalScrollBarVisibility="Auto" |
|||
HorizontalScrollBarVisibility="Disabled"> |
|||
<Grid MinHeight="760" Margin="28"> |
|||
<Border MaxWidth="680" |
|||
HorizontalAlignment="Center" |
|||
VerticalAlignment="Center" |
|||
Background="#4F4F4F" |
|||
BorderBrush="#BDBDBD" |
|||
BorderThickness="2" |
|||
Padding="28"> |
|||
<Grid> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="22"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="22"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<TextBlock Grid.Row="0" |
|||
Text="Housing Assembly Login" |
|||
Foreground="White" |
|||
FontSize="36" |
|||
FontWeight="Bold" |
|||
HorizontalAlignment="Center" |
|||
TextWrapping="Wrap"/> |
|||
|
|||
<Grid Grid.Row="2" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Maker" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="MakerTextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="3" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Model" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="ModelTextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="4" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Variant 1" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="Variant1TextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="5" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Variant 2" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="Variant2TextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="6" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="ID" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="OperatorTextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="7" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="PW" Style="{StaticResource FieldLabelStyle}"/> |
|||
<PasswordBox x:Name="PasswordBox" |
|||
Grid.Column="1" |
|||
Height="52" |
|||
MinWidth="180" |
|||
Background="Black" |
|||
Foreground="White" |
|||
BorderBrush="#DADADA" |
|||
BorderThickness="2" |
|||
FontSize="24" |
|||
FontWeight="Bold" |
|||
HorizontalContentAlignment="Center" |
|||
VerticalContentAlignment="Center" |
|||
Padding="8,0"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="8" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Line" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="LineNoTextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="9" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Lot No" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="LotNoTextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Row="10" Margin="0,6"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="150"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
<TextBlock Grid.Column="0" Text="Jig No" Style="{StaticResource FieldLabelStyle}"/> |
|||
<TextBox x:Name="JigNoTextBox" Grid.Column="1" Style="{StaticResource InputStyle}"/> |
|||
</Grid> |
|||
|
|||
<Button Grid.Row="12" |
|||
Content="Log In" |
|||
Style="{StaticResource CommandButtonStyle}" |
|||
Click="LogInButton_Click"/> |
|||
</Grid> |
|||
</Border> |
|||
</Grid> |
|||
</ScrollViewer> |
|||
</Window> |
|||
@ -0,0 +1,76 @@ |
|||
using System; |
|||
using System.Windows; |
|||
|
|||
namespace Housing.Login; |
|||
|
|||
public sealed class StartupLoginWindowResult |
|||
{ |
|||
public string Maker { get; set; } = string.Empty; |
|||
public string Model { get; set; } = string.Empty; |
|||
public string Variant1 { get; set; } = string.Empty; |
|||
public string Variant2 { get; set; } = string.Empty; |
|||
public string Operator { get; set; } = string.Empty; |
|||
public string Password { get; set; } = string.Empty; |
|||
public string LineNo { get; set; } = string.Empty; |
|||
public string LotNo { get; set; } = string.Empty; |
|||
public string JigNo { get; set; } = string.Empty; |
|||
} |
|||
|
|||
public partial class StartupLoginWindow : Window |
|||
{ |
|||
private const string MasterId = "su"; |
|||
private const string MasterPassword = "su"; |
|||
|
|||
public StartupLoginWindowResult Result { get; private set; } = new(); |
|||
|
|||
public StartupLoginWindow() |
|||
{ |
|||
InitializeComponent(); |
|||
OperatorTextBox.Focus(); |
|||
} |
|||
|
|||
private void LogInButton_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
var loginId = OperatorTextBox.Text.Trim(); |
|||
var password = PasswordBox.Password; |
|||
|
|||
if (string.IsNullOrWhiteSpace(loginId)) |
|||
{ |
|||
MessageBox.Show(this, "ID를 입력하세요.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
OperatorTextBox.Focus(); |
|||
return; |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(password)) |
|||
{ |
|||
MessageBox.Show(this, "PW를 입력하세요.", "Login", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
PasswordBox.Focus(); |
|||
return; |
|||
} |
|||
|
|||
if (!string.Equals(loginId, MasterId, StringComparison.Ordinal) || |
|||
!string.Equals(password, MasterPassword, StringComparison.Ordinal)) |
|||
{ |
|||
MessageBox.Show(this, "마스터 계정만 로그인할 수 있습니다.", "Login", MessageBoxButton.OK, MessageBoxImage.Error); |
|||
PasswordBox.Clear(); |
|||
PasswordBox.Focus(); |
|||
return; |
|||
} |
|||
|
|||
Result = new StartupLoginWindowResult |
|||
{ |
|||
Maker = MakerTextBox.Text.Trim(), |
|||
Model = ModelTextBox.Text.Trim(), |
|||
Variant1 = Variant1TextBox.Text.Trim(), |
|||
Variant2 = Variant2TextBox.Text.Trim(), |
|||
Operator = loginId, |
|||
Password = password, |
|||
LineNo = LineNoTextBox.Text.Trim(), |
|||
LotNo = LotNoTextBox.Text.Trim(), |
|||
JigNo = JigNoTextBox.Text.Trim() |
|||
}; |
|||
|
|||
DialogResult = true; |
|||
Close(); |
|||
} |
|||
} |
|||
@ -0,0 +1,248 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Window x:Class="Housing.MainWindow" |
|||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
Title="Housing Assembly" |
|||
Width="1200" |
|||
Height="800" |
|||
MinWidth="840" |
|||
MinHeight="560" |
|||
WindowStartupLocation="CenterScreen" |
|||
WindowState="Maximized" |
|||
UseLayoutRounding="True" |
|||
SnapsToDevicePixels="True"> |
|||
<Window.Resources> |
|||
<Style x:Key="LabelStyle" TargetType="TextBlock"> |
|||
<Setter Property="Foreground" Value="#303030"/> |
|||
<Setter Property="FontSize" Value="20"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
<Setter Property="Margin" Value="0,0,0,10"/> |
|||
<Setter Property="TextWrapping" Value="Wrap"/> |
|||
</Style> |
|||
|
|||
<Style x:Key="InputBoxStyle" TargetType="TextBox"> |
|||
<Setter Property="Height" Value="60"/> |
|||
<Setter Property="MinWidth" Value="160"/> |
|||
<Setter Property="Background" Value="#FFFDF4"/> |
|||
<Setter Property="Foreground" Value="#111111"/> |
|||
<Setter Property="BorderBrush" Value="#B8B8B8"/> |
|||
<Setter Property="BorderThickness" Value="1"/> |
|||
<Setter Property="FontSize" Value="24"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
<Setter Property="HorizontalContentAlignment" Value="Center"/> |
|||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
|||
<Setter Property="Padding" Value="10,0"/> |
|||
</Style> |
|||
|
|||
<Style x:Key="OutputBoxStyle" TargetType="TextBox"> |
|||
<Setter Property="MinHeight" Value="220"/> |
|||
<Setter Property="Background" Value="White"/> |
|||
<Setter Property="Foreground" Value="#111111"/> |
|||
<Setter Property="BorderBrush" Value="#B8B8B8"/> |
|||
<Setter Property="BorderThickness" Value="1"/> |
|||
<Setter Property="FontSize" Value="40"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
<Setter Property="HorizontalContentAlignment" Value="Center"/> |
|||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
|||
<Setter Property="Padding" Value="12"/> |
|||
<Setter Property="IsReadOnly" Value="True"/> |
|||
</Style> |
|||
|
|||
<Style x:Key="RangeBoxStyle" TargetType="TextBox"> |
|||
<Setter Property="Width" Value="70"/> |
|||
<Setter Property="Height" Value="32"/> |
|||
<Setter Property="Background" Value="#FFFDF4"/> |
|||
<Setter Property="Foreground" Value="#111111"/> |
|||
<Setter Property="BorderBrush" Value="#B8B8B8"/> |
|||
<Setter Property="BorderThickness" Value="1"/> |
|||
<Setter Property="FontSize" Value="15"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
<Setter Property="HorizontalContentAlignment" Value="Center"/> |
|||
<Setter Property="VerticalContentAlignment" Value="Center"/> |
|||
<Setter Property="Margin" Value="6,0,0,10"/> |
|||
</Style> |
|||
|
|||
<Style x:Key="LogoutButtonStyle" TargetType="Button"> |
|||
<Setter Property="Height" Value="34"/> |
|||
<Setter Property="MinWidth" Value="92"/> |
|||
<Setter Property="Padding" Value="12,0"/> |
|||
<Setter Property="Background" Value="#6A2B2B"/> |
|||
<Setter Property="Foreground" Value="White"/> |
|||
<Setter Property="BorderBrush" Value="#6A2B2B"/> |
|||
<Setter Property="BorderThickness" Value="1"/> |
|||
<Setter Property="FontSize" Value="14"/> |
|||
<Setter Property="FontWeight" Value="Bold"/> |
|||
</Style> |
|||
</Window.Resources> |
|||
|
|||
<ScrollViewer VerticalScrollBarVisibility="Auto" |
|||
HorizontalScrollBarVisibility="Disabled" |
|||
Background="#F4F4F4"> |
|||
<Grid MinHeight="620" Margin="28"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="22"/> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="22"/> |
|||
<RowDefinition Height="*"/> |
|||
<RowDefinition Height="22"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
|
|||
<Grid Grid.Row="0"> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="Auto"/> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<TextBlock Text="Housing Assembly 검사 화면" |
|||
Foreground="#111111" |
|||
FontSize="30" |
|||
FontWeight="Bold" |
|||
VerticalAlignment="Center" |
|||
TextWrapping="Wrap"/> |
|||
|
|||
<StackPanel Grid.Column="1" |
|||
Orientation="Horizontal" |
|||
VerticalAlignment="Center"> |
|||
<TextBlock Text="Login ID" |
|||
Foreground="#404040" |
|||
FontSize="18" |
|||
FontWeight="SemiBold" |
|||
Margin="0,0,10,0" |
|||
VerticalAlignment="Center"/> |
|||
<TextBlock x:Name="OperatorTextBlock" |
|||
Text="-" |
|||
Foreground="#111111" |
|||
FontSize="18" |
|||
FontWeight="Bold" |
|||
Margin="0,0,14,0" |
|||
VerticalAlignment="Center"/> |
|||
<Button x:Name="LogoutButton" |
|||
Content="로그아웃" |
|||
Style="{StaticResource LogoutButtonStyle}" |
|||
Visibility="Collapsed" |
|||
Click="LogoutButton_Click"/> |
|||
</StackPanel> |
|||
</Grid> |
|||
|
|||
<Border Grid.Row="2" |
|||
Background="White" |
|||
BorderBrush="#D0D0D0" |
|||
BorderThickness="1" |
|||
Padding="22"> |
|||
<Grid> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="22"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<Grid Grid.Column="0"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
<TextBlock Text="PCB_Barcode 입력" Style="{StaticResource LabelStyle}"/> |
|||
<TextBox x:Name="BarcodeTextBox" |
|||
Grid.Row="1" |
|||
Style="{StaticResource InputBoxStyle}" |
|||
KeyDown="BarcodeTextBox_KeyDown" |
|||
TextChanged="BarcodeTextBox_TextChanged"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Column="2"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="Auto"/> |
|||
</Grid.RowDefinitions> |
|||
<TextBlock Text="IC_SN 출력" Style="{StaticResource LabelStyle}"/> |
|||
<TextBox x:Name="IcSnOutputTextBox" |
|||
Grid.Row="1" |
|||
Style="{StaticResource InputBoxStyle}" |
|||
Background="White" |
|||
IsReadOnly="True"/> |
|||
</Grid> |
|||
</Grid> |
|||
</Border> |
|||
|
|||
<Border Grid.Row="4" |
|||
Background="White" |
|||
BorderBrush="#D0D0D0" |
|||
BorderThickness="1" |
|||
Padding="22"> |
|||
<Grid> |
|||
<Grid.ColumnDefinitions> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="22"/> |
|||
<ColumnDefinition Width="*"/> |
|||
<ColumnDefinition Width="22"/> |
|||
<ColumnDefinition Width="*"/> |
|||
</Grid.ColumnDefinitions> |
|||
|
|||
<Grid Grid.Column="0"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="*"/> |
|||
</Grid.RowDefinitions> |
|||
<DockPanel LastChildFill="False"> |
|||
<TextBlock Text="PT_Vol_1 / V 출력" Style="{StaticResource LabelStyle}"/> |
|||
<TextBox x:Name="VMinTextBox" Style="{StaticResource RangeBoxStyle}"/> |
|||
<TextBlock Text="~" |
|||
FontSize="18" |
|||
FontWeight="Bold" |
|||
Margin="6,0,0,10" |
|||
VerticalAlignment="Center"/> |
|||
<TextBox x:Name="VMaxTextBox" Style="{StaticResource RangeBoxStyle}"/> |
|||
</DockPanel> |
|||
<TextBox x:Name="VOutputTextBox" |
|||
Grid.Row="1" |
|||
Text="0.00" |
|||
Style="{StaticResource OutputBoxStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Column="2"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="*"/> |
|||
</Grid.RowDefinitions> |
|||
<DockPanel LastChildFill="False"> |
|||
<TextBlock Text="PT_Current_1 / A 출력" Style="{StaticResource LabelStyle}"/> |
|||
<TextBox x:Name="AMinTextBox" Style="{StaticResource RangeBoxStyle}"/> |
|||
<TextBlock Text="~" |
|||
FontSize="18" |
|||
FontWeight="Bold" |
|||
Margin="6,0,0,10" |
|||
VerticalAlignment="Center"/> |
|||
<TextBox x:Name="AMaxTextBox" Style="{StaticResource RangeBoxStyle}"/> |
|||
</DockPanel> |
|||
<TextBox x:Name="AOutputTextBox" |
|||
Grid.Row="1" |
|||
Text="0.00" |
|||
Style="{StaticResource OutputBoxStyle}"/> |
|||
</Grid> |
|||
|
|||
<Grid Grid.Column="4"> |
|||
<Grid.RowDefinitions> |
|||
<RowDefinition Height="Auto"/> |
|||
<RowDefinition Height="*"/> |
|||
</Grid.RowDefinitions> |
|||
<TextBlock Text="Result / 결과 출력" Style="{StaticResource LabelStyle}"/> |
|||
<TextBox x:Name="ResultOutputTextBox" |
|||
Grid.Row="1" |
|||
Style="{StaticResource OutputBoxStyle}" |
|||
FontSize="22" |
|||
FontWeight="SemiBold" |
|||
HorizontalContentAlignment="Left" |
|||
VerticalContentAlignment="Top" |
|||
TextWrapping="Wrap" |
|||
AcceptsReturn="True" |
|||
VerticalScrollBarVisibility="Auto"/> |
|||
</Grid> |
|||
</Grid> |
|||
</Border> |
|||
|
|||
<Grid Grid.Row="6"/> |
|||
</Grid> |
|||
</ScrollViewer> |
|||
</Window> |
|||
@ -0,0 +1,337 @@ |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using System.Windows; |
|||
using System.Windows.Controls; |
|||
using System.Windows.Input; |
|||
using System.Windows.Threading; |
|||
using Housing.Login; |
|||
using Housing.Models; |
|||
using Housing.Services; |
|||
|
|||
namespace Housing; |
|||
|
|||
public partial class MainWindow : Window |
|||
{ |
|||
private const string BarcodeRequiredMessage = "PCB_Barcode를 입력하고 Enter를 눌러주세요."; |
|||
private const string BoardReadyMessage = "Enter를 누르면 보드 연결 테스트를 시작합니다."; |
|||
private const string DefaultOutputValue = "0.00"; |
|||
|
|||
private StartupLoginWindowResult _loginResult = new(); |
|||
private InspectionJudgementSettings _inspectionSettings; |
|||
private bool _isStartupLoginShown; |
|||
private bool _isBoardTestRunning; |
|||
|
|||
public MainWindow() |
|||
{ |
|||
_inspectionSettings = InspectionSettingsStore.Load(GetInspectionSettingsIniPath()); |
|||
|
|||
InitializeComponent(); |
|||
ApplyInspectionSettingsToTextBoxes(); |
|||
ApplyLoginResult(); |
|||
ResetScreen(); |
|||
} |
|||
|
|||
protected override void OnContentRendered(EventArgs e) |
|||
{ |
|||
base.OnContentRendered(e); |
|||
|
|||
if (_isStartupLoginShown) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
_isStartupLoginShown = true; |
|||
if (!ShowLoginPopup()) |
|||
{ |
|||
Close(); |
|||
} |
|||
} |
|||
|
|||
private void BarcodeTextBox_TextChanged(object sender, TextChangedEventArgs e) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(BarcodeTextBox.Text)) |
|||
{ |
|||
IcSnOutputTextBox.Clear(); |
|||
ResultOutputTextBox.Text = BarcodeRequiredMessage; |
|||
return; |
|||
} |
|||
|
|||
ResultOutputTextBox.Text = BoardReadyMessage; |
|||
} |
|||
|
|||
private async void BarcodeTextBox_KeyDown(object sender, KeyEventArgs e) |
|||
{ |
|||
if (e.Key != Key.Enter && e.Key != Key.Return) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
e.Handled = true; |
|||
await RunBoardSequenceAsync(); |
|||
} |
|||
|
|||
public async Task OnEquipmentStartButtonPressedAsync() |
|||
{ |
|||
await RunBoardSequenceAsync(); |
|||
} |
|||
|
|||
private async Task RunBoardSequenceAsync() |
|||
{ |
|||
if (_isBoardTestRunning) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (string.IsNullOrWhiteSpace(BarcodeTextBox.Text)) |
|||
{ |
|||
ResultOutputTextBox.Text = BarcodeRequiredMessage; |
|||
FocusBarcodeInput(); |
|||
return; |
|||
} |
|||
|
|||
if (!TryApplyInspectionSettingsFromTextBoxes()) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
_isBoardTestRunning = true; |
|||
SetInputEnabled(false); |
|||
|
|||
ResultOutputTextBox.Text = |
|||
"보드 테스트 시작\r\n" + |
|||
"1. 시료 연결 확인\r\n" + |
|||
"2. IC_SN 읽기\r\n" + |
|||
"3. CAL DEFAULT 조건 설정\r\n" + |
|||
"4. 계측 장비에서 V/A 읽기"; |
|||
|
|||
var boardSettings = BoardHardwareSettings.Load(GetHardwareIniPath()); |
|||
var equipmentSettings = EquipmentMeasurementSettings.Load(GetHardwareIniPath()); |
|||
var service = new BoardTestService(boardSettings, equipmentSettings); |
|||
var measurement = await service.RunAsync(); |
|||
|
|||
IcSnOutputTextBox.Text = measurement.IcSn; |
|||
VOutputTextBox.Text = FormatDecimal(measurement.Voltage); |
|||
AOutputTextBox.Text = FormatDecimal(measurement.Current); |
|||
|
|||
var resultCode = _inspectionSettings.IsPass(measurement.Voltage, measurement.Current) ? "OK" : "NG"; |
|||
ResultOutputTextBox.Text = $"{resultCode}\r\n{measurement.RawLog}"; |
|||
|
|||
await SaveCurrentResultAsync(resultCode); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
ResultOutputTextBox.Text = $"보드 테스트 실패: {ex.Message}"; |
|||
} |
|||
finally |
|||
{ |
|||
_isBoardTestRunning = false; |
|||
SetInputEnabled(true); |
|||
FocusBarcodeInput(); |
|||
} |
|||
} |
|||
|
|||
private void LogoutButton_Click(object sender, RoutedEventArgs e) |
|||
{ |
|||
ResetScreen(); |
|||
_loginResult = new StartupLoginWindowResult(); |
|||
OperatorTextBlock.Text = "-"; |
|||
LogoutButton.Visibility = Visibility.Collapsed; |
|||
|
|||
if (!ShowLoginPopup()) |
|||
{ |
|||
Close(); |
|||
} |
|||
} |
|||
|
|||
private bool ShowLoginPopup() |
|||
{ |
|||
var loginWindow = new StartupLoginWindow |
|||
{ |
|||
Owner = this |
|||
}; |
|||
|
|||
if (loginWindow.ShowDialog() != true) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
_loginResult = loginWindow.Result; |
|||
ApplyLoginResult(); |
|||
ResetScreen(); |
|||
return true; |
|||
} |
|||
|
|||
private void ApplyLoginResult() |
|||
{ |
|||
OperatorTextBlock.Text = string.IsNullOrWhiteSpace(_loginResult.Operator) ? "-" : _loginResult.Operator; |
|||
LogoutButton.Visibility = string.IsNullOrWhiteSpace(_loginResult.Operator) |
|||
? Visibility.Collapsed |
|||
: Visibility.Visible; |
|||
} |
|||
|
|||
private void ResetScreen() |
|||
{ |
|||
BarcodeTextBox.Clear(); |
|||
IcSnOutputTextBox.Clear(); |
|||
VOutputTextBox.Text = DefaultOutputValue; |
|||
AOutputTextBox.Text = DefaultOutputValue; |
|||
ResultOutputTextBox.Text = BarcodeRequiredMessage; |
|||
FocusBarcodeInput(); |
|||
} |
|||
|
|||
private void SetInputEnabled(bool isEnabled) |
|||
{ |
|||
BarcodeTextBox.IsEnabled = isEnabled; |
|||
VMinTextBox.IsEnabled = isEnabled; |
|||
VMaxTextBox.IsEnabled = isEnabled; |
|||
AMinTextBox.IsEnabled = isEnabled; |
|||
AMaxTextBox.IsEnabled = isEnabled; |
|||
LogoutButton.IsEnabled = isEnabled; |
|||
} |
|||
|
|||
private void FocusBarcodeInput() |
|||
{ |
|||
Dispatcher.BeginInvoke(new Action(() => |
|||
{ |
|||
BarcodeTextBox.Focus(); |
|||
Keyboard.Focus(BarcodeTextBox); |
|||
BarcodeTextBox.SelectAll(); |
|||
}), DispatcherPriority.Input); |
|||
} |
|||
|
|||
private async Task SaveCurrentResultAsync(string resultCode) |
|||
{ |
|||
try |
|||
{ |
|||
var repository = new HousingAssemblyRepository(DatabaseSettings.Load(GetDatabaseIniPath())); |
|||
var record = CreateCurrentRecord(resultCode); |
|||
await repository.InsertAsync(record); |
|||
|
|||
ResultOutputTextBox.Text += "\r\nDB 저장 완료"; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
ResultOutputTextBox.Text += $"\r\nDB 저장 실패: {ex.Message}"; |
|||
} |
|||
} |
|||
|
|||
private HousingAssemblyRecord CreateCurrentRecord(string resultCode) |
|||
{ |
|||
var barcode = BarcodeTextBox.Text.Trim(); |
|||
var icSn = IcSnOutputTextBox.Text.Trim(); |
|||
|
|||
if (string.IsNullOrWhiteSpace(icSn)) |
|||
{ |
|||
icSn = barcode; |
|||
IcSnOutputTextBox.Text = icSn; |
|||
} |
|||
|
|||
return new HousingAssemblyRecord |
|||
{ |
|||
IcSn = icSn, |
|||
PcbBarcode = barcode, |
|||
Maker = _loginResult.Maker, |
|||
Model = _loginResult.Model, |
|||
Variant1 = _loginResult.Variant1, |
|||
Variant2 = _loginResult.Variant2, |
|||
Operator = _loginResult.Operator, |
|||
Password = _loginResult.Password, |
|||
ProductionDate = DateTime.Now, |
|||
Line = _loginResult.LineNo, |
|||
LotNo = _loginResult.LotNo, |
|||
JigNo = _loginResult.JigNo, |
|||
PtVol1 = ParseDecimal(VOutputTextBox.Text), |
|||
PtCurrent1 = ParseDecimal(AOutputTextBox.Text), |
|||
Result = resultCode |
|||
}; |
|||
} |
|||
|
|||
private void ApplyInspectionSettingsToTextBoxes() |
|||
{ |
|||
VMinTextBox.Text = FormatDecimal(_inspectionSettings.VMin); |
|||
VMaxTextBox.Text = FormatDecimal(_inspectionSettings.VMax); |
|||
AMinTextBox.Text = FormatDecimal(_inspectionSettings.AMin); |
|||
AMaxTextBox.Text = FormatDecimal(_inspectionSettings.AMax); |
|||
} |
|||
|
|||
private bool TryApplyInspectionSettingsFromTextBoxes() |
|||
{ |
|||
if (!TryReadRangeValue(VMinTextBox, "V 최소값", out var vMin) || |
|||
!TryReadRangeValue(VMaxTextBox, "V 최대값", out var vMax) || |
|||
!TryReadRangeValue(AMinTextBox, "A 최소값", out var aMin) || |
|||
!TryReadRangeValue(AMaxTextBox, "A 최대값", out var aMax)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
if (vMin > vMax) |
|||
{ |
|||
MessageBox.Show(this, "V 최소값은 최대값보다 클 수 없습니다.", "판정 범위", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
VMinTextBox.Focus(); |
|||
return false; |
|||
} |
|||
|
|||
if (aMin > aMax) |
|||
{ |
|||
MessageBox.Show(this, "A 최소값은 최대값보다 클 수 없습니다.", "판정 범위", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
AMinTextBox.Focus(); |
|||
return false; |
|||
} |
|||
|
|||
_inspectionSettings = new InspectionJudgementSettings |
|||
{ |
|||
VMin = vMin, |
|||
VMax = vMax, |
|||
AMin = aMin, |
|||
AMax = aMax |
|||
}; |
|||
InspectionSettingsStore.Save(GetInspectionSettingsIniPath(), _inspectionSettings); |
|||
return true; |
|||
} |
|||
|
|||
private bool TryReadRangeValue(TextBox textBox, string fieldName, out decimal value) |
|||
{ |
|||
if (decimal.TryParse(textBox.Text, NumberStyles.Number, CultureInfo.InvariantCulture, out value)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (decimal.TryParse(textBox.Text, out value)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
MessageBox.Show(this, $"{fieldName}은 숫자로 입력하세요.", "판정 범위", MessageBoxButton.OK, MessageBoxImage.Warning); |
|||
textBox.Focus(); |
|||
return false; |
|||
} |
|||
|
|||
private static decimal ParseDecimal(string value) |
|||
{ |
|||
return decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result) |
|||
? result |
|||
: 0m; |
|||
} |
|||
|
|||
private static string FormatDecimal(decimal value) |
|||
{ |
|||
return value.ToString("0.00", CultureInfo.InvariantCulture); |
|||
} |
|||
|
|||
private static string GetDatabaseIniPath() |
|||
{ |
|||
return Path.Combine(AppContext.BaseDirectory, "Database.ini"); |
|||
} |
|||
|
|||
private static string GetHardwareIniPath() |
|||
{ |
|||
return Path.Combine(AppContext.BaseDirectory, "Hardware.ini"); |
|||
} |
|||
|
|||
private static string GetInspectionSettingsIniPath() |
|||
{ |
|||
return Path.Combine(AppContext.BaseDirectory, "InspectionSettings.ini"); |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Housing.Models; |
|||
|
|||
public sealed class BoardMeasurementResult |
|||
{ |
|||
public string IcSn { get; set; } = string.Empty; |
|||
public decimal Voltage { get; set; } |
|||
public decimal Current { get; set; } |
|||
public string RawLog { get; set; } = string.Empty; |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
|
|||
namespace Housing.Models; |
|||
|
|||
public sealed class HousingAssemblyRecord |
|||
{ |
|||
public string IcSn { get; set; } = string.Empty; |
|||
public string PcbBarcode { get; set; } = string.Empty; |
|||
public string Maker { get; set; } = string.Empty; |
|||
public string Model { get; set; } = string.Empty; |
|||
public string Variant1 { get; set; } = string.Empty; |
|||
public string Variant2 { get; set; } = string.Empty; |
|||
public string Operator { get; set; } = string.Empty; |
|||
public string Password { get; set; } = string.Empty; |
|||
public DateTime ProductionDate { get; set; } = DateTime.Now; |
|||
public string Line { get; set; } = string.Empty; |
|||
public string LotNo { get; set; } = string.Empty; |
|||
public string JigNo { get; set; } = string.Empty; |
|||
public decimal PtVol1 { get; set; } |
|||
public decimal PtCurrent1 { get; set; } |
|||
public string Result { get; set; } = string.Empty; |
|||
public string Spare1 { get; set; } = string.Empty; |
|||
public string Spare2 { get; set; } = string.Empty; |
|||
public string Spare3 { get; set; } = string.Empty; |
|||
public string Spare4 { get; set; } = string.Empty; |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
namespace Housing.Models; |
|||
|
|||
public sealed class InspectionJudgementSettings |
|||
{ |
|||
public decimal VMin { get; set; } = 0.00m; |
|||
public decimal VMax { get; set; } = 9.99m; |
|||
public decimal AMin { get; set; } = 0.00m; |
|||
public decimal AMax { get; set; } = 9.99m; |
|||
|
|||
public bool IsPass(decimal voltage, decimal current) |
|||
{ |
|||
return voltage >= VMin && |
|||
voltage <= VMax && |
|||
current >= AMin && |
|||
current <= AMax; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
using System.IO; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class BoardHardwareSettings |
|||
{ |
|||
public string PortName { get; set; } = "Auto"; |
|||
public int BaudRate { get; set; } = 115200; |
|||
public int ReadTimeout { get; set; } = 5000; |
|||
public string ConnectCommand { get; set; } = "x00c_001101:owt28006727ea97c7801"; |
|||
public string ReadIdCommand { get; set; } = "x00c_001101:ow2800326003e"; |
|||
public string CalDefaultCommand { get; set; } = "x00c_001001"; |
|||
public string EndToken { get; set; } = "<end>"; |
|||
|
|||
public static BoardHardwareSettings Load(string filePath) |
|||
{ |
|||
if (!File.Exists(filePath)) |
|||
{ |
|||
throw new FileNotFoundException("Hardware.ini 파일을 찾을 수 없습니다.", filePath); |
|||
} |
|||
|
|||
var values = IniFile.LoadSection(filePath, "Board"); |
|||
return new BoardHardwareSettings |
|||
{ |
|||
PortName = GetString(values, "PortName", "Auto"), |
|||
BaudRate = GetInt(values, "BaudRate", 115200), |
|||
ReadTimeout = GetInt(values, "ReadTimeout", 5000), |
|||
ConnectCommand = GetString(values, "ConnectCommand", "x00c_001101:owt28006727ea97c7801"), |
|||
ReadIdCommand = GetString(values, "ReadIdCommand", "x00c_001101:ow2800326003e"), |
|||
CalDefaultCommand = GetString(values, "CalDefaultCommand", "x00c_001001"), |
|||
EndToken = GetString(values, "EndToken", "<end>") |
|||
}; |
|||
} |
|||
|
|||
private static string GetString(Dictionary<string, string> values, string key, string defaultValue) |
|||
{ |
|||
return values.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value) |
|||
? value.Trim() |
|||
: defaultValue; |
|||
} |
|||
|
|||
private static int GetInt(Dictionary<string, string> values, string key, int defaultValue) |
|||
{ |
|||
return values.TryGetValue(key, out var value) && int.TryParse(value, out var result) |
|||
? result |
|||
: defaultValue; |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
using Housing.Models; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class BoardTestService |
|||
{ |
|||
private readonly BoardHardwareSettings _boardSettings; |
|||
private readonly EquipmentMeasurementSettings _equipmentSettings; |
|||
|
|||
public BoardTestService(BoardHardwareSettings boardSettings, EquipmentMeasurementSettings equipmentSettings) |
|||
{ |
|||
_boardSettings = boardSettings; |
|||
_equipmentSettings = equipmentSettings; |
|||
} |
|||
|
|||
public async Task<BoardMeasurementResult> RunAsync() |
|||
{ |
|||
using var boardClient = new SerialBoardClient(_boardSettings); |
|||
var log = new StringBuilder(); |
|||
log.AppendLine($"> BOARD PORT: {boardClient.PortName} @ {_boardSettings.BaudRate}"); |
|||
|
|||
var connectResponse = await SendAndLogAsync(boardClient, "CONNECT", _boardSettings.ConnectCommand, _boardSettings.ReadTimeout, true, log); |
|||
if (!ContainsSuccess(connectResponse)) |
|||
{ |
|||
throw new InvalidOperationException("시료 연결 확인 실패"); |
|||
} |
|||
|
|||
var idResponse = await SendAndLogAsync(boardClient, "READ_ID", _boardSettings.ReadIdCommand, _boardSettings.ReadTimeout, true, log); |
|||
var icSn = ExtractFirstHexLine(idResponse); |
|||
if (string.IsNullOrWhiteSpace(icSn)) |
|||
{ |
|||
throw new InvalidOperationException("IC_SN 읽기 실패"); |
|||
} |
|||
|
|||
await SendAndLogAsync(boardClient, "CAL_DEFAULT", _boardSettings.CalDefaultCommand, _boardSettings.ReadTimeout, false, log); |
|||
|
|||
var equipmentService = new EquipmentMeasurementService(_equipmentSettings); |
|||
var equipmentResult = await equipmentService.ReadAsync(); |
|||
log.Append(equipmentResult.RawLog); |
|||
|
|||
return new BoardMeasurementResult |
|||
{ |
|||
IcSn = icSn, |
|||
Voltage = equipmentResult.Voltage, |
|||
Current = equipmentResult.Current, |
|||
RawLog = log.ToString() |
|||
}; |
|||
} |
|||
|
|||
private static async Task<string> SendAndLogAsync( |
|||
SerialBoardClient client, |
|||
string label, |
|||
string command, |
|||
int timeoutMilliseconds, |
|||
bool waitForEndToken, |
|||
StringBuilder log) |
|||
{ |
|||
log.AppendLine($"> BOARD {label}: {command}"); |
|||
var response = await client.SendCommandAsync(command, timeoutMilliseconds, waitForEndToken); |
|||
log.Append(response); |
|||
return response; |
|||
} |
|||
|
|||
private static bool ContainsSuccess(string response) |
|||
{ |
|||
return response |
|||
.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries) |
|||
.Any(line => string.Equals(line.Trim(), "Success", StringComparison.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
private static string ExtractFirstHexLine(string response) |
|||
{ |
|||
foreach (var rawLine in response.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries)) |
|||
{ |
|||
var line = rawLine.Trim(); |
|||
if (line.Equals("<end>", StringComparison.OrdinalIgnoreCase) || |
|||
line.Equals("Fail", StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (Regex.IsMatch(line, "^[0-9A-Fa-f]{8,32}$")) |
|||
{ |
|||
return line.ToUpperInvariant(); |
|||
} |
|||
} |
|||
|
|||
return string.Empty; |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
using System.IO; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class DatabaseSettings |
|||
{ |
|||
public string Ip { get; set; } = string.Empty; |
|||
public string Database { get; set; } = string.Empty; |
|||
public string DbId { get; set; } = string.Empty; |
|||
public string DbPw { get; set; } = string.Empty; |
|||
public bool Encrypt { get; set; } = true; |
|||
public bool TrustServerCertificate { get; set; } = true; |
|||
public int Timeout { get; set; } = 5; |
|||
|
|||
public static DatabaseSettings Load(string filePath) |
|||
{ |
|||
if (!File.Exists(filePath)) |
|||
{ |
|||
throw new FileNotFoundException("Database.ini 파일을 찾을 수 없습니다.", filePath); |
|||
} |
|||
|
|||
var values = IniFile.LoadSection(filePath, "Database"); |
|||
return new DatabaseSettings |
|||
{ |
|||
Ip = GetValue(values, "IP", "Server"), |
|||
Database = GetValue(values, "Database", "DB"), |
|||
DbId = GetValue(values, "DbId", "UserId", "ID"), |
|||
DbPw = GetValue(values, "DbPw", "Password", "PW"), |
|||
Encrypt = GetBool(values, true, "Encrypt"), |
|||
TrustServerCertificate = GetBool(values, true, "TrustServerCertificate"), |
|||
Timeout = GetInt(values, 5, "Timeout") |
|||
}; |
|||
} |
|||
|
|||
private static string GetValue(Dictionary<string, string> values, params string[] keys) |
|||
{ |
|||
foreach (var key in keys) |
|||
{ |
|||
if (values.TryGetValue(key, out var value)) |
|||
{ |
|||
return value.Trim(); |
|||
} |
|||
} |
|||
|
|||
return string.Empty; |
|||
} |
|||
|
|||
private static bool GetBool(Dictionary<string, string> values, bool defaultValue, params string[] keys) |
|||
{ |
|||
var value = GetValue(values, keys); |
|||
return string.IsNullOrWhiteSpace(value) ? defaultValue : bool.Parse(value); |
|||
} |
|||
|
|||
private static int GetInt(Dictionary<string, string> values, int defaultValue, params string[] keys) |
|||
{ |
|||
var value = GetValue(values, keys); |
|||
return string.IsNullOrWhiteSpace(value) ? defaultValue : int.Parse(value); |
|||
} |
|||
} |
|||
@ -0,0 +1,138 @@ |
|||
using System.Globalization; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
using Housing.Models; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class EquipmentMeasurementService |
|||
{ |
|||
private readonly EquipmentMeasurementSettings _settings; |
|||
|
|||
public EquipmentMeasurementService(EquipmentMeasurementSettings settings) |
|||
{ |
|||
_settings = settings; |
|||
} |
|||
|
|||
public async Task<BoardMeasurementResult> ReadAsync() |
|||
{ |
|||
var log = new StringBuilder(); |
|||
|
|||
if (_settings.SettleMilliseconds > 0) |
|||
{ |
|||
await Task.Delay(_settings.SettleMilliseconds); |
|||
} |
|||
|
|||
var voltage = await ReadChannelAsync("VOLTAGE", _settings.Voltage, log); |
|||
var current = await ReadChannelAsync("CURRENT", _settings.Current, log); |
|||
|
|||
return new BoardMeasurementResult |
|||
{ |
|||
Voltage = voltage, |
|||
Current = current, |
|||
RawLog = log.ToString() |
|||
}; |
|||
} |
|||
|
|||
private async Task<decimal> ReadChannelAsync(string label, ScpiChannelSettings channel, StringBuilder log) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(channel.ReadCommand)) |
|||
{ |
|||
throw new InvalidOperationException($"Hardware.ini [Equipment] {label} ReadCommand 값을 확인하세요."); |
|||
} |
|||
|
|||
using var client = await CreateClientAsync(label, channel); |
|||
log.AppendLine($"> {label} CONNECT: {DescribeClient(client, channel)}"); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(channel.SetupCommand)) |
|||
{ |
|||
log.AppendLine($"> {label} SETUP: {channel.SetupCommand}"); |
|||
await client.SendAsync(channel.SetupCommand); |
|||
} |
|||
|
|||
log.AppendLine($"> {label} READ: {channel.ReadCommand}"); |
|||
var response = await client.QueryAsync(channel.ReadCommand); |
|||
log.AppendLine(response); |
|||
|
|||
return ExtractFirstDecimal(response, label); |
|||
} |
|||
|
|||
private Task<IScpiClient> CreateClientAsync(string label, ScpiChannelSettings channel) |
|||
{ |
|||
var connection = GetConnectionType(channel); |
|||
return connection switch |
|||
{ |
|||
"TCP" or "LAN" => CreateTcpClientAsync(label, channel), |
|||
"SERIAL" or "COM" => Task.FromResult<IScpiClient>(new SerialScpiClient(channel.PortName, channel.BaudRate, _settings.Timeout, channel.GetIdnMatches())), |
|||
"VISA" or "USB" => CreateVisaClientAsync(channel), |
|||
_ => throw new InvalidOperationException($"{label} 장비 연결 방식은 Visa, Serial, Tcp 중 하나로 설정하세요.") |
|||
}; |
|||
} |
|||
|
|||
private static string GetConnectionType(ScpiChannelSettings channel) |
|||
{ |
|||
if (!string.IsNullOrWhiteSpace(channel.Connection)) |
|||
{ |
|||
return channel.Connection.Trim().ToUpperInvariant(); |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(channel.ResourceName)) |
|||
{ |
|||
return "VISA"; |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(channel.IdnMatch)) |
|||
{ |
|||
return "VISA"; |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(channel.PortName)) |
|||
{ |
|||
return "SERIAL"; |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(channel.Host)) |
|||
{ |
|||
return "TCP"; |
|||
} |
|||
|
|||
return string.Empty; |
|||
} |
|||
|
|||
private async Task<IScpiClient> CreateTcpClientAsync(string label, ScpiChannelSettings channel) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(channel.Host)) |
|||
{ |
|||
throw new InvalidOperationException($"Hardware.ini [Equipment] {label} Host 값을 확인하세요."); |
|||
} |
|||
|
|||
return await TcpScpiClient.ConnectAsync(channel.Host, channel.Port, _settings.Timeout); |
|||
} |
|||
|
|||
private async Task<IScpiClient> CreateVisaClientAsync(ScpiChannelSettings channel) |
|||
{ |
|||
return await VisaScpiClient.OpenMatchingAsync(channel.ResourceName, channel.GetIdnMatches(), _settings.Timeout); |
|||
} |
|||
|
|||
private static string DescribeClient(IScpiClient client, ScpiChannelSettings channel) |
|||
{ |
|||
return client switch |
|||
{ |
|||
SerialScpiClient serial => $"Serial {serial.PortName} @ {channel.BaudRate}", |
|||
VisaScpiClient visa => $"VISA {visa.ResourceName}", |
|||
TcpScpiClient => $"TCP {channel.Host}:{channel.Port}", |
|||
_ => channel.Connection |
|||
}; |
|||
} |
|||
|
|||
private static decimal ExtractFirstDecimal(string response, string label) |
|||
{ |
|||
var match = Regex.Match(response, @"[-+]?\d+(?:\.\d+)?(?:[Ee][-+]?\d+)?"); |
|||
if (!match.Success) |
|||
{ |
|||
throw new InvalidOperationException($"{label} 측정값 파싱 실패"); |
|||
} |
|||
|
|||
return decimal.Parse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture); |
|||
} |
|||
} |
|||
@ -0,0 +1,84 @@ |
|||
using System.IO; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class EquipmentMeasurementSettings |
|||
{ |
|||
public int Timeout { get; set; } = 5000; |
|||
public int SettleMilliseconds { get; set; } = 300; |
|||
public ScpiChannelSettings Voltage { get; set; } = new(); |
|||
public ScpiChannelSettings Current { get; set; } = new(); |
|||
|
|||
public static EquipmentMeasurementSettings Load(string filePath) |
|||
{ |
|||
if (!File.Exists(filePath)) |
|||
{ |
|||
throw new FileNotFoundException("Hardware.ini 파일을 찾을 수 없습니다.", filePath); |
|||
} |
|||
|
|||
var values = IniFile.LoadSection(filePath, "Equipment"); |
|||
return new EquipmentMeasurementSettings |
|||
{ |
|||
Timeout = GetInt(values, "Timeout", 5000), |
|||
SettleMilliseconds = GetInt(values, "SettleMilliseconds", 300), |
|||
Voltage = LoadChannel(values, "Voltage", "MEAS:VOLT:DC?", "34460,34461,34465,34470,3446,3447"), |
|||
Current = LoadChannel(values, "Current", "MEAS:CURR? CH1", "E362") |
|||
}; |
|||
} |
|||
|
|||
private static ScpiChannelSettings LoadChannel( |
|||
Dictionary<string, string> values, |
|||
string prefix, |
|||
string defaultReadCommand, |
|||
string defaultIdnMatch) |
|||
{ |
|||
return new ScpiChannelSettings |
|||
{ |
|||
Connection = GetString(values, prefix + "Connection", string.Empty), |
|||
ResourceName = GetString(values, prefix + "Resource", string.Empty), |
|||
IdnMatch = GetString(values, prefix + "IdnMatch", defaultIdnMatch), |
|||
Host = GetString(values, prefix + "Host", string.Empty), |
|||
Port = GetInt(values, prefix + "Port", 5025), |
|||
PortName = GetString(values, prefix + "PortName", string.Empty), |
|||
BaudRate = GetInt(values, prefix + "BaudRate", 115200), |
|||
SetupCommand = GetString(values, prefix + "SetupCommand", string.Empty), |
|||
ReadCommand = GetString(values, prefix + "ReadCommand", defaultReadCommand) |
|||
}; |
|||
} |
|||
|
|||
private static string GetString(Dictionary<string, string> values, string key, string defaultValue) |
|||
{ |
|||
return values.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value) |
|||
? value.Trim() |
|||
: defaultValue; |
|||
} |
|||
|
|||
private static int GetInt(Dictionary<string, string> values, string key, int defaultValue) |
|||
{ |
|||
return values.TryGetValue(key, out var value) && int.TryParse(value, out var result) |
|||
? result |
|||
: defaultValue; |
|||
} |
|||
} |
|||
|
|||
public sealed class ScpiChannelSettings |
|||
{ |
|||
public string Connection { get; set; } = string.Empty; |
|||
public string ResourceName { get; set; } = string.Empty; |
|||
public string IdnMatch { get; set; } = string.Empty; |
|||
public string Host { get; set; } = string.Empty; |
|||
public int Port { get; set; } = 5025; |
|||
public string PortName { get; set; } = string.Empty; |
|||
public int BaudRate { get; set; } = 115200; |
|||
public string SetupCommand { get; set; } = string.Empty; |
|||
public string ReadCommand { get; set; } = string.Empty; |
|||
|
|||
public string[] GetIdnMatches() |
|||
{ |
|||
return IdnMatch |
|||
.Split(',', ';') |
|||
.Select(value => value.Trim()) |
|||
.Where(value => !string.IsNullOrWhiteSpace(value)) |
|||
.ToArray(); |
|||
} |
|||
} |
|||
@ -0,0 +1,130 @@ |
|||
using System.Data; |
|||
using Housing.Models; |
|||
using Microsoft.Data.SqlClient; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class HousingAssemblyRepository |
|||
{ |
|||
private readonly DatabaseSettings _settings; |
|||
|
|||
public HousingAssemblyRepository(DatabaseSettings settings) |
|||
{ |
|||
_settings = settings; |
|||
} |
|||
|
|||
public async Task InsertAsync(HousingAssemblyRecord record) |
|||
{ |
|||
using var connection = new SqlConnection(CreateConnectionString()); |
|||
using var command = connection.CreateCommand(); |
|||
|
|||
command.CommandText = @"
|
|||
INSERT INTO dbo.Housing_Assembly |
|||
( |
|||
[IC_SN], |
|||
[PCB_Barcode], |
|||
[Maker], |
|||
[Model], |
|||
[Variant_1], |
|||
[Variant_2], |
|||
[Operator], |
|||
[Password], |
|||
[Production_Date], |
|||
[Line], |
|||
[Lot_No], |
|||
[Jig_No], |
|||
[PT_Vol_1], |
|||
[PT_Current_1], |
|||
[Result], |
|||
[Spare_1], |
|||
[Spare_2], |
|||
[Spare_3], |
|||
[Spare_4] |
|||
) |
|||
VALUES |
|||
( |
|||
@IC_SN, |
|||
@PCB_Barcode, |
|||
@Maker, |
|||
@Model, |
|||
@Variant_1, |
|||
@Variant_2, |
|||
@Operator, |
|||
@Password, |
|||
@Production_Date, |
|||
@Line, |
|||
@Lot_No, |
|||
@Jig_No, |
|||
@PT_Vol_1, |
|||
@PT_Current_1, |
|||
@Result, |
|||
@Spare_1, |
|||
@Spare_2, |
|||
@Spare_3, |
|||
@Spare_4 |
|||
);";
|
|||
|
|||
AddNVarChar(command, "@IC_SN", 50, record.IcSn); |
|||
AddNVarChar(command, "@PCB_Barcode", 50, record.PcbBarcode); |
|||
AddNVarChar(command, "@Maker", 10, record.Maker); |
|||
AddNVarChar(command, "@Model", 10, record.Model); |
|||
AddNVarChar(command, "@Variant_1", 10, record.Variant1); |
|||
AddNVarChar(command, "@Variant_2", 10, record.Variant2); |
|||
AddNVarChar(command, "@Operator", 10, record.Operator); |
|||
AddNVarChar(command, "@Password", 10, record.Password); |
|||
command.Parameters.Add(new SqlParameter("@Production_Date", SqlDbType.DateTime2) { Value = record.ProductionDate }); |
|||
AddNVarChar(command, "@Line", 5, record.Line); |
|||
AddNVarChar(command, "@Lot_No", 8, record.LotNo); |
|||
AddNVarChar(command, "@Jig_No", 10, record.JigNo); |
|||
AddDecimal(command, "@PT_Vol_1", record.PtVol1); |
|||
AddDecimal(command, "@PT_Current_1", record.PtCurrent1); |
|||
AddNVarChar(command, "@Result", 5, record.Result); |
|||
AddNVarChar(command, "@Spare_1", 30, record.Spare1); |
|||
AddNVarChar(command, "@Spare_2", 30, record.Spare2); |
|||
AddNVarChar(command, "@Spare_3", 30, record.Spare3); |
|||
AddNVarChar(command, "@Spare_4", 30, record.Spare4); |
|||
|
|||
await connection.OpenAsync(); |
|||
await command.ExecuteNonQueryAsync(); |
|||
} |
|||
|
|||
private string CreateConnectionString() |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(_settings.Ip) || |
|||
string.IsNullOrWhiteSpace(_settings.Database) || |
|||
string.IsNullOrWhiteSpace(_settings.DbId)) |
|||
{ |
|||
throw new InvalidOperationException("Database.ini의 IP, Database, DbId 값을 확인하세요."); |
|||
} |
|||
|
|||
var builder = new SqlConnectionStringBuilder |
|||
{ |
|||
DataSource = _settings.Ip, |
|||
InitialCatalog = _settings.Database, |
|||
UserID = _settings.DbId, |
|||
Password = _settings.DbPw, |
|||
Encrypt = _settings.Encrypt, |
|||
TrustServerCertificate = _settings.TrustServerCertificate, |
|||
ConnectTimeout = _settings.Timeout |
|||
}; |
|||
|
|||
return builder.ConnectionString; |
|||
} |
|||
|
|||
private static void AddNVarChar(SqlCommand command, string name, int size, string value) |
|||
{ |
|||
command.Parameters.Add(new SqlParameter(name, SqlDbType.NVarChar, size) { Value = value }); |
|||
} |
|||
|
|||
private static void AddDecimal(SqlCommand command, string name, decimal value) |
|||
{ |
|||
var parameter = new SqlParameter(name, SqlDbType.Decimal) |
|||
{ |
|||
Precision = 3, |
|||
Scale = 2, |
|||
Value = value |
|||
}; |
|||
|
|||
command.Parameters.Add(parameter); |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Housing.Services; |
|||
|
|||
public interface IScpiClient : IDisposable |
|||
{ |
|||
Task SendAsync(string command); |
|||
Task<string> QueryAsync(string command); |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
using System.IO; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public static class IniFile |
|||
{ |
|||
public static Dictionary<string, string> LoadSection(string filePath, string sectionName) |
|||
{ |
|||
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); |
|||
var isTargetSection = false; |
|||
|
|||
foreach (var rawLine in File.ReadAllLines(filePath)) |
|||
{ |
|||
var line = rawLine.Trim(); |
|||
if (string.IsNullOrWhiteSpace(line) || line.StartsWith(';') || line.StartsWith('#')) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (line.StartsWith('[') && line.EndsWith(']')) |
|||
{ |
|||
var currentSection = line.Substring(1, line.Length - 2).Trim(); |
|||
isTargetSection = string.Equals(currentSection, sectionName, StringComparison.OrdinalIgnoreCase); |
|||
continue; |
|||
} |
|||
|
|||
if (!isTargetSection) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var separatorIndex = line.IndexOf('='); |
|||
if (separatorIndex <= 0) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
var key = line.Substring(0, separatorIndex).Trim(); |
|||
var value = line.Substring(separatorIndex + 1).Trim(); |
|||
values[key] = value; |
|||
} |
|||
|
|||
return values; |
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System.Globalization; |
|||
using System.IO; |
|||
using Housing.Models; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public static class InspectionSettingsStore |
|||
{ |
|||
public static InspectionJudgementSettings Load(string filePath) |
|||
{ |
|||
if (!File.Exists(filePath)) |
|||
{ |
|||
var defaultSettings = new InspectionJudgementSettings(); |
|||
Save(filePath, defaultSettings); |
|||
return defaultSettings; |
|||
} |
|||
|
|||
var values = IniFile.LoadSection(filePath, "Judgement"); |
|||
return new InspectionJudgementSettings |
|||
{ |
|||
VMin = GetDecimal(values, "VMin", 0.00m), |
|||
VMax = GetDecimal(values, "VMax", 9.99m), |
|||
AMin = GetDecimal(values, "AMin", 0.00m), |
|||
AMax = GetDecimal(values, "AMax", 9.99m) |
|||
}; |
|||
} |
|||
|
|||
public static void Save(string filePath, InspectionJudgementSettings settings) |
|||
{ |
|||
var lines = new[] |
|||
{ |
|||
"[Judgement]", |
|||
$"VMin={Format(settings.VMin)}", |
|||
$"VMax={Format(settings.VMax)}", |
|||
$"AMin={Format(settings.AMin)}", |
|||
$"AMax={Format(settings.AMax)}" |
|||
}; |
|||
|
|||
File.WriteAllLines(filePath, lines); |
|||
} |
|||
|
|||
private static decimal GetDecimal(Dictionary<string, string> values, string key, decimal defaultValue) |
|||
{ |
|||
if (!values.TryGetValue(key, out var value)) |
|||
{ |
|||
return defaultValue; |
|||
} |
|||
|
|||
return decimal.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out var result) |
|||
? result |
|||
: defaultValue; |
|||
} |
|||
|
|||
private static string Format(decimal value) |
|||
{ |
|||
return value.ToString("0.00", CultureInfo.InvariantCulture); |
|||
} |
|||
} |
|||
@ -0,0 +1,193 @@ |
|||
using System.IO.Ports; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class SerialBoardClient : IDisposable |
|||
{ |
|||
private readonly BoardHardwareSettings _settings; |
|||
private readonly SerialPort _serialPort; |
|||
|
|||
public string PortName => _serialPort.PortName; |
|||
|
|||
public SerialBoardClient(BoardHardwareSettings settings) |
|||
{ |
|||
_settings = settings; |
|||
var portName = ResolvePortName(settings); |
|||
|
|||
_serialPort = CreateSerialPort(portName, _settings); |
|||
} |
|||
|
|||
public void Open() |
|||
{ |
|||
if (!_serialPort.IsOpen) |
|||
{ |
|||
_serialPort.Open(); |
|||
_serialPort.DiscardInBuffer(); |
|||
_serialPort.DiscardOutBuffer(); |
|||
} |
|||
} |
|||
|
|||
public Task<string> SendCommandAsync(string command, int timeoutMilliseconds, bool waitForEndToken) |
|||
{ |
|||
return Task.Run(() => SendCommand(command, timeoutMilliseconds, waitForEndToken)); |
|||
} |
|||
|
|||
private static SerialPort CreateSerialPort(string portName, BoardHardwareSettings settings) |
|||
{ |
|||
return new SerialPort(portName, settings.BaudRate) |
|||
{ |
|||
Encoding = Encoding.ASCII, |
|||
NewLine = "\r\n", |
|||
ReadTimeout = settings.ReadTimeout, |
|||
WriteTimeout = settings.ReadTimeout, |
|||
DtrEnable = true, |
|||
RtsEnable = true |
|||
}; |
|||
} |
|||
|
|||
private static string ResolvePortName(BoardHardwareSettings settings) |
|||
{ |
|||
if (!IsAutoPort(settings.PortName)) |
|||
{ |
|||
return settings.PortName.Trim(); |
|||
} |
|||
|
|||
var portNames = GetOrderedPortNames(); |
|||
if (portNames.Length == 0) |
|||
{ |
|||
throw new InvalidOperationException("사용 가능한 COM 포트를 찾을 수 없습니다."); |
|||
} |
|||
|
|||
foreach (var portName in portNames) |
|||
{ |
|||
if (CanConnectBoard(portName, settings)) |
|||
{ |
|||
return portName; |
|||
} |
|||
} |
|||
|
|||
throw new InvalidOperationException($"보드 자동 포트 검색 실패. 확인한 포트: {string.Join(", ", portNames)}"); |
|||
} |
|||
|
|||
private static bool IsAutoPort(string portName) |
|||
{ |
|||
return string.IsNullOrWhiteSpace(portName) || |
|||
string.Equals(portName.Trim(), "Auto", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
private static string[] GetOrderedPortNames() |
|||
{ |
|||
return SerialPort.GetPortNames() |
|||
.OrderBy(GetPortNumber) |
|||
.ThenBy(portName => portName, StringComparer.OrdinalIgnoreCase) |
|||
.ToArray(); |
|||
} |
|||
|
|||
private static int GetPortNumber(string portName) |
|||
{ |
|||
var match = Regex.Match(portName, @"\d+"); |
|||
return match.Success && int.TryParse(match.Value, out var number) |
|||
? number |
|||
: int.MaxValue; |
|||
} |
|||
|
|||
private static bool CanConnectBoard(string portName, BoardHardwareSettings settings) |
|||
{ |
|||
try |
|||
{ |
|||
using var probe = CreateSerialPort(portName, settings); |
|||
probe.Open(); |
|||
probe.DiscardInBuffer(); |
|||
probe.DiscardOutBuffer(); |
|||
|
|||
if (string.IsNullOrWhiteSpace(settings.ConnectCommand)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
probe.Write(settings.ConnectCommand.Trim() + "\r\n"); |
|||
var startedAt = DateTime.UtcNow; |
|||
|
|||
while ((DateTime.UtcNow - startedAt).TotalMilliseconds < settings.ReadTimeout) |
|||
{ |
|||
try |
|||
{ |
|||
var line = probe.ReadLine(); |
|||
if (IsSuccessLine(line)) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (!string.IsNullOrWhiteSpace(settings.EndToken) && |
|||
line.Contains(settings.EndToken, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
catch (TimeoutException) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static bool IsSuccessLine(string line) |
|||
{ |
|||
return string.Equals(line.Trim(), "Success", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
private string SendCommand(string command, int timeoutMilliseconds, bool waitForEndToken) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(command)) |
|||
{ |
|||
return string.Empty; |
|||
} |
|||
|
|||
Open(); |
|||
_serialPort.ReadTimeout = Math.Max(500, timeoutMilliseconds); |
|||
_serialPort.DiscardInBuffer(); |
|||
_serialPort.Write(command.Trim() + "\r\n"); |
|||
|
|||
var startedAt = DateTime.UtcNow; |
|||
var response = new StringBuilder(); |
|||
|
|||
while ((DateTime.UtcNow - startedAt).TotalMilliseconds < timeoutMilliseconds) |
|||
{ |
|||
try |
|||
{ |
|||
var line = _serialPort.ReadLine(); |
|||
response.AppendLine(line); |
|||
|
|||
if (!waitForEndToken) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
if (line.Contains(_settings.EndToken, StringComparison.OrdinalIgnoreCase)) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
catch (TimeoutException) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return response.ToString(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_serialPort.Dispose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,132 @@ |
|||
using System.IO.Ports; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class SerialScpiClient : IScpiClient |
|||
{ |
|||
private readonly SerialPort _serialPort; |
|||
|
|||
public string PortName => _serialPort.PortName; |
|||
|
|||
public SerialScpiClient(string portName, int baudRate, int timeoutMilliseconds, IEnumerable<string>? idnMatches = null) |
|||
{ |
|||
var resolvedPortName = ResolvePortName(portName, baudRate, timeoutMilliseconds, idnMatches); |
|||
|
|||
_serialPort = CreateSerialPort(resolvedPortName, baudRate, timeoutMilliseconds); |
|||
_serialPort.Open(); |
|||
_serialPort.DiscardInBuffer(); |
|||
_serialPort.DiscardOutBuffer(); |
|||
} |
|||
|
|||
public Task SendAsync(string command) |
|||
{ |
|||
return Task.Run(() => _serialPort.Write(command.Trim() + "\n")); |
|||
} |
|||
|
|||
public Task<string> QueryAsync(string command) |
|||
{ |
|||
return Task.Run(() => |
|||
{ |
|||
_serialPort.DiscardInBuffer(); |
|||
_serialPort.Write(command.Trim() + "\n"); |
|||
return _serialPort.ReadLine().Trim(); |
|||
}); |
|||
} |
|||
|
|||
private static SerialPort CreateSerialPort(string portName, int baudRate, int timeoutMilliseconds) |
|||
{ |
|||
return new SerialPort(portName, baudRate) |
|||
{ |
|||
Encoding = Encoding.ASCII, |
|||
NewLine = "\n", |
|||
ReadTimeout = timeoutMilliseconds, |
|||
WriteTimeout = timeoutMilliseconds, |
|||
DtrEnable = true, |
|||
RtsEnable = true |
|||
}; |
|||
} |
|||
|
|||
private static string ResolvePortName(string portName, int baudRate, int timeoutMilliseconds, IEnumerable<string>? idnMatches) |
|||
{ |
|||
if (!IsAutoPort(portName)) |
|||
{ |
|||
return portName.Trim(); |
|||
} |
|||
|
|||
var portNames = SerialPort.GetPortNames() |
|||
.OrderBy(GetPortNumber) |
|||
.ThenBy(candidate => candidate, StringComparer.OrdinalIgnoreCase) |
|||
.ToArray(); |
|||
|
|||
if (portNames.Length == 0) |
|||
{ |
|||
throw new InvalidOperationException("사용 가능한 장비 COM 포트를 찾을 수 없습니다."); |
|||
} |
|||
|
|||
var tokens = NormalizeIdnMatches(idnMatches); |
|||
if (tokens.Length == 0 && portNames.Length == 1) |
|||
{ |
|||
return portNames[0]; |
|||
} |
|||
|
|||
foreach (var candidate in portNames) |
|||
{ |
|||
if (MatchesIdn(candidate, baudRate, timeoutMilliseconds, tokens)) |
|||
{ |
|||
return candidate; |
|||
} |
|||
} |
|||
|
|||
throw new InvalidOperationException($"SCPI 장비 자동 포트 검색 실패. 확인한 포트: {string.Join(", ", portNames)}"); |
|||
} |
|||
|
|||
private static bool IsAutoPort(string portName) |
|||
{ |
|||
return string.IsNullOrWhiteSpace(portName) || |
|||
string.Equals(portName.Trim(), "Auto", StringComparison.OrdinalIgnoreCase); |
|||
} |
|||
|
|||
private static int GetPortNumber(string portName) |
|||
{ |
|||
var match = Regex.Match(portName, @"\d+"); |
|||
return match.Success && int.TryParse(match.Value, out var number) |
|||
? number |
|||
: int.MaxValue; |
|||
} |
|||
|
|||
private static string[] NormalizeIdnMatches(IEnumerable<string>? idnMatches) |
|||
{ |
|||
return idnMatches? |
|||
.SelectMany(value => value.Split(',', ';')) |
|||
.Select(value => value.Trim()) |
|||
.Where(value => !string.IsNullOrWhiteSpace(value)) |
|||
.ToArray() ?? Array.Empty<string>(); |
|||
} |
|||
|
|||
private static bool MatchesIdn(string portName, int baudRate, int timeoutMilliseconds, string[] idnMatches) |
|||
{ |
|||
try |
|||
{ |
|||
using var probe = CreateSerialPort(portName, baudRate, timeoutMilliseconds); |
|||
probe.Open(); |
|||
probe.DiscardInBuffer(); |
|||
probe.DiscardOutBuffer(); |
|||
probe.Write("*IDN?\n"); |
|||
var idn = probe.ReadLine().Trim(); |
|||
|
|||
return idnMatches.Length == 0 || |
|||
idnMatches.Any(token => idn.Contains(token, StringComparison.OrdinalIgnoreCase)); |
|||
} |
|||
catch |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_serialPort.Dispose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,92 @@ |
|||
using System.Net.Sockets; |
|||
using System.Text; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class TcpScpiClient : IScpiClient |
|||
{ |
|||
private readonly TcpClient _tcpClient; |
|||
private readonly NetworkStream _stream; |
|||
private readonly int _timeoutMilliseconds; |
|||
|
|||
private TcpScpiClient(TcpClient tcpClient, int timeoutMilliseconds) |
|||
{ |
|||
_tcpClient = tcpClient; |
|||
_stream = _tcpClient.GetStream(); |
|||
_timeoutMilliseconds = timeoutMilliseconds; |
|||
} |
|||
|
|||
public static async Task<TcpScpiClient> ConnectAsync(string host, int port, int timeoutMilliseconds) |
|||
{ |
|||
var tcpClient = new TcpClient(); |
|||
using var cancellation = new CancellationTokenSource(timeoutMilliseconds); |
|||
|
|||
try |
|||
{ |
|||
await tcpClient.ConnectAsync(host, port, cancellation.Token); |
|||
} |
|||
catch (OperationCanceledException) |
|||
{ |
|||
tcpClient.Dispose(); |
|||
throw new TimeoutException($"{host}:{port} 장비 연결 시간이 초과되었습니다."); |
|||
} |
|||
catch |
|||
{ |
|||
tcpClient.Dispose(); |
|||
throw; |
|||
} |
|||
|
|||
return new TcpScpiClient(tcpClient, timeoutMilliseconds); |
|||
} |
|||
|
|||
public async Task SendAsync(string command) |
|||
{ |
|||
using var cancellation = new CancellationTokenSource(_timeoutMilliseconds); |
|||
var bytes = Encoding.ASCII.GetBytes(command.Trim() + "\n"); |
|||
await _stream.WriteAsync(bytes, cancellation.Token); |
|||
await _stream.FlushAsync(cancellation.Token); |
|||
} |
|||
|
|||
public async Task<string> QueryAsync(string command) |
|||
{ |
|||
await SendAsync(command); |
|||
|
|||
using var cancellation = new CancellationTokenSource(_timeoutMilliseconds); |
|||
var buffer = new byte[1024]; |
|||
var response = new StringBuilder(); |
|||
|
|||
while (true) |
|||
{ |
|||
int bytesRead; |
|||
try |
|||
{ |
|||
bytesRead = await _stream.ReadAsync(buffer, cancellation.Token); |
|||
} |
|||
catch (OperationCanceledException) |
|||
{ |
|||
throw new TimeoutException($"{command} 응답 시간이 초과되었습니다."); |
|||
} |
|||
|
|||
if (bytesRead == 0) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
var chunk = Encoding.ASCII.GetString(buffer, 0, bytesRead); |
|||
response.Append(chunk); |
|||
|
|||
if (chunk.Contains('\n')) |
|||
{ |
|||
break; |
|||
} |
|||
} |
|||
|
|||
return response.ToString().Trim(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
_stream.Dispose(); |
|||
_tcpClient.Dispose(); |
|||
} |
|||
} |
|||
@ -0,0 +1,214 @@ |
|||
using System.Collections; |
|||
|
|||
namespace Housing.Services; |
|||
|
|||
public sealed class VisaScpiClient : IScpiClient |
|||
{ |
|||
private readonly dynamic _io; |
|||
|
|||
private VisaScpiClient(dynamic io, string resourceName) |
|||
{ |
|||
_io = io; |
|||
ResourceName = resourceName; |
|||
} |
|||
|
|||
public string ResourceName { get; } |
|||
|
|||
public static Task<VisaScpiClient> OpenAsync(string resourceName, int timeoutMilliseconds) |
|||
{ |
|||
return Task.Run(() => Open(resourceName, timeoutMilliseconds)); |
|||
} |
|||
|
|||
public static Task<VisaScpiClient> OpenMatchingAsync( |
|||
string resourceName, |
|||
IEnumerable<string> idnMatches, |
|||
int timeoutMilliseconds) |
|||
{ |
|||
return string.IsNullOrWhiteSpace(resourceName) |
|||
? Task.Run(() => OpenFirstMatching(idnMatches, timeoutMilliseconds)) |
|||
: OpenAsync(resourceName, timeoutMilliseconds); |
|||
} |
|||
|
|||
private static VisaScpiClient Open(string resourceName, int timeoutMilliseconds) |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(resourceName)) |
|||
{ |
|||
throw new InvalidOperationException("장비 VISA Resource 값을 확인하세요."); |
|||
} |
|||
|
|||
var (resourceManagerType, formattedIoType) = GetVisaComTypes(); |
|||
dynamic resourceManager = Activator.CreateInstance(resourceManagerType)!; |
|||
return OpenResource(resourceManager, formattedIoType, resourceName.Trim(), timeoutMilliseconds); |
|||
} |
|||
|
|||
private static VisaScpiClient OpenFirstMatching(IEnumerable<string> idnMatches, int timeoutMilliseconds) |
|||
{ |
|||
var tokens = NormalizeIdnMatches(idnMatches); |
|||
var (resourceManagerType, formattedIoType) = GetVisaComTypes(); |
|||
dynamic resourceManager = Activator.CreateInstance(resourceManagerType)!; |
|||
var resourceNames = FindResourceNames(resourceManager); |
|||
|
|||
if (resourceNames.Length == 0) |
|||
{ |
|||
throw new InvalidOperationException("VISA 장비를 찾을 수 없습니다. USB/LAN 장비 연결과 VISA 드라이버 설치를 확인하세요."); |
|||
} |
|||
|
|||
var checkedResources = new List<string>(); |
|||
foreach (var resourceName in resourceNames) |
|||
{ |
|||
VisaScpiClient? client = null; |
|||
try |
|||
{ |
|||
client = OpenResource(resourceManager, formattedIoType, resourceName, timeoutMilliseconds); |
|||
var idn = client.Query("*IDN?"); |
|||
checkedResources.Add($"{resourceName}: {idn}"); |
|||
|
|||
if (tokens.Length == 0 || |
|||
tokens.Any(token => idn.Contains(token, StringComparison.OrdinalIgnoreCase))) |
|||
{ |
|||
return client; |
|||
} |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
checkedResources.Add($"{resourceName}: {ex.Message}"); |
|||
} |
|||
|
|||
client?.Dispose(); |
|||
} |
|||
|
|||
var tokenMessage = tokens.Length == 0 ? string.Empty : $" IDN 키워드: {string.Join(", ", tokens)}."; |
|||
var checkedMessage = checkedResources.Count == 0 |
|||
? string.Empty |
|||
: $" 확인 결과: {string.Join(" / ", checkedResources)}"; |
|||
|
|||
throw new InvalidOperationException($"VISA 장비 자동 검색 실패.{tokenMessage}{checkedMessage}"); |
|||
} |
|||
|
|||
private static (Type ResourceManagerType, Type FormattedIoType) GetVisaComTypes() |
|||
{ |
|||
var resourceManagerType = Type.GetTypeFromProgID("VISA.GlobalRM") ?? |
|||
Type.GetTypeFromProgID("VisaComLib.ResourceManager"); |
|||
var formattedIoType = Type.GetTypeFromProgID("VISA.FormattedIO488") ?? |
|||
Type.GetTypeFromProgID("VisaComLib.FormattedIO488"); |
|||
|
|||
if (resourceManagerType is null || formattedIoType is null) |
|||
{ |
|||
throw new InvalidOperationException("VISA 라이브러리를 찾을 수 없습니다. Keysight IO Libraries 또는 NI-VISA 설치가 필요합니다."); |
|||
} |
|||
|
|||
return (resourceManagerType, formattedIoType); |
|||
} |
|||
|
|||
private static VisaScpiClient OpenResource( |
|||
dynamic resourceManager, |
|||
Type formattedIoType, |
|||
string resourceName, |
|||
int timeoutMilliseconds) |
|||
{ |
|||
dynamic session = resourceManager.Open(resourceName); |
|||
|
|||
try |
|||
{ |
|||
session.Timeout = timeoutMilliseconds; |
|||
} |
|||
catch |
|||
{ |
|||
// Some VISA COM sessions expose timeout through driver-specific properties only.
|
|||
} |
|||
|
|||
dynamic formattedIo = Activator.CreateInstance(formattedIoType)!; |
|||
formattedIo.IO = session; |
|||
return new VisaScpiClient(formattedIo, resourceName); |
|||
} |
|||
|
|||
private static string[] FindResourceNames(dynamic resourceManager) |
|||
{ |
|||
var resourceNames = new SortedSet<string>(StringComparer.OrdinalIgnoreCase); |
|||
foreach (var pattern in new[] { "?*INSTR", "USB?*INSTR", "TCPIP?*INSTR", "GPIB?*INSTR", "ASRL?*INSTR" }) |
|||
{ |
|||
try |
|||
{ |
|||
AddResourceNames(resourceNames, resourceManager.FindRsrc(pattern)); |
|||
} |
|||
catch |
|||
{ |
|||
// Resource discovery support differs by VISA COM implementation and backend.
|
|||
} |
|||
} |
|||
|
|||
return resourceNames.ToArray(); |
|||
} |
|||
|
|||
private static void AddResourceNames(ISet<string> resourceNames, object? value) |
|||
{ |
|||
switch (value) |
|||
{ |
|||
case null: |
|||
return; |
|||
case string resourceName: |
|||
AddResourceName(resourceNames, resourceName); |
|||
return; |
|||
case IEnumerable enumerable: |
|||
foreach (var item in enumerable) |
|||
{ |
|||
AddResourceName(resourceNames, item?.ToString()); |
|||
} |
|||
|
|||
return; |
|||
default: |
|||
AddResourceName(resourceNames, value.ToString()); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
private static void AddResourceName(ISet<string> resourceNames, string? resourceName) |
|||
{ |
|||
if (!string.IsNullOrWhiteSpace(resourceName)) |
|||
{ |
|||
resourceNames.Add(resourceName.Trim()); |
|||
} |
|||
} |
|||
|
|||
private static string[] NormalizeIdnMatches(IEnumerable<string> idnMatches) |
|||
{ |
|||
return idnMatches |
|||
.SelectMany(value => value.Split(',', ';')) |
|||
.Select(value => value.Trim()) |
|||
.Where(value => !string.IsNullOrWhiteSpace(value)) |
|||
.ToArray(); |
|||
} |
|||
|
|||
public Task SendAsync(string command) |
|||
{ |
|||
return Task.Run(() => Send(command)); |
|||
} |
|||
|
|||
public Task<string> QueryAsync(string command) |
|||
{ |
|||
return Task.Run(() => Query(command)); |
|||
} |
|||
|
|||
private void Send(string command) |
|||
{ |
|||
_io.WriteString(command.Trim() + "\n", true); |
|||
} |
|||
|
|||
private string Query(string command) |
|||
{ |
|||
Send(command); |
|||
return ((string)_io.ReadString()).Trim(); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
try |
|||
{ |
|||
_io.IO.Close(); |
|||
} |
|||
catch |
|||
{ |
|||
// Closing is best-effort because VISA COM implementations differ by vendor.
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
{ |
|||
"runtimeTarget": { |
|||
"name": ".NETCoreApp,Version=v10.0", |
|||
"signature": "" |
|||
}, |
|||
"compilationOptions": {}, |
|||
"targets": { |
|||
".NETCoreApp,Version=v10.0": { |
|||
"Housing/1.0.0": { |
|||
"runtime": { |
|||
"Housing.dll": {} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"libraries": { |
|||
"Housing/1.0.0": { |
|||
"type": "project", |
|||
"serviceable": false, |
|||
"sha512": "" |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,19 @@ |
|||
{ |
|||
"runtimeOptions": { |
|||
"tfm": "net10.0", |
|||
"frameworks": [ |
|||
{ |
|||
"name": "Microsoft.NETCore.App", |
|||
"version": "10.0.0" |
|||
}, |
|||
{ |
|||
"name": "Microsoft.WindowsDesktop.App", |
|||
"version": "10.0.0" |
|||
} |
|||
], |
|||
"configProperties": { |
|||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, |
|||
"CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,8 @@ |
|||
[Database] |
|||
IP=127.0.0.1 |
|||
Database=Housing |
|||
DbId=sa |
|||
DbPw=your_password |
|||
Encrypt=True |
|||
TrustServerCertificate=True |
|||
Timeout=5 |
|||
@ -0,0 +1,44 @@ |
|||
[Board] |
|||
PortName=Auto |
|||
BaudRate=115200 |
|||
ReadTimeout=5000 |
|||
ConnectCommand=x00c_001101:owt28006727ea97c7801 |
|||
ReadIdCommand=x00c_001101:ow2800326003e |
|||
CalDefaultCommand=x00c_001001 |
|||
EndToken=<end> |
|||
|
|||
[Equipment] |
|||
; 목표: 장비 화면에 표시되는 현재 측정값을 읽습니다. |
|||
; V 출력: Keysight 34465A DMM 값 |
|||
; A 출력: Keysight E36233A Power Supply CH1 Current 값 |
|||
; USB 장비는 Keysight Connection Expert 또는 NI MAX에서 보이는 VISA 주소를 Resource에 넣으세요. |
|||
; 예: USB0::0x2A8D::0x1301::MY12345678::INSTR |
|||
Timeout=5000 |
|||
SettleMilliseconds=300 |
|||
|
|||
; 34465A DMM |
|||
VoltageConnection=Visa |
|||
VoltageResource= |
|||
VoltageIdnMatch=34460,34461,34465,34470,3446,3447 |
|||
VoltageSetupCommand= |
|||
VoltageReadCommand=MEAS:VOLT:DC? |
|||
|
|||
; E36233A Power Supply |
|||
CurrentConnection=Visa |
|||
CurrentResource= |
|||
CurrentIdnMatch=E362 |
|||
CurrentSetupCommand= |
|||
CurrentReadCommand=MEAS:CURR? CH1 |
|||
|
|||
; COM 포트로 잡히는 장비 예시: |
|||
; VoltageConnection=Serial |
|||
; VoltagePortName=Auto |
|||
; VoltageBaudRate=115200 |
|||
|
|||
; LAN 장비 예시: |
|||
; VoltageConnection=Tcp |
|||
; VoltageHost=192.168.0.10 |
|||
; VoltagePort=5025 |
|||
|
|||
; E36233A 파워서플라이에서 CH1 전압까지 읽고 싶을 때 예시: |
|||
; VoltageReadCommand=MEAS:VOLT? CH1 |
|||
@ -0,0 +1,835 @@ |
|||
{ |
|||
"runtimeTarget": { |
|||
"name": ".NETCoreApp,Version=v9.0", |
|||
"signature": "" |
|||
}, |
|||
"compilationOptions": {}, |
|||
"targets": { |
|||
".NETCoreApp,Version=v9.0": { |
|||
"Housing/1.0.0": { |
|||
"dependencies": { |
|||
"Microsoft.Data.SqlClient": "6.1.3", |
|||
"System.IO.Ports": "9.0.10" |
|||
}, |
|||
"runtime": { |
|||
"Housing.dll": {} |
|||
} |
|||
}, |
|||
"Azure.Core/1.47.1": { |
|||
"dependencies": { |
|||
"Microsoft.Bcl.AsyncInterfaces": "8.0.0", |
|||
"System.ClientModel": "1.5.1", |
|||
"System.Memory.Data": "8.0.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Azure.Core.dll": { |
|||
"assemblyVersion": "1.47.1.0", |
|||
"fileVersion": "1.4700.125.36505" |
|||
} |
|||
} |
|||
}, |
|||
"Azure.Identity/1.14.2": { |
|||
"dependencies": { |
|||
"Azure.Core": "1.47.1", |
|||
"Microsoft.Identity.Client": "4.73.1", |
|||
"Microsoft.Identity.Client.Extensions.Msal": "4.73.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Azure.Identity.dll": { |
|||
"assemblyVersion": "1.14.2.0", |
|||
"fileVersion": "1.1400.225.36004" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Bcl.AsyncInterfaces/8.0.0": { |
|||
"runtime": { |
|||
"lib/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll": { |
|||
"assemblyVersion": "8.0.0.0", |
|||
"fileVersion": "8.0.23.53103" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Bcl.Cryptography/9.0.4": { |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Bcl.Cryptography.dll": { |
|||
"assemblyVersion": "9.0.0.4", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Data.SqlClient/6.1.3": { |
|||
"dependencies": { |
|||
"Azure.Core": "1.47.1", |
|||
"Azure.Identity": "1.14.2", |
|||
"Microsoft.Bcl.Cryptography": "9.0.4", |
|||
"Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", |
|||
"Microsoft.Extensions.Caching.Memory": "9.0.4", |
|||
"Microsoft.IdentityModel.JsonWebTokens": "7.7.1", |
|||
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", |
|||
"Microsoft.SqlServer.Server": "1.0.0" |
|||
}, |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Data.SqlClient.dll": { |
|||
"assemblyVersion": "6.0.0.0", |
|||
"fileVersion": "6.13.25316.3" |
|||
} |
|||
}, |
|||
"resources": { |
|||
"lib/net9.0/cs/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "cs" |
|||
}, |
|||
"lib/net9.0/de/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "de" |
|||
}, |
|||
"lib/net9.0/es/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "es" |
|||
}, |
|||
"lib/net9.0/fr/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "fr" |
|||
}, |
|||
"lib/net9.0/it/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "it" |
|||
}, |
|||
"lib/net9.0/ja/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "ja" |
|||
}, |
|||
"lib/net9.0/ko/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "ko" |
|||
}, |
|||
"lib/net9.0/pl/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "pl" |
|||
}, |
|||
"lib/net9.0/pt-BR/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "pt-BR" |
|||
}, |
|||
"lib/net9.0/ru/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "ru" |
|||
}, |
|||
"lib/net9.0/tr/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "tr" |
|||
}, |
|||
"lib/net9.0/zh-Hans/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "zh-Hans" |
|||
}, |
|||
"lib/net9.0/zh-Hant/Microsoft.Data.SqlClient.resources.dll": { |
|||
"locale": "zh-Hant" |
|||
} |
|||
}, |
|||
"runtimeTargets": { |
|||
"runtimes/unix/lib/net9.0/Microsoft.Data.SqlClient.dll": { |
|||
"rid": "unix", |
|||
"assetType": "runtime", |
|||
"assemblyVersion": "6.0.0.0", |
|||
"fileVersion": "6.13.25316.3" |
|||
}, |
|||
"runtimes/win/lib/net9.0/Microsoft.Data.SqlClient.dll": { |
|||
"rid": "win", |
|||
"assetType": "runtime", |
|||
"assemblyVersion": "6.0.0.0", |
|||
"fileVersion": "6.13.25316.3" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Data.SqlClient.SNI.runtime/6.0.2": { |
|||
"runtimeTargets": { |
|||
"runtimes/win-arm64/native/Microsoft.Data.SqlClient.SNI.dll": { |
|||
"rid": "win-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "6.2.0.0" |
|||
}, |
|||
"runtimes/win-x64/native/Microsoft.Data.SqlClient.SNI.dll": { |
|||
"rid": "win-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "6.2.0.0" |
|||
}, |
|||
"runtimes/win-x86/native/Microsoft.Data.SqlClient.SNI.dll": { |
|||
"rid": "win-x86", |
|||
"assetType": "native", |
|||
"fileVersion": "6.2.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Extensions.Caching.Abstractions/9.0.4": { |
|||
"dependencies": { |
|||
"Microsoft.Extensions.Primitives": "9.0.4" |
|||
}, |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Extensions.Caching.Abstractions.dll": { |
|||
"assemblyVersion": "9.0.0.0", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Extensions.Caching.Memory/9.0.4": { |
|||
"dependencies": { |
|||
"Microsoft.Extensions.Caching.Abstractions": "9.0.4", |
|||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.4", |
|||
"Microsoft.Extensions.Logging.Abstractions": "9.0.4", |
|||
"Microsoft.Extensions.Options": "9.0.4", |
|||
"Microsoft.Extensions.Primitives": "9.0.4" |
|||
}, |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Extensions.Caching.Memory.dll": { |
|||
"assemblyVersion": "9.0.0.0", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Extensions.DependencyInjection.Abstractions/9.0.4": { |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { |
|||
"assemblyVersion": "9.0.0.0", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Extensions.Logging.Abstractions/9.0.4": { |
|||
"dependencies": { |
|||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.4" |
|||
}, |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll": { |
|||
"assemblyVersion": "9.0.0.0", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Extensions.Options/9.0.4": { |
|||
"dependencies": { |
|||
"Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.4", |
|||
"Microsoft.Extensions.Primitives": "9.0.4" |
|||
}, |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Extensions.Options.dll": { |
|||
"assemblyVersion": "9.0.0.0", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Extensions.Primitives/9.0.4": { |
|||
"runtime": { |
|||
"lib/net9.0/Microsoft.Extensions.Primitives.dll": { |
|||
"assemblyVersion": "9.0.0.0", |
|||
"fileVersion": "9.0.425.16305" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Identity.Client/4.73.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.Abstractions": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.Identity.Client.dll": { |
|||
"assemblyVersion": "4.73.1.0", |
|||
"fileVersion": "4.73.1.0" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.Identity.Client.Extensions.Msal/4.73.1": { |
|||
"dependencies": { |
|||
"Microsoft.Identity.Client": "4.73.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.Identity.Client.Extensions.Msal.dll": { |
|||
"assemblyVersion": "4.73.1.0", |
|||
"fileVersion": "4.73.1.0" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.IdentityModel.Abstractions/7.7.1": { |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.IdentityModel.Abstractions.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.IdentityModel.JsonWebTokens/7.7.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.Tokens": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.IdentityModel.Logging/7.7.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.Abstractions": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.IdentityModel.Logging.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.IdentityModel.Protocols/7.7.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.Tokens": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.IdentityModel.Protocols.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/7.7.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.Protocols": "7.7.1", |
|||
"System.IdentityModel.Tokens.Jwt": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.IdentityModel.Tokens/7.7.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.Logging": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/Microsoft.IdentityModel.Tokens.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"Microsoft.SqlServer.Server/1.0.0": { |
|||
"runtime": { |
|||
"lib/netstandard2.0/Microsoft.SqlServer.Server.dll": { |
|||
"assemblyVersion": "1.0.0.0", |
|||
"fileVersion": "1.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.android-arm.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/android-arm/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "android-arm", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.android-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/android-arm64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "android-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.android-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/android-x64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "android-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.android-x86.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/android-x86/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "android-x86", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-arm.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-arm/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-arm", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-arm64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-bionic-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-bionic-arm64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-bionic-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-bionic-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-bionic-x64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-bionic-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-musl-arm.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-musl-arm/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-musl-arm", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-musl-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-musl-arm64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-musl-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-musl-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-musl-x64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-musl-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.linux-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/linux-x64/native/libSystem.IO.Ports.Native.so": { |
|||
"rid": "linux-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.maccatalyst-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/maccatalyst-arm64/native/libSystem.IO.Ports.Native.dylib": { |
|||
"rid": "maccatalyst-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.maccatalyst-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/maccatalyst-x64/native/libSystem.IO.Ports.Native.dylib": { |
|||
"rid": "maccatalyst-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.native.System.IO.Ports/9.0.10": { |
|||
"dependencies": { |
|||
"runtime.android-arm.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.android-arm64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.android-x64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.android-x86.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-arm.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-arm64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-bionic-arm64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-bionic-x64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-musl-arm.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-musl-arm64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-musl-x64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.linux-x64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.maccatalyst-arm64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.maccatalyst-x64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.osx-arm64.runtime.native.System.IO.Ports": "9.0.10", |
|||
"runtime.osx-x64.runtime.native.System.IO.Ports": "9.0.10" |
|||
} |
|||
}, |
|||
"runtime.osx-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/osx-arm64/native/libSystem.IO.Ports.Native.dylib": { |
|||
"rid": "osx-arm64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"runtime.osx-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"runtimeTargets": { |
|||
"runtimes/osx-x64/native/libSystem.IO.Ports.Native.dylib": { |
|||
"rid": "osx-x64", |
|||
"assetType": "native", |
|||
"fileVersion": "0.0.0.0" |
|||
} |
|||
} |
|||
}, |
|||
"System.ClientModel/1.5.1": { |
|||
"dependencies": { |
|||
"Microsoft.Extensions.Logging.Abstractions": "9.0.4", |
|||
"System.Memory.Data": "8.0.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/System.ClientModel.dll": { |
|||
"assemblyVersion": "1.5.1.0", |
|||
"fileVersion": "1.500.125.36405" |
|||
} |
|||
} |
|||
}, |
|||
"System.IdentityModel.Tokens.Jwt/7.7.1": { |
|||
"dependencies": { |
|||
"Microsoft.IdentityModel.JsonWebTokens": "7.7.1", |
|||
"Microsoft.IdentityModel.Tokens": "7.7.1" |
|||
}, |
|||
"runtime": { |
|||
"lib/net8.0/System.IdentityModel.Tokens.Jwt.dll": { |
|||
"assemblyVersion": "7.7.1.0", |
|||
"fileVersion": "7.7.1.50719" |
|||
} |
|||
} |
|||
}, |
|||
"System.IO.Ports/9.0.10": { |
|||
"dependencies": { |
|||
"runtime.native.System.IO.Ports": "9.0.10" |
|||
}, |
|||
"runtime": { |
|||
"lib/net9.0/System.IO.Ports.dll": { |
|||
"assemblyVersion": "9.0.0.10", |
|||
"fileVersion": "9.0.1025.47515" |
|||
} |
|||
}, |
|||
"runtimeTargets": { |
|||
"runtimes/unix/lib/net9.0/System.IO.Ports.dll": { |
|||
"rid": "unix", |
|||
"assetType": "runtime", |
|||
"assemblyVersion": "9.0.0.10", |
|||
"fileVersion": "9.0.1025.47515" |
|||
}, |
|||
"runtimes/win/lib/net9.0/System.IO.Ports.dll": { |
|||
"rid": "win", |
|||
"assetType": "runtime", |
|||
"assemblyVersion": "9.0.0.10", |
|||
"fileVersion": "9.0.1025.47515" |
|||
} |
|||
} |
|||
}, |
|||
"System.Memory.Data/8.0.1": { |
|||
"runtime": { |
|||
"lib/net8.0/System.Memory.Data.dll": { |
|||
"assemblyVersion": "8.0.0.1", |
|||
"fileVersion": "8.0.1024.46610" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
}, |
|||
"libraries": { |
|||
"Housing/1.0.0": { |
|||
"type": "project", |
|||
"serviceable": false, |
|||
"sha512": "" |
|||
}, |
|||
"Azure.Core/1.47.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", |
|||
"path": "azure.core/1.47.1", |
|||
"hashPath": "azure.core.1.47.1.nupkg.sha512" |
|||
}, |
|||
"Azure.Identity/1.14.2": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", |
|||
"path": "azure.identity/1.14.2", |
|||
"hashPath": "azure.identity.1.14.2.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Bcl.AsyncInterfaces/8.0.0": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", |
|||
"path": "microsoft.bcl.asyncinterfaces/8.0.0", |
|||
"hashPath": "microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Bcl.Cryptography/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==", |
|||
"path": "microsoft.bcl.cryptography/9.0.4", |
|||
"hashPath": "microsoft.bcl.cryptography.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Data.SqlClient/6.1.3": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-ys/z8Tx8074CDU20EilNvBRJuJdwKSthpHkzUpt3JghnjB6GjbZusoOcCtNbhPCCWsEJqN8bxaT7HnS3UZuUDQ==", |
|||
"path": "microsoft.data.sqlclient/6.1.3", |
|||
"hashPath": "microsoft.data.sqlclient.6.1.3.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Data.SqlClient.SNI.runtime/6.0.2": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==", |
|||
"path": "microsoft.data.sqlclient.sni.runtime/6.0.2", |
|||
"hashPath": "microsoft.data.sqlclient.sni.runtime.6.0.2.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Extensions.Caching.Abstractions/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-imcZ5BGhBw5mNsWLepBbqqumWaFe0GtvyCvne2/2wsDIBRa2+Lhx4cU/pKt/4BwOizzUEOls2k1eOJQXHGMalg==", |
|||
"path": "microsoft.extensions.caching.abstractions/9.0.4", |
|||
"hashPath": "microsoft.extensions.caching.abstractions.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Extensions.Caching.Memory/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-G5rEq1Qez5VJDTEyRsRUnewAspKjaY57VGsdZ8g8Ja6sXXzoiI3PpTd1t43HjHqNWD5A06MQveb2lscn+2CU+w==", |
|||
"path": "microsoft.extensions.caching.memory/9.0.4", |
|||
"hashPath": "microsoft.extensions.caching.memory.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Extensions.DependencyInjection.Abstractions/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-UI0TQPVkS78bFdjkTodmkH0Fe8lXv9LnhGFKgKrsgUJ5a5FVdFRcgjIkBVLbGgdRhxWirxH/8IXUtEyYJx6GQg==", |
|||
"path": "microsoft.extensions.dependencyinjection.abstractions/9.0.4", |
|||
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Extensions.Logging.Abstractions/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-0MXlimU4Dud6t+iNi5NEz3dO2w1HXdhoOLaYFuLPCjAsvlPQGwOT6V2KZRMLEhCAm/stSZt1AUv0XmDdkjvtbw==", |
|||
"path": "microsoft.extensions.logging.abstractions/9.0.4", |
|||
"hashPath": "microsoft.extensions.logging.abstractions.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Extensions.Options/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-fiFI2+58kicqVZyt/6obqoFwHiab7LC4FkQ3mmiBJ28Yy4fAvy2+v9MRnSvvlOO8chTOjKsdafFl/K9veCPo5g==", |
|||
"path": "microsoft.extensions.options/9.0.4", |
|||
"hashPath": "microsoft.extensions.options.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Extensions.Primitives/9.0.4": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-SPFyMjyku1nqTFFJ928JAMd0QnRe4xjE7KeKnZMWXf3xk+6e0WiOZAluYtLdbJUXtsl2cCRSi8cBquJ408k8RA==", |
|||
"path": "microsoft.extensions.primitives/9.0.4", |
|||
"hashPath": "microsoft.extensions.primitives.9.0.4.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Identity.Client/4.73.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", |
|||
"path": "microsoft.identity.client/4.73.1", |
|||
"hashPath": "microsoft.identity.client.4.73.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.Identity.Client.Extensions.Msal/4.73.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", |
|||
"path": "microsoft.identity.client.extensions.msal/4.73.1", |
|||
"hashPath": "microsoft.identity.client.extensions.msal.4.73.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.IdentityModel.Abstractions/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==", |
|||
"path": "microsoft.identitymodel.abstractions/7.7.1", |
|||
"hashPath": "microsoft.identitymodel.abstractions.7.7.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.IdentityModel.JsonWebTokens/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", |
|||
"path": "microsoft.identitymodel.jsonwebtokens/7.7.1", |
|||
"hashPath": "microsoft.identitymodel.jsonwebtokens.7.7.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.IdentityModel.Logging/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", |
|||
"path": "microsoft.identitymodel.logging/7.7.1", |
|||
"hashPath": "microsoft.identitymodel.logging.7.7.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.IdentityModel.Protocols/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", |
|||
"path": "microsoft.identitymodel.protocols/7.7.1", |
|||
"hashPath": "microsoft.identitymodel.protocols.7.7.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", |
|||
"path": "microsoft.identitymodel.protocols.openidconnect/7.7.1", |
|||
"hashPath": "microsoft.identitymodel.protocols.openidconnect.7.7.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.IdentityModel.Tokens/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", |
|||
"path": "microsoft.identitymodel.tokens/7.7.1", |
|||
"hashPath": "microsoft.identitymodel.tokens.7.7.1.nupkg.sha512" |
|||
}, |
|||
"Microsoft.SqlServer.Server/1.0.0": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==", |
|||
"path": "microsoft.sqlserver.server/1.0.0", |
|||
"hashPath": "microsoft.sqlserver.server.1.0.0.nupkg.sha512" |
|||
}, |
|||
"runtime.android-arm.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-KUeHD0wRFCTS9QHantD5Cv/RzDzVY/mQP1Z/eKLtlX5A5SZvsqeomAoayPdh/QmgSzquoHeIDMAMp8VVU+Xzag==", |
|||
"path": "runtime.android-arm.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.android-arm.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.android-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-b+z8JoBrZ5TMXiXeh0s+s9/uIVx6PmulEuMaN81JLM68aAb4DWHi7t5CL+8bWJhsFhd8VAYoZ9pi5miNTFPeuA==", |
|||
"path": "runtime.android-arm64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.android-arm64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.android-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-K7u+/G2gPoRLNc974p1Tnp44VRLlpQWZrKEQofBTpyJZPgd46ayvXayqT4jyGodG4O6Q6+yY2pYUYlqv1K2l6w==", |
|||
"path": "runtime.android-x64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.android-x64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.android-x86.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-TGT4P40ockzrZf/K46A3VAl2dC2PWAS6WhqqtZJbH5G7XgBMX/FoaoY6DtFtz6u7RVl4zhjdG3QWXEv/u/1Hlg==", |
|||
"path": "runtime.android-x86.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.android-x86.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-arm.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-Y2EEaUtO1JolypkFcqgsDxjmOleHa7d9OxBY4Osw5vIdQpOfP0Qj30czQfkN7cZTQH8NxsSr5WawVbk5yFabFg==", |
|||
"path": "runtime.linux-arm.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-arm.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-nIFQGoz22wdgtmS4Ce+weqGUBh1kpO4XbNEgCU01+7P/+yZAb+gbRSeJUyUmCPhyW0S8FhX1xgJDH/SiJgP05Q==", |
|||
"path": "runtime.linux-arm64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-arm64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-bionic-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-jV3esGC4j69yPlRzj50EbJq4syweBm4rOWKYJ3nWCMbVzTW1YQ2o4QhiVjCDOEKEf6q5eVGEaa6fyVXQ/K95Hw==", |
|||
"path": "runtime.linux-bionic-arm64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-bionic-arm64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-bionic-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-UfoiSWuf75mYJPknRXSezDoFYaCp5dWoUjASjg6gQSa7FD2G59Mee6vMEzHFS+x8N+H8oNnL9TCIZUD4/8e/2Q==", |
|||
"path": "runtime.linux-bionic-x64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-bionic-x64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-musl-arm.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-CWwqjMVVtYiW4I9wk9YuUaSxxkPZKZ/BKj5ppAsIZv4X9u/dyh8+Qbj3Fly61uUXpGxXU4QFQhYuPi5pJTAOBA==", |
|||
"path": "runtime.linux-musl-arm.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-musl-arm.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-musl-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-NxMtoYPV8lreQggsXWsXXRF/djycKieThc4O2kxGB6EgjsiRDuNdnbODV0lWGV6v4mn08uQvuOhmBP5ZYpVdkA==", |
|||
"path": "runtime.linux-musl-arm64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-musl-arm64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-musl-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-ZNQ/D4lUGxTbfGAZRWP4vp7tCLqvInit03YXAiFXDWh/DnMEosBjrwcu8vbWgSsF01DUbyZQai8lwAStIZWo8w==", |
|||
"path": "runtime.linux-musl-x64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-musl-x64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.linux-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-mOTksL8qwN9EiWz71Dzhc98iQhKmtHlWH5GbhiCJ+ES2ei1HLr2bXSNMrXRd5s0Wfzg6xeQmT5M9umcgtv8Bzg==", |
|||
"path": "runtime.linux-x64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.linux-x64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.maccatalyst-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-aKoLfCdLoQGhPh2VYBn6sn1kDuwgtXKJ4D2Ql/2WLCGCuXestpxLBS0JhSVBFSp1HrFdazj4aSwpYurtes+1Gg==", |
|||
"path": "runtime.maccatalyst-arm64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.maccatalyst-arm64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.maccatalyst-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-B+boSbUptH2fiKiUXBIu5hlQ3oH+nAdPY9TNmpU10nuoFW/DNz21fCXY3UOIz9oRXp5Ao2b7RlurgpBl0AgoOQ==", |
|||
"path": "runtime.maccatalyst-x64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.maccatalyst-x64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-7AzXN+J8PkTVctfymH+tEaAmj0wNKFPyACqp5cYff0DrHxnsQhv7xtRWxJRrQ0azOAFGR1mhWN4aM1QkbQQ0Rw==", |
|||
"path": "runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.osx-arm64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-m+gRRrmCTwP30YiVnFeZg/zRWgzVcOlN28cIPMkK11C9UU60waLknTnRLlQUagIkWaCDifKJCB6wtEeca5QiMA==", |
|||
"path": "runtime.osx-arm64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.osx-arm64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"runtime.osx-x64.runtime.native.System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-89HgF1Oplzjomn0BLeqJxEC17d//zmbs7CMKT12ZvjvFMvpMFO8uQUZ9xRIh91rM0ByfbhSobe2IRezjpeDNlg==", |
|||
"path": "runtime.osx-x64.runtime.native.system.io.ports/9.0.10", |
|||
"hashPath": "runtime.osx-x64.runtime.native.system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"System.ClientModel/1.5.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", |
|||
"path": "system.clientmodel/1.5.1", |
|||
"hashPath": "system.clientmodel.1.5.1.nupkg.sha512" |
|||
}, |
|||
"System.IdentityModel.Tokens.Jwt/7.7.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", |
|||
"path": "system.identitymodel.tokens.jwt/7.7.1", |
|||
"hashPath": "system.identitymodel.tokens.jwt.7.7.1.nupkg.sha512" |
|||
}, |
|||
"System.IO.Ports/9.0.10": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-jMvwu+NOk/+vlOzTp9vpxIeGq+yRA+3EbkmpLMs37AAy9cI8YlY/ntTHL00w26Tvu6cIkx0/TdjmeHm0l99Nqw==", |
|||
"path": "system.io.ports/9.0.10", |
|||
"hashPath": "system.io.ports.9.0.10.nupkg.sha512" |
|||
}, |
|||
"System.Memory.Data/8.0.1": { |
|||
"type": "package", |
|||
"serviceable": true, |
|||
"sha512": "sha512-BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==", |
|||
"path": "system.memory.data/8.0.1", |
|||
"hashPath": "system.memory.data.8.0.1.nupkg.sha512" |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,19 @@ |
|||
{ |
|||
"runtimeOptions": { |
|||
"tfm": "net9.0", |
|||
"frameworks": [ |
|||
{ |
|||
"name": "Microsoft.NETCore.App", |
|||
"version": "9.0.0" |
|||
}, |
|||
{ |
|||
"name": "Microsoft.WindowsDesktop.App", |
|||
"version": "9.0.0" |
|||
} |
|||
], |
|||
"configProperties": { |
|||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, |
|||
"CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
[Judgement] |
|||
VMin=0.00 |
|||
VMax=9.99 |
|||
AMin=0.00 |
|||
AMax=9.99 |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue