Files
DentistryHandpieces/DentistryHandpieces/HiddenSpeedSettingsViewModel.cs
2026-06-10 10:33:44 +08:00

161 lines
5.2 KiB
C#

using System.Collections.ObjectModel;
using System.Globalization;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
namespace DentistryHandpieces;
public sealed class SpeedCoefficientSettingRow : ObservableObject
{
private string _valueText = string.Empty;
public required ushort Address { get; init; }
public required string RangeText { get; init; }
public string ValueText
{
get => _valueText;
set => SetProperty(ref _valueText, value);
}
}
public sealed class HiddenSpeedSettingsViewModel : ObservableObject
{
private const ushort FirstSpeedCoefficientRegister = 1200;
private const int SpeedCoefficientCount = 30;
private readonly IPlcRegisterService _plcRegisterService;
private readonly PlcConnectionConfig _plcConfig;
private string _statusText = "打开后将从 PLC 读取参数。";
private bool _isBusy;
public HiddenSpeedSettingsViewModel(IPlcRegisterService plcRegisterService, PlcConnectionConfig plcConfig)
{
_plcRegisterService = plcRegisterService;
_plcConfig = plcConfig;
ReadCommand = new AsyncRelayCommand(ReadAsync);
SaveSpeedCoefficientCommand = new AsyncRelayCommand<SpeedCoefficientSettingRow>(SaveSpeedCoefficientAsync);
for (int index = 0; index < SpeedCoefficientCount; index++)
{
int lower = index == 0 ? 0 : index * 10000 + 1;
int upper = (index + 1) * 10000;
SpeedCoefficientSettings.Add(new SpeedCoefficientSettingRow
{
Address = checked((ushort)(FirstSpeedCoefficientRegister + index * 2)),
RangeText = $"转速系数{lower}-{upper}"
});
}
}
public ObservableCollection<SpeedCoefficientSettingRow> SpeedCoefficientSettings { get; } = [];
public IAsyncRelayCommand ReadCommand { get; }
public IAsyncRelayCommand<SpeedCoefficientSettingRow> SaveSpeedCoefficientCommand { get; }
public string StatusText
{
get => _statusText;
private set => SetProperty(ref _statusText, value);
}
public async Task ReadAsync()
{
if (_isBusy)
{
return;
}
_isBusy = true;
StatusText = "正在读取 PLC 隐藏参数...";
try
{
await ReadValuesAsync();
StatusText = "隐藏参数读取成功。";
}
catch (Exception ex)
{
StatusText = $"读取失败:{OperatorMessageFormatter.FromException(ex)}";
}
finally
{
_isBusy = false;
}
}
private async Task SaveSpeedCoefficientAsync(SpeedCoefficientSettingRow? row)
{
if (_isBusy || row is null)
{
return;
}
if (!TryParseFloat(row.ValueText, out float value))
{
StatusText = $"{row.RangeText} 必须是有效数字。";
return;
}
_isBusy = true;
StatusText = $"正在保存{row.RangeText}...";
try
{
await _plcRegisterService.WriteFloatAsync(_plcConfig, row.Address, value);
float confirmedValue = await _plcRegisterService.ReadFloatAsync(_plcConfig, row.Address);
if (float.IsNaN(confirmedValue)
|| float.IsInfinity(confirmedValue)
|| !AreEquivalentFloatRegisterValues(value, confirmedValue))
{
throw new InvalidOperationException("PLC 写入后回读不一致。");
}
row.ValueText = FormatNumber(confirmedValue);
StatusText = $"{row.RangeText}已保存并回读确认。";
}
catch (Exception ex)
{
StatusText = $"{row.RangeText}保存失败:{OperatorMessageFormatter.FromException(ex)}";
}
finally
{
_isBusy = false;
}
}
private async Task ReadValuesAsync()
{
ushort[] addresses = SpeedCoefficientSettings.Select(static row => row.Address).ToArray();
IReadOnlyDictionary<ushort, float> values = await _plcRegisterService.ReadFloatValuesAsync(_plcConfig, addresses);
foreach (SpeedCoefficientSettingRow row in SpeedCoefficientSettings)
{
if (!values.TryGetValue(row.Address, out float value)
|| float.IsNaN(value)
|| float.IsInfinity(value))
{
throw new InvalidOperationException($"{row.RangeText}读取无效。");
}
row.ValueText = FormatNumber(value);
}
}
private static string FormatNumber(float value)
{
return value.ToString("0.########", CultureInfo.InvariantCulture);
}
private static bool TryParseFloat(string input, out float value)
{
bool parsed = float.TryParse(input, NumberStyles.Float, CultureInfo.CurrentCulture, out value)
|| float.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out value);
return parsed && !float.IsNaN(value) && !float.IsInfinity(value);
}
private static bool AreEquivalentFloatRegisterValues(float left, float right)
{
return BitConverter.SingleToInt32Bits(left) == BitConverter.SingleToInt32Bits(right);
}
}