Files
Z173/ViewModels/MainViewModel.cs
2026-06-16 20:50:05 +08:00

498 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
private bool _alarmShownLow = false;
private bool _alarmShownHigh = false;
public IAsyncRelayCommand OpenValveCommand { get; }
[ObservableProperty]
private bool isConnected;
[ObservableProperty]
private string connectionStatus = "未连接";
[ObservableProperty]
private float currentFlow;
[ObservableProperty]
private bool isPumpRunning;
[ObservableProperty]
private bool isTesting;
[ObservableProperty]
private bool canStopTest; // 控制“停止测试”按钮的启用状态
[ObservableProperty]
private int sampleTimeSeconds = 60;
[ObservableProperty]
private int remainingSeconds;
[ObservableProperty]
private ObservableCollection<StageData> stages;
[ObservableProperty]
private TestResult currentResult;
[ObservableProperty]
private RealTimeData realTime;
[ObservableProperty]
private CalibrationConfig calibration;
[ObservableProperty]
private bool constantTempStartEnabled = true; // 是否允许恒温启动除霜时为false
public IAsyncRelayCommand StartPumpCommand { get; }
public IAsyncRelayCommand StopPumpCommand { get; }
// 阀门/泵状态(用于界面显示)
[ObservableProperty]
private bool valveStatus; // true=开启, false=关闭
[ObservableProperty]
private bool pumpStatus; // true=运行, false=停止
public IAsyncRelayCommand CloseValveCommand { get; }
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);
StopTestCommand = new AsyncRelayCommand(StopTestAsync, () => IsTesting);
CalculateCommand = new RelayCommand(CalculateResult);
ExportReportCommand = new AsyncRelayCommand(ExportReportAsync);
// 写入命令
WriteConstantTempStartCommand = new AsyncRelayCommand<bool>(WriteConstantTempStartAsync);
WriteDefrostStartCommand = new AsyncRelayCommand<bool>(WriteDefrostStartAsync);
WriteDefrostTempSetCommand = new AsyncRelayCommand<float>(WriteDefrostTempSetAsync);
WriteDefrostTimeSetCommand = new AsyncRelayCommand<int>(WriteDefrostTimeSetAsync);
RealTime = new RealTimeData();
Calibration = new CalibrationConfig();
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;
});
// 监听属性变化,当除霜启动时更新恒温启动使能
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);
}
};
}
public IAsyncRelayCommand StopTestCommand { get; }
public IAsyncRelayCommand ConnectCommand { get; }
public IRelayCommand DisconnectCommand { get; }
public IAsyncRelayCommand StartTestCommand { get; }
public IRelayCommand CalculateCommand { get; }
public IAsyncRelayCommand ExportReportCommand { get; }
public IAsyncRelayCommand<bool> WriteConstantTempStartCommand { get; }
public IAsyncRelayCommand<bool> WriteDefrostStartCommand { get; }
public IAsyncRelayCommand<float> WriteDefrostTempSetCommand { get; }
public IAsyncRelayCommand<int> WriteDefrostTimeSetCommand { get; }
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);
}
private async Task ConnectAsync()
{
try
{
await _plcService.EnsureConnectedAsync();
IsConnected = true;
ConnectionStatus = "已连接";
_ = Task.Run(ReadFlowLoop);
_ = Task.Run(ReadRealTimeLoop);
_ = Task.Run(ReadAlarmLoop); // 报警循环
_ = Task.Run(ReadDefrostStatusLoop); // 除霜相关状态
}
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; }
}
}
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;
});
// 在 ReadRealTimeLoop 中添加
var valve = await _plcService.ReadCoilAsync(_config.ValveCoil);
var pump = await _plcService.ReadCoilAsync(_config.PumpCoil);
Application.Current.Dispatcher.Invoke(() =>
{
ValveStatus = valve;
PumpStatus = pump;
});
if (calibrated < Calibration.FlowLowLimit || calibrated > Calibration.FlowHighLimit)
{
//Application.Current.Dispatcher.Invoke(() =>
// MessageBox.Show($"流量异常: {calibrated:F2} L/min", "警告", MessageBoxButton.OK, MessageBoxImage.Warning));
}
}
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);
}
private async Task StartTestAsync()
{
if (!IsConnected)
{
await ConnectAsync();
if (!IsConnected) return;
}
if (IsTesting) return;
IsTesting = true;
_testCts = new CancellationTokenSource();
//StopTestCommand.NotifyCanExecuteChanged();
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;
}
fineMass += Stages[8].NetWeight;
double fpd = fineMass * 1000;
double fpf = (fineMass / totalMass) * 100;
CurrentResult = new TestResult
{
TestTime = DateTime.Now,
TotalMass = totalMass,
FineParticleDose = fpd,
FineParticleFraction = fpf,
Stages = Stages.ToList(),
FlowRate = CurrentFlow,
Temperature = RealTime.Temperature,
DifferentialPressure = RealTime.DifferentialPressure
};
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);
}
}
}
}