Files
Z173/ViewModels/MainViewModel.cs

498 lines
19 KiB
C#
Raw Normal View History

2026-06-13 14:16:34 +08:00
using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using AciTester.Models;
using AciTester.Services;
namespace AciTester.ViewModels
{
public partial class MainViewModel : ObservableObject
{
public readonly IPlcService _plcService;
private readonly IReportService _reportService;
public readonly PlcConfiguration _config;
private CancellationTokenSource _testCts;
2026-06-16 11:53:02 +08:00
private bool _alarmShownLow = false;
private bool _alarmShownHigh = false;
2026-06-16 20:50:05 +08:00
public IAsyncRelayCommand OpenValveCommand { get; }
2026-06-13 14:16:34 +08:00
[ObservableProperty]
private bool isConnected;
[ObservableProperty]
private string connectionStatus = "未连接";
[ObservableProperty]
private float currentFlow;
[ObservableProperty]
private bool isPumpRunning;
[ObservableProperty]
private bool isTesting;
2026-06-16 20:50:05 +08:00
[ObservableProperty]
private bool canStopTest; // 控制“停止测试”按钮的启用状态
2026-06-13 14:16:34 +08:00
[ObservableProperty]
2026-06-16 11:53:02 +08:00
private int sampleTimeSeconds = 60;
2026-06-13 14:16:34 +08:00
[ObservableProperty]
private int remainingSeconds;
[ObservableProperty]
private ObservableCollection<StageData> stages;
[ObservableProperty]
private TestResult currentResult;
[ObservableProperty]
private RealTimeData realTime;
[ObservableProperty]
private CalibrationConfig calibration;
2026-06-16 11:53:02 +08:00
[ObservableProperty]
private bool constantTempStartEnabled = true; // 是否允许恒温启动除霜时为false
2026-06-16 20:50:05 +08:00
public IAsyncRelayCommand StartPumpCommand { get; }
public IAsyncRelayCommand StopPumpCommand { get; }
// 阀门/泵状态(用于界面显示)
[ObservableProperty]
private bool valveStatus; // true=开启, false=关闭
[ObservableProperty]
private bool pumpStatus; // true=运行, false=停止
public IAsyncRelayCommand CloseValveCommand { get; }
2026-06-13 14:16:34 +08:00
public MainViewModel()
{
_config = new PlcConfiguration();
_plcService = new ModbusTcpPlcService(_config);
_reportService = new ExcelReportService();
Stages = new ObservableCollection<StageData>
{
new StageData { StageName = "Stage 0", CutoffDiameter = 9.0 },
new StageData { StageName = "Stage 1", CutoffDiameter = 5.8 },
new StageData { StageName = "Stage 2", CutoffDiameter = 4.7 },
new StageData { StageName = "Stage 3", CutoffDiameter = 3.3 },
new StageData { StageName = "Stage 4", CutoffDiameter = 2.1 },
new StageData { StageName = "Stage 5", CutoffDiameter = 1.1 },
new StageData { StageName = "Stage 6", CutoffDiameter = 0.7 },
new StageData { StageName = "Stage 7", CutoffDiameter = 0.4 },
new StageData { StageName = "Filter", CutoffDiameter = 0.0 }
};
ConnectCommand = new AsyncRelayCommand(ConnectAsync);
DisconnectCommand = new RelayCommand(Disconnect);
StartTestCommand = new AsyncRelayCommand(StartTestAsync);
2026-06-16 20:50:05 +08:00
StopTestCommand = new AsyncRelayCommand(StopTestAsync, () => IsTesting);
2026-06-13 14:16:34 +08:00
CalculateCommand = new RelayCommand(CalculateResult);
ExportReportCommand = new AsyncRelayCommand(ExportReportAsync);
2026-06-16 11:53:02 +08:00
// 写入命令
WriteConstantTempStartCommand = new AsyncRelayCommand<bool>(WriteConstantTempStartAsync);
WriteDefrostStartCommand = new AsyncRelayCommand<bool>(WriteDefrostStartAsync);
WriteDefrostTempSetCommand = new AsyncRelayCommand<float>(WriteDefrostTempSetAsync);
WriteDefrostTimeSetCommand = new AsyncRelayCommand<int>(WriteDefrostTimeSetAsync);
2026-06-13 14:16:34 +08:00
RealTime = new RealTimeData();
Calibration = new CalibrationConfig();
2026-06-16 20:50:05 +08:00
OpenValveCommand = new AsyncRelayCommand(async () =>
{
await _plcService.WriteCoilAsync(_config.ValveCoil, true);
ValveStatus = true;
});
CloseValveCommand = new AsyncRelayCommand(async () =>
{
await _plcService.WriteCoilAsync(_config.ValveCoil, false);
ValveStatus = false;
});
StartPumpCommand = new AsyncRelayCommand(async () =>
{
await _plcService.WriteCoilAsync(_config.PumpCoil, true);
PumpStatus = true;
});
StopPumpCommand = new AsyncRelayCommand(async () =>
{
await _plcService.WriteCoilAsync(_config.PumpCoil, false);
PumpStatus = false;
});
2026-06-13 14:16:34 +08:00
2026-06-16 11:53:02 +08:00
// 监听属性变化,当除霜启动时更新恒温启动使能
RealTime.PropertyChanged += async (s, e) =>
{
if (e.PropertyName == nameof(RealTime.DefrostTempSet))
{
await WriteDefrostTempSetAsync(RealTime.DefrostTempSet);
}
else if (e.PropertyName == nameof(RealTime.DefrostTimeSet))
{
await WriteDefrostTimeSetAsync(RealTime.DefrostTimeSet);
}
};
2026-06-13 14:16:34 +08:00
2026-06-16 20:50:05 +08:00
}
public IAsyncRelayCommand StopTestCommand { get; }
2026-06-13 14:16:34 +08:00
public IAsyncRelayCommand ConnectCommand { get; }
public IRelayCommand DisconnectCommand { get; }
public IAsyncRelayCommand StartTestCommand { get; }
public IRelayCommand CalculateCommand { get; }
public IAsyncRelayCommand ExportReportCommand { get; }
2026-06-16 11:53:02 +08:00
public IAsyncRelayCommand<bool> WriteConstantTempStartCommand { get; }
public IAsyncRelayCommand<bool> WriteDefrostStartCommand { get; }
public IAsyncRelayCommand<float> WriteDefrostTempSetCommand { get; }
public IAsyncRelayCommand<int> WriteDefrostTimeSetCommand { get; }
2026-06-13 14:16:34 +08:00
2026-06-16 20:50:05 +08:00
private async Task StopTestAsync()
{
if (!IsTesting) return;
try
{
// 停止真空泵
await _plcService.WriteCoilAsync(_config.PumpCoil, false);
IsPumpRunning = false;
}
catch (Exception ex)
{
MessageBox.Show($"停止泵失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
finally
{
// 重置状态
IsTesting = false;
CanStopTest = false;
_testCts?.Cancel(); // 取消倒计时
RemainingSeconds = 0;
// 更新按钮状态
StopTestCommand.NotifyCanExecuteChanged();
}
//MessageBox.Show("测试已手动停止,请进行称重并录入数据。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
2026-06-13 14:16:34 +08:00
private async Task ConnectAsync()
{
try
{
await _plcService.EnsureConnectedAsync();
IsConnected = true;
ConnectionStatus = "已连接";
_ = Task.Run(ReadFlowLoop);
2026-06-16 11:53:02 +08:00
_ = Task.Run(ReadRealTimeLoop);
_ = Task.Run(ReadAlarmLoop); // 报警循环
_ = Task.Run(ReadDefrostStatusLoop); // 除霜相关状态
2026-06-13 14:16:34 +08:00
}
catch (Exception ex)
{
MessageBox.Show($"连接失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
IsConnected = false;
ConnectionStatus = "连接失败";
}
}
private void Disconnect()
{
_plcService.Dispose();
IsConnected = false;
ConnectionStatus = "未连接";
}
private async Task ReadFlowLoop()
{
while (IsConnected)
{
try
{
var flow = await _plcService.ReadFloatAsync(_config.FlowRegister);
Application.Current.Dispatcher.Invoke(() => CurrentFlow = flow);
await Task.Delay(1000);
}
catch { break; }
}
}
2026-06-16 11:53:02 +08:00
private async Task ReadRealTimeLoop()
{
while (IsConnected)
{
try
{
var rawFlow = await _plcService.ReadFloatAsync(_config.FlowRegister);
var calibrated = rawFlow * Calibration.FlowCalibration;
var temp = await _plcService.ReadFloatAsync(_config.TemperatureReg);
var calibratedTemp = temp * Calibration.TemperatureCalibration;
var pumpPres = await _plcService.ReadFloatAsync(_config.PumpPressureReg);
var calibratedPump = pumpPres * Calibration.PumpPressureCalibration;
var impPres = await _plcService.ReadFloatAsync(_config.ImpactorPressureReg);
var calibratedImp = impPres * Calibration.ImpactorPressureCalibration;
Application.Current.Dispatcher.Invoke(() =>
{
RealTime.RawFlow = rawFlow;
RealTime.CalibratedFlow = calibrated;
RealTime.Temperature = calibratedTemp;
RealTime.PumpPressure = calibratedPump;
RealTime.ImpactorPressure = calibratedImp;
RealTime.DifferentialPressure = calibratedImp - calibratedPump;
});
2026-06-16 20:50:05 +08:00
// 在 ReadRealTimeLoop 中添加
var valve = await _plcService.ReadCoilAsync(_config.ValveCoil);
var pump = await _plcService.ReadCoilAsync(_config.PumpCoil);
Application.Current.Dispatcher.Invoke(() =>
{
ValveStatus = valve;
PumpStatus = pump;
});
2026-06-16 11:53:02 +08:00
if (calibrated < Calibration.FlowLowLimit || calibrated > Calibration.FlowHighLimit)
{
2026-06-16 20:50:05 +08:00
//Application.Current.Dispatcher.Invoke(() =>
// MessageBox.Show($"流量异常: {calibrated:F2} L/min", "警告", MessageBoxButton.OK, MessageBoxImage.Warning));
2026-06-16 11:53:02 +08:00
}
}
catch { }
await Task.Delay(1000);
}
}
private async Task ReadAlarmLoop()
{
while (IsConnected)
{
try
{
var lowAlarm = await _plcService.ReadCoilAsync(_config.AcLowPressureAlarmCoil);
var highAlarm = await _plcService.ReadCoilAsync(_config.AcHighPressureAlarmCoil);
Application.Current.Dispatcher.Invoke(() =>
{
RealTime.AcLowPressureAlarm = lowAlarm;
RealTime.AcHighPressureAlarm = highAlarm;
});
if (lowAlarm && !_alarmShownLow)
{
_alarmShownLow = true;
Application.Current.Dispatcher.Invoke(() =>
{
var result = MessageBox.Show("空调低压报警,请检查空调情况", "报警", MessageBoxButton.OK, MessageBoxImage.Error);
if (result == MessageBoxResult.OK)
{
_ = _plcService.WriteCoilAsync(_config.AcLowPressureAlarmCoil, false);
_alarmShownLow = false;
}
});
}
if (highAlarm && !_alarmShownHigh)
{
_alarmShownHigh = true;
Application.Current.Dispatcher.Invoke(() =>
{
var result = MessageBox.Show("空调高压报警,请检查空调情况", "报警", MessageBoxButton.OK, MessageBoxImage.Error);
if (result == MessageBoxResult.OK)
{
_ = _plcService.WriteCoilAsync(_config.AcHighPressureAlarmCoil, false);
_alarmShownHigh = false;
}
});
}
}
catch { }
await Task.Delay(500);
}
}
private async Task ReadDefrostStatusLoop()
{
while (IsConnected)
{
try
{
// 读取 D50 空调启动倒计时
var countdown = await _plcService.ReadInt32Async(_config.AcStartupCountdownReg);
// 读取 D302 除霜温度设置
var defrostTemp = await _plcService.ReadFloatAsync(_config.DefrostTempSetReg);
// 读取 D310 除霜时间设置
var defrostTime = await _plcService.ReadInt32Async(_config.DefrostTimeSetReg);
// 读取 D42 D40 除霜计时
var min = await _plcService.ReadInt32Async(_config.DefrostMinuteReg);
var sec = await _plcService.ReadInt32Async(_config.DefrostSecondReg);
// 读取 M4 M19
var constTemp = await _plcService.ReadCoilAsync(_config.ConstantTempStartCoil);
var defrostStart = await _plcService.ReadCoilAsync(_config.DefrostStartCoil);
Application.Current.Dispatcher.Invoke(() =>
{
RealTime.AcStartupCountdown = countdown;
RealTime.DefrostTempSet = defrostTemp;
RealTime.DefrostTimeSet = defrostTime;
RealTime.DefrostMinute = min;
RealTime.DefrostSecond = sec;
RealTime.ConstantTempStart = constTemp;
RealTime.DefrostStart = defrostStart;
});
}
catch { }
await Task.Delay(1000);
}
}
private async Task WriteConstantTempStartAsync(bool value)
{
if (!IsConnected) return;
if (RealTime.DefrostStart)
{
MessageBox.Show("设备正在除霜,不能进行恒温操作", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
await _plcService.WriteCoilAsync(_config.ConstantTempStartCoil, value);
}
private async Task WriteDefrostStartAsync(bool value)
{
if (!IsConnected) return;
await _plcService.WriteCoilAsync(_config.DefrostStartCoil, value);
if (value)
{
// 立即更新界面使能
ConstantTempStartEnabled = false;
}
else
{
ConstantTempStartEnabled = true;
}
}
private async Task WriteDefrostTempSetAsync(float value)
{
if (!IsConnected) return;
await _plcService.WriteMultipleRegistersAsync(_config.DefrostTempSetReg, value);
}
private async Task WriteDefrostTimeSetAsync(int value)
{
if (!IsConnected) return;
await _plcService.WriteInt32Async(_config.DefrostTimeSetReg, value);
}
2026-06-13 14:16:34 +08:00
private async Task StartTestAsync()
{
if (!IsConnected)
{
await ConnectAsync();
if (!IsConnected) return;
}
if (IsTesting) return;
IsTesting = true;
_testCts = new CancellationTokenSource();
2026-06-16 20:50:05 +08:00
//StopTestCommand.NotifyCanExecuteChanged();
2026-06-13 14:16:34 +08:00
try
{
await _plcService.WriteCoilAsync(_config.PumpCoil, true);
IsPumpRunning = true;
RemainingSeconds = SampleTimeSeconds;
for (int i = 0; i < SampleTimeSeconds; i++)
{
if (_testCts.Token.IsCancellationRequested) break;
await Task.Delay(1000);
RemainingSeconds--;
}
await _plcService.WriteCoilAsync(_config.PumpCoil, false);
IsPumpRunning = false;
MessageBox.Show($"采样完成!\n实际采样时间: {SampleTimeSeconds} 秒\n请进行称重并录入数据。",
"提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show($"测试异常: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
try { await _plcService.WriteCoilAsync(_config.PumpCoil, false); } catch { }
IsPumpRunning = false;
}
finally
{
IsTesting = false;
_testCts?.Dispose();
}
}
private void CalculateResult()
{
double totalMass = 0;
foreach (var stage in Stages)
totalMass += stage.NetWeight;
if (totalMass <= 0)
{
MessageBox.Show("总质量为零,无法计算", "警告", MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
double fineMass = 0;
foreach (var stage in Stages)
{
if (stage.CutoffDiameter <= 5.0 && stage.CutoffDiameter > 0)
fineMass += stage.NetWeight;
}
2026-06-16 11:53:02 +08:00
fineMass += Stages[8].NetWeight;
2026-06-13 14:16:34 +08:00
2026-06-16 11:53:02 +08:00
double fpd = fineMass * 1000;
2026-06-13 14:16:34 +08:00
double fpf = (fineMass / totalMass) * 100;
CurrentResult = new TestResult
{
TestTime = DateTime.Now,
TotalMass = totalMass,
FineParticleDose = fpd,
FineParticleFraction = fpf,
Stages = Stages.ToList(),
FlowRate = CurrentFlow,
2026-06-16 11:53:02 +08:00
Temperature = RealTime.Temperature,
DifferentialPressure = RealTime.DifferentialPressure
2026-06-13 14:16:34 +08:00
};
MessageBox.Show($"计算完成\n总质量: {totalMass:F4} g\n微细粒子剂量: {fpd:F2} mg\n微细粒子分数: {fpf:F2}%",
"计算结果", MessageBoxButton.OK, MessageBoxImage.Information);
}
private async Task ExportReportAsync()
{
if (CurrentResult == null)
{
MessageBox.Show("请先计算测试结果", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var saveDialog = new Microsoft.Win32.SaveFileDialog
{
Filter = "Excel文件|*.xlsx",
FileName = $"ACI_Test_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx"
};
if (saveDialog.ShowDialog() == true)
{
await _reportService.GenerateReportAsync(CurrentResult, saveDialog.FileName);
MessageBox.Show("报告已保存", "完成", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
}
}