添加项目文件。
This commit is contained in:
92
ViewModels/MainViewModel.cs
Normal file
92
ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace HME_MoistureLossMeter.ViewModels
|
||||
{
|
||||
public partial class MainViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
[ObservableProperty]
|
||||
private object _currentView;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _currentViewName = "测试画面";
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isConnected;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _connectionStatus = "未连接";
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isTesting;
|
||||
|
||||
[ObservableProperty]
|
||||
private DateTime _currentTime = DateTime.Now;
|
||||
|
||||
public TestViewModel TestViewModel { get; }
|
||||
public ManualControlViewModel ManualControlViewModel { get; }
|
||||
public RecordViewModel RecordViewModel { get; }
|
||||
public ReportViewModel ReportViewModel { get; }
|
||||
|
||||
public MainViewModel(IServiceProvider serviceProvider,
|
||||
TestViewModel testViewModel,
|
||||
ManualControlViewModel manualControlViewModel,
|
||||
RecordViewModel recordViewModel,
|
||||
ReportViewModel reportViewModel)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
TestViewModel = testViewModel;
|
||||
ManualControlViewModel = manualControlViewModel;
|
||||
RecordViewModel = recordViewModel;
|
||||
ReportViewModel = reportViewModel;
|
||||
|
||||
CurrentView = TestViewModel;
|
||||
CurrentViewName = "测试画面";
|
||||
|
||||
var timer = new System.Timers.Timer(1000);
|
||||
timer.Elapsed += (s, e) => CurrentTime = DateTime.Now;
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateToTest()
|
||||
{
|
||||
CurrentView = TestViewModel;
|
||||
CurrentViewName = "测试画面";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateToManual()
|
||||
{
|
||||
CurrentView = ManualControlViewModel;
|
||||
CurrentViewName = "手动控制";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateToRecord()
|
||||
{
|
||||
CurrentView = RecordViewModel;
|
||||
CurrentViewName = "记录画面";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void NavigateToReport()
|
||||
{
|
||||
CurrentView = ReportViewModel;
|
||||
CurrentViewName = "报表";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ExitApplication()
|
||||
{
|
||||
var result = MessageBox.Show("确定要退出程序吗?", "确认退出",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
if (result == MessageBoxResult.Yes)
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
176
ViewModels/ManualControlViewModel.cs
Normal file
176
ViewModels/ManualControlViewModel.cs
Normal file
@@ -0,0 +1,176 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using HME_MoistureLossMeter.Services;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Serilog;
|
||||
|
||||
namespace HME_MoistureLossMeter.ViewModels
|
||||
{
|
||||
public partial class ManualControlViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _manualSpeed;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _tidalCoeff;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _freqCoeff;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isConnected;
|
||||
|
||||
public ManualControlViewModel(IPlcService plcService)
|
||||
{
|
||||
_plcService = plcService;
|
||||
|
||||
// 启动时读取参数
|
||||
Task.Run(async () => await ReadParameters());
|
||||
}
|
||||
|
||||
private async Task ReadParameters()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_plcService.IsConnected)
|
||||
await _plcService.EnsureConnectedAsync();
|
||||
|
||||
ManualSpeed = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_MANUAL_SPEED);
|
||||
TidalCoeff = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_TIDAL_COEFF);
|
||||
FreqCoeff = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_FREQ_COEFF);
|
||||
IsConnected = true;
|
||||
StatusMessage = "参数读取成功";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsConnected = false;
|
||||
StatusMessage = $"读取参数失败: {ex.Message}";
|
||||
Log.Error(ex, "读取手动参数失败");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SaveParameters()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteMultipleRegistersAsync(ModbusTcpPlcService.ADDR_MANUAL_SPEED, ManualSpeed);
|
||||
await _plcService.WriteMultipleRegistersAsync(ModbusTcpPlcService.ADDR_TIDAL_COEFF, TidalCoeff);
|
||||
await _plcService.WriteMultipleRegistersAsync(ModbusTcpPlcService.ADDR_FREQ_COEFF, FreqCoeff);
|
||||
StatusMessage = "参数保存成功";
|
||||
Log.Information("手动参数已保存");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"保存参数失败: {ex.Message}";
|
||||
Log.Error(ex, "保存手动参数失败");
|
||||
MessageBox.Show($"保存参数失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ManualInhale()
|
||||
{
|
||||
await ExecuteCoilCommand(ModbusTcpPlcService.COIL_MANUAL_INHALE, "手动吸气");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ManualExhale()
|
||||
{
|
||||
await ExecuteCoilCommand(ModbusTcpPlcService.COIL_MANUAL_EXHALE, "手动呼气");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MoveLeft()
|
||||
{
|
||||
await ExecuteCoilCommand(ModbusTcpPlcService.COIL_LEFT, "左移");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MoveRight()
|
||||
{
|
||||
await ExecuteCoilCommand(ModbusTcpPlcService.COIL_RIGHT, "右移");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MoveUp()
|
||||
{
|
||||
await ExecuteCoilCommand(ModbusTcpPlcService.COIL_UP, "上升");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MoveDown()
|
||||
{
|
||||
await ExecuteCoilCommand(ModbusTcpPlcService.COIL_DOWN, "下降");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ZeroCalibration()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_ZERO, true);
|
||||
StatusMessage = "校零已触发";
|
||||
Log.Information("校零触发");
|
||||
|
||||
// 等待完成后自动复位
|
||||
await Task.Delay(2000);
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_ZERO, false);
|
||||
StatusMessage = "校零完成";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"校零失败: {ex.Message}";
|
||||
Log.Error(ex, "校零失败");
|
||||
MessageBox.Show($"校零失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteCoilCommand(ushort coilAddress, string commandName)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(coilAddress, true);
|
||||
StatusMessage = $"{commandName}已执行";
|
||||
Log.Information("{Command}执行", commandName);
|
||||
|
||||
// 短暂延迟后自动复位
|
||||
await Task.Delay(200);
|
||||
await _plcService.WriteCoilAsync(coilAddress, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"{commandName}失败: {ex.Message}";
|
||||
Log.Error(ex, "{Command}失败", commandName);
|
||||
MessageBox.Show($"{commandName}失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RefreshParameters()
|
||||
{
|
||||
await ReadParameters();
|
||||
}
|
||||
}
|
||||
}
|
||||
141
ViewModels/RecordViewModel.cs
Normal file
141
ViewModels/RecordViewModel.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using HME_MoistureLossMeter.Models;
|
||||
using HME_MoistureLossMeter.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using Serilog;
|
||||
using System;
|
||||
|
||||
namespace HME_MoistureLossMeter.ViewModels
|
||||
{
|
||||
public partial class RecordViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IMesService _mesService;
|
||||
private readonly DeviceConfiguration _deviceConfig;
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<HistoryRecordModel> _records = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private HistoryRecordModel _selectedRecord;
|
||||
|
||||
public RecordViewModel(IMesService mesService, DeviceConfiguration deviceConfig)
|
||||
{
|
||||
_mesService = mesService;
|
||||
_deviceConfig = deviceConfig;
|
||||
|
||||
// 加载测试数据
|
||||
LoadTestRecords();
|
||||
}
|
||||
|
||||
private void LoadTestRecords()
|
||||
{
|
||||
// 从本地存储或数据库加载历史记录
|
||||
// 这里使用示例数据,实际应从数据库读取
|
||||
Records.Add(new HistoryRecordModel
|
||||
{
|
||||
RecordId = 1,
|
||||
TestTime = DateTime.Now.AddHours(-1).ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
OperatorId = 1001,
|
||||
BatchNo = "BATCH-2024-001",
|
||||
ExperimentId = 1,
|
||||
InitialMass = 50.5f,
|
||||
FinalMass = 48.2f,
|
||||
MoistureLoss = 2.3f
|
||||
});
|
||||
|
||||
Records.Add(new HistoryRecordModel
|
||||
{
|
||||
RecordId = 2,
|
||||
TestTime = DateTime.Now.AddHours(-2).ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
OperatorId = 1002,
|
||||
BatchNo = "BATCH-2024-002",
|
||||
ExperimentId = 2,
|
||||
InitialMass = 45.8f,
|
||||
FinalMass = 44.1f,
|
||||
MoistureLoss = 1.7f
|
||||
});
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SyncToMes()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
StatusMessage = "正在同步历史数据到MES...";
|
||||
|
||||
var historyData = new HistoryDataModel
|
||||
{
|
||||
DeviceId = _deviceConfig.DeviceId,
|
||||
Records = Records.ToList()
|
||||
};
|
||||
|
||||
bool success = await _mesService.SendHistoryDataAsync(historyData);
|
||||
|
||||
if (success)
|
||||
{
|
||||
StatusMessage = "历史数据同步成功";
|
||||
Log.Information("历史数据同步到MES成功");
|
||||
MessageBox.Show("历史数据同步成功", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusMessage = "历史数据同步失败";
|
||||
MessageBox.Show("历史数据同步失败,请检查网络连接", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"同步失败: {ex.Message}";
|
||||
Log.Error(ex, "同步历史数据到MES失败");
|
||||
MessageBox.Show($"同步失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ClearRecords()
|
||||
{
|
||||
var result = MessageBox.Show("确定要清除所有历史记录吗?", "确认清除",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
Records.Clear();
|
||||
StatusMessage = "历史记录已清除";
|
||||
Log.Information("历史记录已清除");
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void DeleteSelectedRecord()
|
||||
{
|
||||
if (SelectedRecord == null)
|
||||
{
|
||||
MessageBox.Show("请先选择要删除的记录", "提示",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show($"确定要删除记录 #{SelectedRecord.RecordId} 吗?", "确认删除",
|
||||
MessageBoxButton.YesNo, MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
Records.Remove(SelectedRecord);
|
||||
SelectedRecord = null;
|
||||
StatusMessage = "记录已删除";
|
||||
Log.Information("记录已删除");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
ViewModels/ReportViewModel.cs
Normal file
12
ViewModels/ReportViewModel.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace HME_MoistureLossMeter.ViewModels
|
||||
{
|
||||
public partial class ReportViewModel : ViewModelBase
|
||||
{
|
||||
public ReportViewModel()
|
||||
{
|
||||
StatusMessage = "报表视图(待扩展)";
|
||||
}
|
||||
}
|
||||
}
|
||||
573
ViewModels/TestViewModel.cs
Normal file
573
ViewModels/TestViewModel.cs
Normal file
@@ -0,0 +1,573 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using HME_MoistureLossMeter.Models;
|
||||
using HME_MoistureLossMeter.Services;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Media3D;
|
||||
|
||||
namespace HME_MoistureLossMeter.ViewModels
|
||||
{
|
||||
public partial class TestViewModel : ViewModelBase
|
||||
{
|
||||
private readonly IPlcService _plcService;
|
||||
private readonly IMesService _mesService;
|
||||
private readonly DeviceConfiguration _deviceConfig;
|
||||
private CancellationTokenSource _cts;
|
||||
private bool _isDataLoopRunning;
|
||||
private float _totalWaterTemp;
|
||||
private float _totalChamberTemp;
|
||||
private float _totalTidalVolume;
|
||||
private float _totalFrequency;
|
||||
private float _totalAirVolume;
|
||||
private float _totalOutletFlow;
|
||||
private int _dataSampleCount;
|
||||
|
||||
// 实时数据属性
|
||||
[ObservableProperty]
|
||||
private float _waterTemp;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _chamberTemp;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _weight;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _airVolume;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _outletFlow;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _presetHour;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _presetMinute;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _displayHour;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _displayMinute;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _displaySecond;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _initialMass;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _finalMass;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _moistureLoss;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _batchNo = "";
|
||||
|
||||
[ObservableProperty]
|
||||
private int _operatorId;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _experimentId;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _tidalVolume;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _frequency;
|
||||
|
||||
[ObservableProperty]
|
||||
private int _breathCount;
|
||||
|
||||
[ObservableProperty]
|
||||
private float _dryAirFlow;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isTesting;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isHeating;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isInhale;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isExhale;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isRising;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isFalling;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _isConnected;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _connectionStatus = "未连接";
|
||||
|
||||
[ObservableProperty]
|
||||
private DateTime _currentTime = DateTime.Now;
|
||||
|
||||
public ObservableCollection<HistoryRecordModel> TestRecords { get; } = new();
|
||||
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MoveUp() => await ExecuteCoilCommand(ModbusTcpPlcService.COIL_UP, "上升");
|
||||
|
||||
[RelayCommand]
|
||||
private async Task MoveDown() => await ExecuteCoilCommand(ModbusTcpPlcService.COIL_DOWN, "下降");
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ManualInhale() => await ExecuteCoilCommand(ModbusTcpPlcService.COIL_MANUAL_INHALE, "手动吸气");
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ManualExhale() => await ExecuteCoilCommand(ModbusTcpPlcService.COIL_MANUAL_EXHALE, "手动呼气");
|
||||
|
||||
// 辅助方法
|
||||
private async Task ExecuteCoilCommand(ushort coilAddress, string commandName)
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(coilAddress, true);
|
||||
StatusMessage = $"{commandName}已执行";
|
||||
await Task.Delay(200);
|
||||
await _plcService.WriteCoilAsync(coilAddress, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusMessage = $"{commandName}失败: {ex.Message}";
|
||||
Log.Error(ex, "{Command}失败", commandName);
|
||||
MessageBox.Show($"{commandName}失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public TestViewModel(IPlcService plcService, IMesService mesService, DeviceConfiguration deviceConfig)
|
||||
{
|
||||
_plcService = plcService;
|
||||
_mesService = mesService;
|
||||
_deviceConfig = deviceConfig;
|
||||
|
||||
// 初始化定时更新
|
||||
var timer = new System.Timers.Timer(1000);
|
||||
timer.Elapsed += (s, e) => CurrentTime = DateTime.Now;
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
partial void OnIsTestingChanged(bool value)
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
// 开始测试时重置数据
|
||||
_dataSampleCount = 0;
|
||||
_totalWaterTemp = 0;
|
||||
_totalChamberTemp = 0;
|
||||
_totalTidalVolume = 0;
|
||||
_totalFrequency = 0;
|
||||
_totalAirVolume = 0;
|
||||
_totalOutletFlow = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Connect()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
StatusMessage = "正在连接PLC...";
|
||||
await _plcService.EnsureConnectedAsync();
|
||||
IsConnected = true;
|
||||
ConnectionStatus = "已连接";
|
||||
StatusMessage = "PLC连接成功";
|
||||
Log.Information("PLC连接成功");
|
||||
|
||||
// 启动数据循环
|
||||
StartDataLoop();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsConnected = false;
|
||||
ConnectionStatus = "连接失败";
|
||||
StatusMessage = $"连接失败: {ex.Message}";
|
||||
Log.Error(ex, "PLC连接失败");
|
||||
MessageBox.Show($"PLC连接失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Disconnect()
|
||||
{
|
||||
StopDataLoop();
|
||||
IsConnected = false;
|
||||
ConnectionStatus = "已断开";
|
||||
StatusMessage = "已断开PLC连接";
|
||||
Log.Information("PLC连接已断开");
|
||||
}
|
||||
|
||||
private void StartDataLoop()
|
||||
{
|
||||
if (_isDataLoopRunning) return;
|
||||
|
||||
_cts = new CancellationTokenSource();
|
||||
_isDataLoopRunning = true;
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (!_cts.Token.IsCancellationRequested && IsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
await ReadAllData();
|
||||
await Task.Delay(_deviceConfig.UpdateIntervalMs, _cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "读取数据异常");
|
||||
StatusMessage = $"读取数据异常: {ex.Message}";
|
||||
await Task.Delay(2000);
|
||||
}
|
||||
}
|
||||
}, _cts.Token);
|
||||
}
|
||||
|
||||
private void StopDataLoop()
|
||||
{
|
||||
_isDataLoopRunning = false;
|
||||
_cts?.Cancel();
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
}
|
||||
|
||||
private async Task ReadAllData()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取浮点数数据
|
||||
WaterTemp = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_WATER_TEMP);
|
||||
ChamberTemp = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_CHAMBER_TEMP);
|
||||
Weight = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_WEIGHT);
|
||||
AirVolume = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_AIR_VOLUME);
|
||||
OutletFlow = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_OUTLET_FLOW);
|
||||
TidalVolume = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_TIDAL_VOLUME);
|
||||
Frequency = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_FREQUENCY);
|
||||
DryAirFlow = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_DRY_AIR_FLOW);
|
||||
InitialMass = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_INITIAL_MASS);
|
||||
FinalMass = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_FINAL_MASS);
|
||||
MoistureLoss = await _plcService.ReadFloatAsync(ModbusTcpPlcService.ADDR_MOISTURE_LOSS);
|
||||
|
||||
// 读取整数数据
|
||||
PresetHour = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_PRESET_HOUR);
|
||||
PresetMinute = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_PRESET_MINUTE);
|
||||
DisplayHour = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_DISPLAY_HOUR);
|
||||
DisplayMinute = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_DISPLAY_MINUTE);
|
||||
DisplaySecond = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_DISPLAY_SECOND);
|
||||
BreathCount = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_BREATH_COUNT);
|
||||
OperatorId = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_OPERATOR_ID);
|
||||
ExperimentId = await _plcService.ReadInt32Async(ModbusTcpPlcService.ADDR_EXPERIMENT_ID);
|
||||
|
||||
// 读取字符串(批号)
|
||||
var batchRegs = await _plcService.ReadHoldingRegistersAsync(ModbusTcpPlcService.ADDR_BATCH_NO, 10);
|
||||
BatchNo = RegistersToString(batchRegs);
|
||||
|
||||
// 读取线圈状态
|
||||
IsTesting = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_TEST);
|
||||
IsHeating = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_HEAT);
|
||||
IsInhale = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_INHALE);
|
||||
IsExhale = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_EXHALE);
|
||||
IsRising = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_UP);
|
||||
IsFalling = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_DOWN);
|
||||
|
||||
// 如果正在测试,收集数据用于平均值计算
|
||||
if (IsTesting)
|
||||
{
|
||||
_dataSampleCount++;
|
||||
_totalWaterTemp += WaterTemp;
|
||||
_totalChamberTemp += ChamberTemp;
|
||||
_totalTidalVolume += TidalVolume;
|
||||
_totalFrequency += Frequency;
|
||||
_totalAirVolume += AirVolume;
|
||||
_totalOutletFlow += OutletFlow;
|
||||
|
||||
// 发送实时数据到MES
|
||||
await SendRealtimeDataToMes();
|
||||
}
|
||||
|
||||
StatusMessage = "数据读取成功";
|
||||
IsConnected = true;
|
||||
ConnectionStatus = "已连接";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
IsConnected = false;
|
||||
ConnectionStatus = "通信异常";
|
||||
Log.Error(ex, "读取数据失败");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private string RegistersToString(ushort[] registers)
|
||||
{
|
||||
var bytes = new byte[registers.Length * 2];
|
||||
for (int i = 0; i < registers.Length; i++)
|
||||
{
|
||||
var regBytes = BitConverter.GetBytes(registers[i]);
|
||||
bytes[i * 2] = regBytes[0];
|
||||
bytes[i * 2 + 1] = regBytes[1];
|
||||
}
|
||||
return System.Text.Encoding.ASCII.GetString(bytes).TrimEnd('\0');
|
||||
}
|
||||
|
||||
private async Task SendRealtimeDataToMes()
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = new RealtimeDataModel
|
||||
{
|
||||
DeviceId = _deviceConfig.DeviceId,
|
||||
Timestamp = DateTime.Now.ToString("o"),
|
||||
Realtime = new RealtimeData
|
||||
{
|
||||
WaterTemp = WaterTemp,
|
||||
ChamberTemp = ChamberTemp,
|
||||
Weight = Weight,
|
||||
AirVolume = AirVolume,
|
||||
OutletFlow = OutletFlow,
|
||||
TidalVolume = TidalVolume,
|
||||
Frequency = Frequency,
|
||||
BreathRate = BreathCount,
|
||||
DryAirFlow = DryAirFlow
|
||||
},
|
||||
Settings = new SettingsData
|
||||
{
|
||||
PresetHour = PresetHour,
|
||||
PresetMinute = PresetMinute,
|
||||
PresetSecond = DisplaySecond,
|
||||
OperatorId = OperatorId,
|
||||
BatchNo = BatchNo,
|
||||
ExperimentId = ExperimentId
|
||||
},
|
||||
Status = new StatusData
|
||||
{
|
||||
IsTesting = IsTesting,
|
||||
IsFault = false
|
||||
}
|
||||
};
|
||||
|
||||
await _mesService.SendRealtimeDataAsync(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "发送实时数据到MES失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 测试控制命令
|
||||
[RelayCommand]
|
||||
private async Task StartTest()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_TEST, true);
|
||||
StatusMessage = "测试已启动";
|
||||
Log.Information("测试启动");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "启动测试失败");
|
||||
MessageBox.Show($"启动测试失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task StopTest()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_STOP, true);
|
||||
StatusMessage = "测试已停止";
|
||||
Log.Information("测试停止");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "停止测试失败");
|
||||
MessageBox.Show($"停止测试失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_RESET, true);
|
||||
StatusMessage = "已复位";
|
||||
Log.Information("设备复位");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "复位失败");
|
||||
MessageBox.Show($"复位失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_CLEAR, true);
|
||||
StatusMessage = "已清零";
|
||||
Log.Information("数据清零");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "清零失败");
|
||||
MessageBox.Show($"清零失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task ToggleHeat()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
bool currentState = await _plcService.ReadCoilAsync(ModbusTcpPlcService.COIL_HEAT);
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_HEAT, !currentState);
|
||||
StatusMessage = currentState ? "加热关闭" : "加热开启";
|
||||
Log.Information("加热切换: {State}", !currentState);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "切换加热失败");
|
||||
MessageBox.Show($"切换加热失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RecordP1()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_P1_RECORD, true);
|
||||
StatusMessage = "P1记录已触发";
|
||||
Log.Information("P1记录触发");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "P1记录失败");
|
||||
MessageBox.Show($"P1记录失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task RecordP2()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_P2_RECORD, true);
|
||||
StatusMessage = "P2记录已触发";
|
||||
Log.Information("P2记录触发");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "P2记录失败");
|
||||
MessageBox.Show($"P2记录失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task Print()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
await _plcService.WriteCoilAsync(ModbusTcpPlcService.COIL_PRINT, true);
|
||||
StatusMessage = "打印已触发";
|
||||
Log.Information("打印触发");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "打印失败");
|
||||
MessageBox.Show($"打印失败: {ex.Message}", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
StopDataLoop();
|
||||
_plcService?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
21
ViewModels/ViewModelBase.cs
Normal file
21
ViewModels/ViewModelBase.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace HME_MoistureLossMeter.ViewModels
|
||||
{
|
||||
public abstract class ViewModelBase : ObservableObject
|
||||
{
|
||||
private bool _isBusy;
|
||||
public bool IsBusy
|
||||
{
|
||||
get => _isBusy;
|
||||
set => SetProperty(ref _isBusy, value);
|
||||
}
|
||||
|
||||
private string _statusMessage = "就绪";
|
||||
public string StatusMessage
|
||||
{
|
||||
get => _statusMessage;
|
||||
set => SetProperty(ref _statusMessage, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user