You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
23 KiB
713 lines
23 KiB
using System.Globalization;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
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를 스캔해주세요.";
|
|
private const string BoardReadyMessage = "스캔 완료. 시료 투입 후 Housing 장비 시작 버튼을 눌러주세요.";
|
|
private const string BarcodeInvalidMessage = "스캔 형식이 올바르지 않습니다. PCBA S/N 바코드를 스캔해주세요.";
|
|
private const string DefaultOutputValue = "0.00";
|
|
private const int ScannerInputMinimumLength = 4;
|
|
private const decimal MilliampsPerAmp = 1000m;
|
|
private static readonly TimeSpan ScannerInputMaxGap = TimeSpan.FromMilliseconds(80);
|
|
private static readonly TimeSpan ScannerInputIdleCommitDelay = TimeSpan.FromMilliseconds(250);
|
|
|
|
private readonly StringBuilder _scannerInputBuffer = new();
|
|
private readonly DispatcherTimer _scannerInputCommitTimer;
|
|
private SerialBarcodeScanner? _barcodeScanner;
|
|
private decimal? _lastMeasuredVoltage;
|
|
private DateTime _lastScannerInputAt = DateTime.MinValue;
|
|
private StartupLoginWindowResult _loginResult = new();
|
|
private InspectionJudgementSettings _inspectionSettings;
|
|
private bool _isStartupLoginShown;
|
|
private bool _isBoardTestRunning;
|
|
|
|
public MainWindow()
|
|
{
|
|
_inspectionSettings = InspectionSettingsStore.Load(GetInspectionSettingsIniPath());
|
|
|
|
InitializeComponent();
|
|
_scannerInputCommitTimer = new DispatcherTimer { Interval = ScannerInputIdleCommitDelay };
|
|
_scannerInputCommitTimer.Tick += ScannerInputCommitTimer_Tick;
|
|
CommandManager.AddPreviewExecutedHandler(BarcodeTextBox, BarcodeTextBox_PreviewExecuted);
|
|
|
|
ApplyInspectionSettingsToTextBoxes();
|
|
ApplyLoginResult();
|
|
ResetScreen();
|
|
}
|
|
|
|
protected override void OnContentRendered(EventArgs e)
|
|
{
|
|
base.OnContentRendered(e);
|
|
|
|
if (_isStartupLoginShown)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_isStartupLoginShown = true;
|
|
if (!ShowLoginPopup())
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
|
|
protected override void OnClosed(EventArgs e)
|
|
{
|
|
_barcodeScanner?.Dispose();
|
|
_scannerInputCommitTimer.Stop();
|
|
CommandManager.RemovePreviewExecutedHandler(BarcodeTextBox, BarcodeTextBox_PreviewExecuted);
|
|
|
|
base.OnClosed(e);
|
|
}
|
|
|
|
private void BarcodeTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(BarcodeTextBox.Text))
|
|
{
|
|
IcSnOutputTextBox.Clear();
|
|
ResultOutputTextBox.Text = BarcodeRequiredMessage;
|
|
return;
|
|
}
|
|
|
|
ResultOutputTextBox.Text = BoardReadyMessage;
|
|
}
|
|
|
|
private void BarcodeTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
|
|
{
|
|
e.Handled = true;
|
|
AppendScannerText(e.Text);
|
|
}
|
|
|
|
private async void BarcodeTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
|
|
{
|
|
if (e.Key == Key.Enter || e.Key == Key.Return || e.Key == Key.Tab)
|
|
{
|
|
e.Handled = true;
|
|
await CompleteScannerInputAsync(runBoardSequence: true);
|
|
return;
|
|
}
|
|
|
|
if (e.Key == Key.Back || e.Key == Key.Delete || e.Key == Key.Escape ||
|
|
Keyboard.Modifiers.HasFlag(ModifierKeys.Control) ||
|
|
Keyboard.Modifiers.HasFlag(ModifierKeys.Alt))
|
|
{
|
|
e.Handled = true;
|
|
ClearPendingScannerInput();
|
|
return;
|
|
}
|
|
}
|
|
|
|
private void BarcodeTextBox_PreviewExecuted(object sender, ExecutedRoutedEventArgs e)
|
|
{
|
|
e.Handled = true;
|
|
}
|
|
|
|
private void ScannerInputCommitTimer_Tick(object? sender, EventArgs e)
|
|
{
|
|
_scannerInputCommitTimer.Stop();
|
|
_ = CompleteScannerInputAsync(runBoardSequence: true);
|
|
}
|
|
|
|
private void AppendScannerText(string text)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
if (_scannerInputBuffer.Length > 0 && now - _lastScannerInputAt > ScannerInputMaxGap)
|
|
{
|
|
_scannerInputBuffer.Clear();
|
|
}
|
|
|
|
_lastScannerInputAt = now;
|
|
_scannerInputBuffer.Append(text);
|
|
_scannerInputCommitTimer.Stop();
|
|
_scannerInputCommitTimer.Start();
|
|
}
|
|
|
|
private async Task CompleteScannerInputAsync(bool runBoardSequence)
|
|
{
|
|
_scannerInputCommitTimer.Stop();
|
|
|
|
var scannerText = _scannerInputBuffer.ToString().Trim();
|
|
_scannerInputBuffer.Clear();
|
|
|
|
await ProcessScannerTextAsync(scannerText, runBoardSequence, showInvalidInput: true);
|
|
}
|
|
|
|
private async Task<bool> ProcessScannerTextAsync(string scannerText, bool runBoardSequence, bool showInvalidInput)
|
|
{
|
|
scannerText = scannerText.Trim();
|
|
var hasPendingScannerInput = scannerText.Length > 0;
|
|
var acceptedScannerInput = TryExtractPcbBarcode(scannerText, out var scannedBarcode);
|
|
|
|
if (acceptedScannerInput)
|
|
{
|
|
BarcodeTextBox.Text = scannedBarcode;
|
|
BarcodeTextBox.CaretIndex = BarcodeTextBox.Text.Length;
|
|
}
|
|
else if (showInvalidInput && hasPendingScannerInput && scannerText.Length >= ScannerInputMinimumLength)
|
|
{
|
|
BarcodeTextBox.Clear();
|
|
ResultOutputTextBox.Text = BarcodeInvalidMessage;
|
|
FocusBarcodeInput();
|
|
}
|
|
|
|
if (runBoardSequence)
|
|
{
|
|
if (!hasPendingScannerInput || acceptedScannerInput)
|
|
{
|
|
await RunBoardSequenceAsync();
|
|
}
|
|
|
|
return acceptedScannerInput;
|
|
}
|
|
|
|
FocusBarcodeInput();
|
|
return acceptedScannerInput;
|
|
}
|
|
|
|
private void StartBarcodeScanner()
|
|
{
|
|
if (_barcodeScanner is not null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SerialBarcodeScanner? barcodeScanner = null;
|
|
try
|
|
{
|
|
var hardwareIniPath = GetHardwareIniPath();
|
|
var settings = BarcodeScannerSettings.Load(hardwareIniPath);
|
|
if (!settings.Enabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
barcodeScanner = new SerialBarcodeScanner(settings, GetReservedBarcodeScannerPortNames(hardwareIniPath));
|
|
barcodeScanner.ScanReceived += BarcodeScanner_ScanReceived;
|
|
barcodeScanner.Start();
|
|
_barcodeScanner = barcodeScanner;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
barcodeScanner?.Dispose();
|
|
_barcodeScanner?.Dispose();
|
|
_barcodeScanner = null;
|
|
ResultOutputTextBox.Text = $"{BarcodeRequiredMessage}\r\nBarcode scanner COM: {ex.Message}";
|
|
}
|
|
}
|
|
|
|
private void BarcodeScanner_ScanReceived(object? sender, BarcodeScanReceivedEventArgs e)
|
|
{
|
|
Dispatcher.BeginInvoke(new Action(async () =>
|
|
{
|
|
try
|
|
{
|
|
var hasBarcodeFormat = TryExtractPcbBarcode(e.Text, out _);
|
|
if (hasBarcodeFormat)
|
|
{
|
|
_barcodeScanner?.ConfirmPort(e.PortName);
|
|
}
|
|
|
|
await ProcessScannerTextAsync(e.Text, runBoardSequence: true, showInvalidInput: e.IsConfirmedPort);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ResultOutputTextBox.Text = $"Barcode scanner input failed: {ex.Message}";
|
|
}
|
|
}), DispatcherPriority.Input);
|
|
}
|
|
|
|
private static bool TryExtractPcbBarcode(string scannerText, out string pcbBarcode)
|
|
{
|
|
pcbBarcode = string.Empty;
|
|
if (string.IsNullOrWhiteSpace(scannerText))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var pcbaSerialMatch = Regex.Match(
|
|
scannerText,
|
|
@"PCBA\s*S\/N\s*:\s*(?<serial>[A-Za-z0-9-]{4,50})",
|
|
RegexOptions.IgnoreCase);
|
|
if (pcbaSerialMatch.Success)
|
|
{
|
|
pcbBarcode = pcbaSerialMatch.Groups["serial"].Value.Trim();
|
|
return true;
|
|
}
|
|
|
|
var plainSerial = scannerText.Trim();
|
|
if (IsPlainPcbBarcode(plainSerial))
|
|
{
|
|
pcbBarcode = plainSerial;
|
|
return true;
|
|
}
|
|
|
|
var fields = scannerText
|
|
.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
|
.Select(part => part.Split(':', 2, StringSplitOptions.TrimEntries))
|
|
.Where(parts => parts.Length == 2)
|
|
.ToDictionary(parts => parts[0], parts => parts[1], StringComparer.OrdinalIgnoreCase);
|
|
|
|
if (!fields.ContainsKey("MODEL") ||
|
|
!fields.TryGetValue("SERIAL", out var serial) ||
|
|
!fields.ContainsKey("OPTION") ||
|
|
string.IsNullOrWhiteSpace(serial))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pcbBarcode = serial.Trim();
|
|
return pcbBarcode.Length <= 50;
|
|
}
|
|
|
|
private static bool IsPlainPcbBarcode(string value)
|
|
{
|
|
return Regex.IsMatch(value, @"^[A-Za-z0-9][A-Za-z0-9-]{3,49}$") &&
|
|
Regex.IsMatch(value, @"\d");
|
|
}
|
|
|
|
private void ClearPendingScannerInput()
|
|
{
|
|
_scannerInputCommitTimer.Stop();
|
|
_scannerInputBuffer.Clear();
|
|
}
|
|
|
|
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);
|
|
|
|
var boardSettings = BoardHardwareSettings.Load(GetHardwareIniPath());
|
|
var equipmentSettings = EquipmentMeasurementSettings.Load(GetHardwareIniPath());
|
|
var startSignalSettings = StartSignalSettings.Load(GetHardwareIniPath());
|
|
|
|
var startSignalReceivedAt = await WaitForEquipmentStartSignalAsync(startSignalSettings);
|
|
var measurementNotBeforeUtc = startSignalReceivedAt?.AddMilliseconds(startSignalSettings.PostSignalDelayMilliseconds);
|
|
|
|
ResultOutputTextBox.Text =
|
|
"보드 테스트 시작\r\n" +
|
|
"1. 시료 연결 확인\r\n" +
|
|
"2. IC_SN 읽기\r\n" +
|
|
"3. CAL DEFAULT 조건 설정\r\n" +
|
|
"4. 시작 신호 기준 지연 후 계측 장비에서 V/A 읽기";
|
|
|
|
var service = new BoardTestService(boardSettings, equipmentSettings);
|
|
string? dbHeaderSaveError = null;
|
|
var measurement = await service.RunAsync(
|
|
async icSn =>
|
|
{
|
|
IcSnOutputTextBox.Text = icSn;
|
|
dbHeaderSaveError = await SaveHeaderStatusAsync(icSn);
|
|
},
|
|
measurementNotBeforeUtc);
|
|
|
|
IcSnOutputTextBox.Text = measurement.IcSn;
|
|
_lastMeasuredVoltage = measurement.Voltage;
|
|
VOutputTextBox.Text = FormatDecimal(measurement.Voltage);
|
|
AOutputTextBox.Text = FormatCurrent(measurement.Current);
|
|
|
|
var resultCode = _inspectionSettings.IsPass(measurement.Voltage, measurement.Current) ? "OK" : "NG";
|
|
ResultOutputTextBox.Text = resultCode;
|
|
|
|
var dbMeasurementSaveError = await SaveMeasurementStatusAsync(resultCode);
|
|
ResultOutputTextBox.Text = $"{resultCode}\r\n{BuildDbSaveMessage(dbHeaderSaveError, dbMeasurementSaveError)}";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ResultOutputTextBox.Text = $"보드 테스트 실패: {ex.Message}";
|
|
}
|
|
finally
|
|
{
|
|
_isBoardTestRunning = false;
|
|
SetInputEnabled(true);
|
|
FocusBarcodeInput();
|
|
}
|
|
}
|
|
|
|
private async Task<DateTime?> WaitForEquipmentStartSignalAsync(StartSignalSettings settings)
|
|
{
|
|
if (!settings.Enabled)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
ResultOutputTextBox.Text =
|
|
"장비 시작 신호 대기\r\n" +
|
|
$"{StartSignalWatcher.Describe(settings)}\r\n" +
|
|
"시료를 넣고 Housing 장비 시작 버튼을 눌러주세요.";
|
|
|
|
await StartSignalWatcher.WaitForStartAsync(settings);
|
|
var receivedAt = DateTime.UtcNow;
|
|
|
|
if (settings.PostSignalDelayMilliseconds <= 0)
|
|
{
|
|
ResultOutputTextBox.Text = "장비 시작 신호 수신 완료";
|
|
return receivedAt;
|
|
}
|
|
|
|
ResultOutputTextBox.Text =
|
|
"장비 시작 신호 수신 완료\r\n" +
|
|
$"전압 측정은 신호 수신 후 {settings.PostSignalDelayMilliseconds / 1000.0:0.###}초 이후 시작합니다.";
|
|
return receivedAt;
|
|
}
|
|
|
|
private async void LogoutButton_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
LogoutButton.IsEnabled = false;
|
|
await RunEquipmentLogoutCommandAsync();
|
|
|
|
ResetScreen();
|
|
_loginResult = new StartupLoginWindowResult();
|
|
OperatorTextBlock.Text = "-";
|
|
LogoutButton.Visibility = Visibility.Collapsed;
|
|
|
|
if (!ShowLoginPopup())
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
|
|
private async Task RunEquipmentLogoutCommandAsync()
|
|
{
|
|
try
|
|
{
|
|
var equipmentSettings = EquipmentMeasurementSettings.Load(GetHardwareIniPath());
|
|
var equipmentService = new EquipmentMeasurementService(equipmentSettings);
|
|
await equipmentService.RunLogoutCommandAsync();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, $"파워서플라이 logout 전원 OFF 실패: {ex.Message}", "Logout", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|
}
|
|
}
|
|
|
|
private bool ShowLoginPopup()
|
|
{
|
|
var loginWindow = new StartupLoginWindow
|
|
{
|
|
Owner = this
|
|
};
|
|
|
|
if (loginWindow.ShowDialog() != true)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
_loginResult = loginWindow.Result;
|
|
ApplyLoginResult();
|
|
ResetScreen();
|
|
StartBarcodeScanner();
|
|
return true;
|
|
}
|
|
|
|
private void ApplyLoginResult()
|
|
{
|
|
OperatorTextBlock.Text = string.IsNullOrWhiteSpace(_loginResult.Operator) ? "-" : _loginResult.Operator;
|
|
var isLoggedIn = !string.IsNullOrWhiteSpace(_loginResult.Operator);
|
|
LogoutButton.Visibility = isLoggedIn
|
|
? Visibility.Visible
|
|
: Visibility.Collapsed;
|
|
LogoutButton.IsEnabled = isLoggedIn;
|
|
}
|
|
|
|
private void ResetScreen()
|
|
{
|
|
BarcodeTextBox.Clear();
|
|
IcSnOutputTextBox.Clear();
|
|
_lastMeasuredVoltage = null;
|
|
VOutputTextBox.Text = DefaultOutputValue;
|
|
AOutputTextBox.Text = FormatCurrent(0m);
|
|
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<string?> SaveHeaderStatusAsync(string icSn)
|
|
{
|
|
try
|
|
{
|
|
var repository = new HousingAssemblyRepository(DatabaseSettings.Load(GetDatabaseIniPath()));
|
|
var record = CreateHeaderRecord(icSn);
|
|
await repository.UpsertHeaderAsync(record);
|
|
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return ex.Message;
|
|
}
|
|
}
|
|
|
|
private async Task<string?> SaveMeasurementStatusAsync(string resultCode)
|
|
{
|
|
try
|
|
{
|
|
var repository = new HousingAssemblyRepository(DatabaseSettings.Load(GetDatabaseIniPath()));
|
|
var record = CreateMeasurementRecord(resultCode);
|
|
await repository.UpdateMeasurementAsync(record);
|
|
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return ex.Message;
|
|
}
|
|
}
|
|
|
|
private static string BuildDbSaveMessage(params string?[] errors)
|
|
{
|
|
var failureMessages = new List<string>();
|
|
foreach (var error in errors)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(error))
|
|
{
|
|
failureMessages.Add(error);
|
|
}
|
|
}
|
|
|
|
return failureMessages.Count == 0
|
|
? "DB 저장 완료"
|
|
: $"DB 저장 실패 ({string.Join("; ", failureMessages)})";
|
|
}
|
|
|
|
private HousingAssemblyRecord CreateHeaderRecord(string icSn)
|
|
{
|
|
var barcode = BarcodeTextBox.Text.Trim();
|
|
|
|
return new HousingAssemblyRecord
|
|
{
|
|
IcSn = icSn,
|
|
PcbBarcode = barcode,
|
|
Maker = _loginResult.Maker,
|
|
Model = _loginResult.Model,
|
|
Variant1 = _loginResult.Variant1,
|
|
Variant2 = _loginResult.Variant2,
|
|
Operator = _loginResult.Operator,
|
|
ProductionDate = DateTime.Now,
|
|
Line = _loginResult.LineNo,
|
|
LotNo = _loginResult.LotNo,
|
|
JigNo = _loginResult.JigNo
|
|
};
|
|
}
|
|
|
|
private HousingAssemblyRecord CreateMeasurementRecord(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,
|
|
PtVol1 = _lastMeasuredVoltage ?? ParseDecimal(VOutputTextBox.Text),
|
|
PtCurrent1 = ParseDecimal(AOutputTextBox.Text),
|
|
Result = resultCode,
|
|
Spare1 = null,
|
|
Spare2 = null,
|
|
Spare3 = null,
|
|
Spare4 = null
|
|
};
|
|
}
|
|
|
|
private void ApplyInspectionSettingsToTextBoxes()
|
|
{
|
|
VMinTextBox.Text = FormatDecimal(_inspectionSettings.VMin);
|
|
VMaxTextBox.Text = FormatDecimal(_inspectionSettings.VMax);
|
|
AMinTextBox.Text = FormatCurrentLimit(_inspectionSettings.AMin);
|
|
AMaxTextBox.Text = FormatCurrentLimit(_inspectionSettings.AMax);
|
|
}
|
|
|
|
private bool TryApplyInspectionSettingsFromTextBoxes()
|
|
{
|
|
if (!TryReadRangeValue(VMinTextBox, "V 최소값", out var vMin) ||
|
|
!TryReadRangeValue(VMaxTextBox, "V 최대값", out var vMax) ||
|
|
!TryReadRangeValue(AMinTextBox, "mA 최소값", out var aMinMilliamp) ||
|
|
!TryReadRangeValue(AMaxTextBox, "mA 최대값", out var aMaxMilliamp))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var aMin = MilliampsToAmps(aMinMilliamp);
|
|
var aMax = MilliampsToAmps(aMaxMilliamp);
|
|
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 FormatCurrent(decimal value)
|
|
{
|
|
return AmpsToMilliamps(value).ToString("0.000", CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
private static string FormatCurrentLimit(decimal value)
|
|
{
|
|
return AmpsToMilliamps(value).ToString("0.00", CultureInfo.InvariantCulture);
|
|
}
|
|
|
|
private static decimal AmpsToMilliamps(decimal amps)
|
|
{
|
|
return amps * MilliampsPerAmp;
|
|
}
|
|
|
|
private static decimal MilliampsToAmps(decimal milliamps)
|
|
{
|
|
return milliamps / MilliampsPerAmp;
|
|
}
|
|
|
|
private static IReadOnlyCollection<string> GetReservedBarcodeScannerPortNames(string hardwareIniPath)
|
|
{
|
|
var reservedPortNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
var boardSettings = BoardHardwareSettings.Load(hardwareIniPath);
|
|
AddReservedPortName(reservedPortNames, boardSettings.PortName);
|
|
|
|
var equipmentSettings = EquipmentMeasurementSettings.Load(hardwareIniPath);
|
|
AddReservedScpiChannelPortName(reservedPortNames, equipmentSettings.Voltage);
|
|
AddReservedScpiChannelPortName(reservedPortNames, equipmentSettings.Current);
|
|
|
|
return reservedPortNames;
|
|
}
|
|
|
|
private static void AddReservedScpiChannelPortName(ISet<string> reservedPortNames, ScpiChannelSettings channel)
|
|
{
|
|
if (string.Equals(channel.Connection, "Serial", StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(channel.Connection, "COM", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
AddReservedPortName(reservedPortNames, channel.PortName);
|
|
}
|
|
}
|
|
|
|
private static void AddReservedPortName(ISet<string> reservedPortNames, string portName)
|
|
{
|
|
if (BarcodeScannerSettings.IsAutoPort(portName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
reservedPortNames.Add(portName.Trim());
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|