Files
HeadgearViewingRange3M/ViewModels/ParameterViewModel.cs
2026-04-01 21:30:13 +08:00

308 lines
12 KiB
C#
Raw Permalink 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 CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MembranePoreTester.Communication;
using Modbus.Device;
using System;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Threading;
namespace MembranePoreTester.ViewModels
{
public partial class ParameterViewModel : ObservableObject
{
private readonly IPlcService _plcService;
private readonly PlcConfiguration _config;
private DispatcherTimer _autoRefreshTimer;
private bool _isUpdatingFromPlc = false; // 防止定时器更新触发写入
// 构造函数(依赖注入)
public ParameterViewModel()
{
_plcService = App.PlcService;
_config = App.PlcConfig;
// 启动定时器(每秒自动刷新)
_autoRefreshTimer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(1)
};
_autoRefreshTimer.Tick += AutoRefreshTimer_Tick;
_autoRefreshTimer.Start();
}
// ========== 设备数据属性(全部使用源生成器) ==========
[ObservableProperty]
private double _upperLampData1;
[ObservableProperty]
private double _upperLampData2;
[ObservableProperty]
private double _upperLampData3;
[ObservableProperty]
private double _upperLampData4;
[ObservableProperty]
private double _upperLampData5;
[ObservableProperty]
private double _upperLampData6;
[ObservableProperty]
private double _lowerLampData1;
[ObservableProperty]
private double _lowerLampData2;
[ObservableProperty]
private double _lowerLampData3;
[ObservableProperty]
private double _lowerLampData4;
[ObservableProperty]
private double _lowerLampData5;
[ObservableProperty]
private double _lowerLampData6;
[ObservableProperty]
private double _middleLamp1;
[ObservableProperty]
private double _middleLamp2;
[ObservableProperty]
private double _middleLamp3;
[ObservableProperty]
private double _middleLamp4;
[ObservableProperty]
private double _middleLamp5;
[ObservableProperty]
private double _middleLamp6;
[ObservableProperty]
private double _middleLamp7;
[ObservableProperty]
private double _leftEyeAreaCoeff;
[ObservableProperty]
private double _rightEyeAreaCoeff;
[ObservableProperty]
private double _saveRateCorrectionCoeff;
[ObservableProperty]
private double _motorLimit;
[ObservableProperty]
private double _resetCompensation;
// 通信状态(用于界面显示)
[ObservableProperty]
private string _communicationStatus = "未连接";
[ObservableProperty]
private System.Windows.Media.SolidColorBrush _statusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);
// ========== 属性变化时的自动写入用户修改时立即写入PLC ==========
// 这里我们重写属性 setter在值变化时写入PLC排除定时器更新
//partial void OnUpperLampData1Changing(double value) => WriteParameterAsync(_config.UpperLampData1, value, false);
//partial void OnUpperLampData2Changing(double value) => WriteParameterAsync(_config.UpperLampData2, value, false);
// ... 其他灯条属性类似(为了简洁,仅展示几个,您需全部添加)
// 您可以使用代码生成工具批量添加,或手动添加所有属性对应的 Changing 方法。
// 注意:只有需要自动写入的才添加,只读属性不添加。
// 为节省篇幅,下面仅示例部分,实际使用时请按此模式为所有需要读写的属性添加
partial void OnLowerLampData1Changing(double value) => WriteParameterAsync(_config.LowerLampData1, value, false);
partial void OnMiddleLamp1Changing(double value) => WriteParameterAsync(_config.MiddleLamp1, value, false);
partial void OnLeftEyeAreaCoeffChanging(double value) => WriteParameterAsync(_config.LeftEyeAreaCoeff, value, true);
partial void OnRightEyeAreaCoeffChanging(double value) => WriteParameterAsync(_config.RightEyeAreaCoeff, value, true);
partial void OnSaveRateCorrectionCoeffChanging(double value) => WriteParameterAsync(_config.SaveRateCorrectionCoeff, value, true);
partial void OnMotorLimitChanging(double value) => WriteParameterAsync(_config.MotorLimit, value, true);
partial void OnResetCompensationChanging(double value) => WriteParameterAsync(_config.ResetCompensation, value, true);
// 写入PLC的通用方法
private async Task WriteParameterAsync(ushort address, double value, bool isFloat)
{
if (_isUpdatingFromPlc) return; // 定时器读取时跳过写入
try
{
if (isFloat)
await WriteFloatAsync(address, (float)value);
else
await WriteInt16Async(address, (ushort)value);
System.Diagnostics.Debug.WriteLine($"写入成功: 地址={address}, 值={value}");
}
catch (Exception ex)
{
System.Windows.MessageBox.Show($"写入参数失败: {ex.Message}", "错误");
}
}
//// ========== 手动保存/读取命令 ==========
//[RelayCommand]
//private async Task SaveAllParameters()
//{
// // 将所有当前值写入PLC
// // 这里可以遍历所有属性,但为了简单,直接调用各个写入方法
// await WriteParameterAsync(_config.UpperLampData1, UpperLampData1, false);
// // ... 所有其他属性
// CommunicationStatus = "参数已保存";
// StatusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green);
//}
//[RelayCommand]
//private async Task LoadAllParameters()
//{
// // 从PLC读取所有参数并刷新界面
// await RefreshAllFromPlc();
// CommunicationStatus = "参数已读取";
// StatusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Green);
//}
// 定时刷新从PLC读取但不触发写入
private async void AutoRefreshTimer_Tick(object sender, EventArgs e)
{
await RefreshAllFromPlc();
}
private async Task RefreshAllFromPlc()
{
_isUpdatingFromPlc = true;
try
{
// 读取并更新每个属性16位整数和浮点数
UpperLampData1 = await ReadInt16Async(_config.UpperLampData1);
UpperLampData2 = await ReadInt16Async(_config.UpperLampData2);
UpperLampData3 = await ReadInt16Async(_config.UpperLampData3);
UpperLampData4 = await ReadInt16Async(_config.UpperLampData4);
UpperLampData5 = await ReadInt16Async(_config.UpperLampData5);
UpperLampData6 = await ReadInt16Async(_config.UpperLampData6);
LowerLampData1 = await ReadInt16Async(_config.LowerLampData1);
LowerLampData2 = await ReadInt16Async(_config.LowerLampData2);
LowerLampData3 = await ReadInt16Async(_config.LowerLampData3);
LowerLampData4 = await ReadInt16Async(_config.LowerLampData4);
LowerLampData5 = await ReadInt16Async(_config.LowerLampData5);
LowerLampData6 = await ReadInt16Async(_config.LowerLampData6);
MiddleLamp1 = await ReadInt16Async(_config.MiddleLamp1);
MiddleLamp2 = await ReadInt16Async(_config.MiddleLamp2);
MiddleLamp3 = await ReadInt16Async(_config.MiddleLamp3);
MiddleLamp4 = await ReadInt16Async(_config.MiddleLamp4);
MiddleLamp5 = await ReadInt16Async(_config.MiddleLamp5);
MiddleLamp6 = await ReadInt16Async(_config.MiddleLamp6);
MiddleLamp7 = await ReadInt16Async(_config.MiddleLamp7);
LeftEyeAreaCoeff = await ReadFloatAsync(_config.LeftEyeAreaCoeff);
RightEyeAreaCoeff = await ReadFloatAsync(_config.RightEyeAreaCoeff);
SaveRateCorrectionCoeff = await ReadFloatAsync(_config.SaveRateCorrectionCoeff);
MotorLimit = await ReadFloatAsync(_config.MotorLimit);
ResetCompensation = await ReadFloatAsync(_config.ResetCompensation);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"刷新参数失败: {ex.Message}");
CommunicationStatus = "通信异常";
StatusColor = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);
}
finally
{
_isUpdatingFromPlc = false;
}
}
// 设备控制命令(供右侧按钮使用)
[RelayCommand]
private async Task Reset()
{
// 实现硬件复位逻辑
CommunicationStatus = "复位指令已发送";
await _plcService.WriteCoilAsync(91, true);
Thread.Sleep(100);
await _plcService.WriteCoilAsync(91, false);
Thread.Sleep(100);
}
[RelayCommand]
private async Task OpenLeftEye()
{
CommunicationStatus = "左眼开启指令已发送";
await _plcService.WriteCoilAsync(0, true);
Thread.Sleep(100);
}
[RelayCommand]
private async Task OpenRightEye()
{
CommunicationStatus = "右眼开启指令已发送";
await _plcService.WriteCoilAsync(1, true);
Thread.Sleep(100);
}
[RelayCommand]
private async Task Reverse()
{
CommunicationStatus = "反转指令已发送";
await _plcService.WriteCoilAsync(10, true);
Thread.Sleep(100);
await _plcService.WriteCoilAsync(10, false);
Thread.Sleep(100);
}
[RelayCommand]
private async Task Forward()
{
CommunicationStatus = "正转指令已发送";
await _plcService.WriteCoilAsync(11, true);
Thread.Sleep(100);
await _plcService.WriteCoilAsync(11, false);
Thread.Sleep(100);
}
// 导航命令(可根据实际需求实现)
[RelayCommand]
private void NavigateHome()
{
// 切换页面逻辑
}
[RelayCommand]
private void NavigateTest()
{
// 切换页面逻辑
}
[RelayCommand]
private void NavigateDataRecord()
{
// 切换页面逻辑
}
[RelayCommand]
private void NavigateRecordScreen()
{
// 切换页面逻辑
}
// ========== PLC 读写基础方法 ==========
private async Task<ushort> ReadInt16Async(ushort address)
{
var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 1);
return registers[0];
}
private async Task WriteInt16Async(ushort address, ushort value)
{
await ((ModbusTcpPlcService)_plcService).WriteSingleRegisterAsync(address, value);
}
private async Task<float> ReadFloatAsync(ushort address)
{
var registers = await ((ModbusTcpPlcService)_plcService).ReadHoldingRegistersAsync(address, 2);
return ((ModbusTcpPlcService)_plcService).UshortToFloat(registers[1], registers[0]);
}
private async Task WriteFloatAsync(ushort address, float value)
{
await ((ModbusTcpPlcService)_plcService).WriteMultipleRegistersAsync(address, value);
}
// 释放定时器(在窗口关闭时调用)
public void Dispose()
{
_autoRefreshTimer?.Stop();
}
}
}