161 lines
5.2 KiB
C#
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);
|
|
}
|
|
}
|