리크 테스트 gui
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.
 
 
 
 
 
 

449 lines
16 KiB

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using leak_test_project.Infrastructure;
using leak_test_project.Models;
using leak_test_project.Services;
using leak_test_project.Utils;
using leak_test_project.ViewModels.Core;
namespace leak_test_project.ViewModels
{
/// <summary>
/// Home 화면의 비즈니스 로직을 담당하는 ViewModel.
/// 좌/우 채널 통신 관리, 측정값 표시, 판정 로직, 자동 시험 프로세스를 포함.
/// </summary>
public class HomeViewModel : ObservableObject, IDisposable
{
private SentinelC28Service _sentinelService;
private SerialProvider _sentinelSerial;
private readonly Dispatcher _dispatcher;
// 자동 시험 프로세스 관련
private IDioBoard _dioBoard;
private IIdSensorService _leftZmdi;
private IIdSensorService _rightZmdi;
private SerialProvider _leftZmdiSerial;
private SerialProvider _rightZmdiSerial;
private TestProcessService _testProcess;
#region Left Channel Properties
private string _leftValue = "";
public string LeftValue { get => _leftValue; set => SetProperty(ref _leftValue, value); }
private string _leftJudgment = "";
public string LeftJudgment { get => _leftJudgment; set => SetProperty(ref _leftJudgment, value); }
private bool _isLeftOk;
public bool IsLeftOk { get => _isLeftOk; set => SetProperty(ref _isLeftOk, value); }
private string _leftStatus = "";
public string LeftStatus { get => _leftStatus; set => SetProperty(ref _leftStatus, value); }
private string _leftStartTime = "";
public string LeftStartTime { get => _leftStartTime; set => SetProperty(ref _leftStartTime, value); }
private string _leftId = "";
public string LeftId
{
get => _leftId;
set
{
if (SetProperty(ref _leftId, value))
OnPropertyChanged(nameof(LeftIdCombined));
}
}
private string _leftLowId = "";
public string LeftLowId
{
get => _leftLowId;
set
{
if (SetProperty(ref _leftLowId, value))
OnPropertyChanged(nameof(LeftIdCombined));
}
}
public string LeftIdCombined => $"{LeftId} / {LeftLowId}";
private string _leftDate = "";
public string LeftDate { get => _leftDate; set => SetProperty(ref _leftDate, value); }
private string _leftSerial_ = "";
public string LeftSerialNo { get => _leftSerial_; set => SetProperty(ref _leftSerial_, value); }
private string _leftMcLine = "";
public string LeftMcLine { get => _leftMcLine; set => SetProperty(ref _leftMcLine, value); }
private string _leftItem = "";
public string LeftItem { get => _leftItem; set => SetProperty(ref _leftItem, value); }
private string _leftError = "";
public string LeftError { get => _leftError; set => SetProperty(ref _leftError, value); }
#endregion
#region Right Channel Properties
private string _rightValue = "";
public string RightValue { get => _rightValue; set => SetProperty(ref _rightValue, value); }
private string _rightJudgment = "";
public string RightJudgment { get => _rightJudgment; set => SetProperty(ref _rightJudgment, value); }
private bool _isRightOk;
public bool IsRightOk { get => _isRightOk; set => SetProperty(ref _isRightOk, value); }
private string _rightStatus = "";
public string RightStatus { get => _rightStatus; set => SetProperty(ref _rightStatus, value); }
private string _rightStartTime = "";
public string RightStartTime { get => _rightStartTime; set => SetProperty(ref _rightStartTime, value); }
private string _rightId = "";
public string RightId
{
get => _rightId;
set
{
if (SetProperty(ref _rightId, value))
OnPropertyChanged(nameof(RightIdCombined));
}
}
private string _rightLowId = "";
public string RightLowId
{
get => _rightLowId;
set
{
if (SetProperty(ref _rightLowId, value))
OnPropertyChanged(nameof(RightIdCombined));
}
}
public string RightIdCombined => $"{RightId} / {RightLowId}";
private string _rightDate = "";
public string RightDate { get => _rightDate; set => SetProperty(ref _rightDate, value); }
private string _rightSerial_ = "";
public string RightSerialNo { get => _rightSerial_; set => SetProperty(ref _rightSerial_, value); }
private string _rightMcLine = "";
public string RightMcLine { get => _rightMcLine; set => SetProperty(ref _rightMcLine, value); }
private string _rightItem = "";
public string RightItem { get => _rightItem; set => SetProperty(ref _rightItem, value); }
private string _rightError = "";
public string RightError { get => _rightError; set => SetProperty(ref _rightError, value); }
#endregion
#region Spec Properties
private string _specUL = "";
public string SpecUL { get => _specUL; set => SetProperty(ref _specUL, value); }
private string _specLL = "";
public string SpecLL { get => _specLL; set => SetProperty(ref _specLL, value); }
#endregion
public HomeViewModel(IDioBoard dioBoard)
{
_dioBoard = dioBoard;
_dispatcher = Dispatcher.CurrentDispatcher;
var config = ConfigManager.Current;
UpdateSpecFromConfig(config);
InitializeCommunication(config);
InitializeTestProcess(config);
ConfigManager.ConfigChanged += OnConfigChanged;
}
private void OnConfigChanged(object sender, EventArgs e)
{
_dispatcher.Invoke(() => {
var newConfig = ConfigManager.Current;
UpdateSpecFromConfig(newConfig);
ApplyConfig();
});
}
private void UpdateSpecFromConfig(AppConfig config)
{
SpecUL = config.SpecUL.ToString("F2");
SpecLL = config.SpecLL.ToString("F2");
}
public void ApplyConfig()
{
CleanupAll();
// 통신 재시작 전 기존 오류 및 상태 메시지 초기화
LeftError = "";
RightError = "";
LeftStatus = "통신 대기 중";
RightStatus = "통신 대기 중";
var config = ConfigManager.Current;
InitializeCommunication(config);
InitializeTestProcess(config);
}
private void CleanupAll()
{
// 1. 시험 프로세스 정지 (스레드 종료 및 이벤트 해제)
_testProcess?.Dispose();
_testProcess = null;
// 2. ZMDI 시리얼 포트 및 서비스 해제
_leftZmdi?.Dispose();
_rightZmdi?.Dispose();
_leftZmdiSerial?.Dispose();
_rightZmdiSerial?.Dispose();
_leftZmdi = null;
_rightZmdi = null;
_leftZmdiSerial = null;
_rightZmdiSerial = null;
// 3. Sentinel C28 해제
_sentinelService?.Disconnect();
_sentinelSerial?.Dispose();
_sentinelService = null;
_sentinelSerial = null;
}
private void InitializeCommunication(AppConfig config)
{
// Sentinel C28 (Leak Sensor) - 단일 포트 사용
_sentinelSerial = new SerialProvider(config.SensorPort, config.SensorBaudRate);
_sentinelService = new SentinelC28Service(_sentinelSerial);
_sentinelService.RawDataReceived += (s, data) => {
System.Diagnostics.Debug.WriteLine($"[SENTINEL RAW] {data}");
};
_sentinelService.OnStreamingParsed += (s, data) => UpdateMeasurement(data);
if (!_sentinelService.Connect())
{
string msg = $"Sentinel C28 포트 연결 실패 ({config.SensorPort})";
LeftError = msg;
RightError = msg;
}
}
private void InitializeTestProcess(AppConfig config)
{
// DIO 보드 초기화 (MainViewModel에서 생성된 보드 사용)
if (_dioBoard == null) return;
// DIO 보드 에러 구독
_dioBoard.ErrorOccurred += (s, msg) => _dispatcher.Invoke(() => {
LeftError = msg;
RightError = msg;
AppendLog(true, $"[DIO Board Error] {msg}");
});
// ID 센서 서비스 (4251 보드 단일 통신)
_leftZmdiSerial = new SerialProvider(config.Board4251Port, config.Board4251BaudRate);
var sharedService = new Board4251Service(_leftZmdiSerial) { TimeoutMs = config.Board4251Timeout };
_leftZmdi = new Board4251SensorService(sharedService, 0);
_rightZmdi = new Board4251SensorService(sharedService, 1);
string sensorName = "4251 보드";
string logPrefix = "[4251 Error]";
if (!_leftZmdi.Connect())
{
string msg = $"{sensorName} 포트 연결 실패 ({config.Board4251Port})";
LeftError = string.IsNullOrEmpty(LeftError) ? msg : $"{LeftError}\n{msg}";
}
if (!_rightZmdi.Connect())
{
string msg = $"{sensorName} 포트 연결 실패 ({config.Board4251Port})";
RightError = string.IsNullOrEmpty(RightError) ? msg : $"{RightError}\n{msg}";
}
_leftZmdi.ProgressMessage += (s, msg) => _dispatcher.Invoke(() => LeftStatus = msg);
_leftZmdi.ErrorMessage += (s, msg) => _dispatcher.Invoke(() => {
LeftError = msg;
AppendLog(true, $"{logPrefix} {msg}");
});
_rightZmdi.ProgressMessage += (s, msg) => _dispatcher.Invoke(() => RightStatus = msg);
_rightZmdi.ErrorMessage += (s, msg) => _dispatcher.Invoke(() => {
RightError = msg;
AppendLog(false, $"{logPrefix} {msg}");
});
// 자동 시험 프로세스
_testProcess = new TestProcessService(_dioBoard, _leftZmdi, _rightZmdi, _sentinelService);
_testProcess.ProgressChanged += (s, e) => _dispatcher.Invoke(() =>
{
if (e.TestIndex == 0) LeftStatus = e.Message;
else RightStatus = e.Message;
});
_testProcess.ErrorOccurred += (s, e) => _dispatcher.Invoke(() =>
{
if (e.TestIndex == 0)
{
LeftStatus = "오류 발생";
LeftError = e.Message;
AppendLog(true, $"[ERROR] {e.Message}");
}
else
{
RightStatus = "오류 발생";
RightError = e.Message;
AppendLog(false, $"[ERROR] {e.Message}");
}
});
_testProcess.ResultClearRequested += (s, testIndex) => _dispatcher.Invoke(() =>
{
if (testIndex == 0) ClearLeftResult();
else ClearRightResult();
});
_testProcess.SensorReadComplete += (s, args) => _dispatcher.Invoke(() =>
{
var d = args.Data;
if (args.TestIndex == 0)
{
LeftId = d.ID;
LeftLowId = d.LowID;
LeftDate = d.Year > 0 ? $"{d.Year}/{d.Month}/{d.Day}" : "";
LeftSerialNo = d.Serial;
LeftMcLine = d.McLine;
LeftItem = d.Item;
}
else
{
RightId = d.ID;
RightLowId = d.LowID;
RightDate = d.Year > 0 ? $"{d.Year}/{d.Month}/{d.Day}" : "";
RightSerialNo = d.Serial;
RightMcLine = d.McLine;
RightItem = d.Item;
}
});
_testProcess.TestCompleted += (s, e) => _dispatcher.Invoke(() =>
{
if (e.TestIndex == 0)
{
LeftValue = e.MeasuredValue;
LeftJudgment = e.Judgment;
IsLeftOk = e.Judgment == "OK";
LeftStatus = "시험 완료";
}
else
{
RightValue = e.MeasuredValue;
RightJudgment = e.Judgment;
IsRightOk = e.Judgment == "OK";
RightStatus = "시험 완료";
}
// SPEC 교차 검증 불일치 경고
if (e.SpecMismatch)
{
string side = e.TestIndex == 0 ? "LEFT" : "RIGHT";
string msg = $"SPEC 불일치 - 프로그램: {e.Judgment}, 센서: {e.SensorJudgment}";
if (e.TestIndex == 0) LeftError = msg;
else RightError = msg;
MessageBox.Show(
"프로그램 스팩과 센서의 스팩이 서로 맞지 않습니다.",
"SPEC 교차 검증 경고",
MessageBoxButton.OK,
MessageBoxImage.Warning);
}
});
_testProcess.Start();
}
private void ClearLeftResult()
{
LeftValue = ""; LeftJudgment = ""; IsLeftOk = false;
LeftStartTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
LeftId = ""; LeftLowId = ""; LeftDate = ""; LeftSerialNo = "";
LeftMcLine = ""; LeftItem = "";
LeftError = "";
}
private void ClearRightResult()
{
RightValue = ""; RightJudgment = ""; IsRightOk = false;
RightStartTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
RightId = ""; RightLowId = ""; RightDate = ""; RightSerialNo = "";
RightMcLine = ""; RightItem = "";
RightError = "";
}
private void UpdateMeasurement(ParsedData data)
{
_dispatcher.Invoke(() => {
// ChannelNo(C01=LEFT, C02=RIGHT)에 따라 데이터 라우팅
if (data.ChannelNo == "C01" || data.ChannelNo == "1")
LeftValue = data.MeasuredValue.ToString("F3");
else if (data.ChannelNo == "C02" || data.ChannelNo == "2")
RightValue = data.MeasuredValue.ToString("F3");
else
{
// 채널 정보가 없으면 양쪽 모두 갱신
LeftValue = data.MeasuredValue.ToString("F3");
RightValue = data.MeasuredValue.ToString("F3");
}
});
}
public async System.Threading.Tasks.Task TestReadIdAsync(int testIndex)
{
if (_testProcess != null)
{
await _testProcess.ExecuteSensorTestAsync(testIndex);
}
}
private const int MaxLogLines = 500;
private void AppendLog(bool isLeft, string message)
{
// 이 기능은 이제 Status/Error 필드로 대체되거나 파일 로그로 대체됨
// 현재는 UI에서 제거되었으므로 Debug 출력만 남김
System.Diagnostics.Debug.WriteLine($"[LOG][{(isLeft ? "LEFT" : "RIGHT")}] {message}");
_dispatcher.Invoke(() => {
if (message.Contains("ERROR") || message.Contains("Error"))
{
if (isLeft) LeftError = message;
else RightError = message;
}
else
{
if (isLeft) LeftStatus = message;
else RightStatus = message;
}
});
}
public void Dispose()
{
ConfigManager.ConfigChanged -= OnConfigChanged;
CleanupAll();
_dioBoard?.Dispose();
}
}
}