218 lines
8.7 KiB
C#
218 lines
8.7 KiB
C#
using Microsoft.Win32;
|
||
using System;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using TabletTester2025.Helpers;
|
||
using TabletTester2025.Models;
|
||
using TabletTester2025.Services;
|
||
|
||
namespace 片剂四用仪.Views
|
||
{
|
||
public partial class ShowData : Window
|
||
{
|
||
private readonly IPlcService _plc;
|
||
private readonly PlcParamMapping[] _paramMappings;
|
||
private CancellationTokenSource? _cts;
|
||
|
||
public ShowData(IPlcService plc, PlcConfiguration plcConfig)
|
||
{
|
||
InitializeComponent();
|
||
_plc = plc;
|
||
_paramMappings = BuildParamMappings(plcConfig);
|
||
Loaded += ShowData_Loaded;
|
||
Closing += ShowData_Closing;
|
||
}
|
||
|
||
private async void ShowData_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
var cts = new CancellationTokenSource();
|
||
_cts = cts;
|
||
BindAllTextBoxWriteEvents();
|
||
await StartPlcReadLoop(cts.Token);
|
||
}
|
||
|
||
private void ShowData_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
|
||
{
|
||
_cts?.Cancel();
|
||
}
|
||
|
||
// ====================== 读取 PLC ======================
|
||
private async Task StartPlcReadLoop(CancellationToken token)
|
||
{
|
||
while (!token.IsCancellationRequested)
|
||
{
|
||
try
|
||
{
|
||
foreach (var m in _paramMappings)
|
||
{
|
||
var tb = FindName(m.TextBoxName) as TextBox;
|
||
if (tb == null) continue;
|
||
if (NumericInput.GetIsKeypadOpen(tb) || tb.IsKeyboardFocusWithin)
|
||
continue;
|
||
|
||
if (m.Type == PlcParamType.Int)
|
||
{
|
||
int value = await _plc.ReadIntAsync((ushort)m.Address);
|
||
|
||
Dispatcher.Invoke(() => tb.Text = value.ToString());
|
||
|
||
}
|
||
else
|
||
{
|
||
float value = await _plc.ReadFloatAsync((ushort)m.Address);
|
||
Dispatcher.Invoke(() => tb.Text = float.IsFinite(value) ? value.ToString("0.###") : string.Empty);
|
||
|
||
}
|
||
}
|
||
// 正常循环的Delay,同样处理取消异常
|
||
try
|
||
{
|
||
await Task.Delay(1000, token);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
// 窗口关闭时的取消异常,正常退出循环
|
||
break;
|
||
}
|
||
catch
|
||
{
|
||
try
|
||
{
|
||
await Task.Delay(1000, token);
|
||
}
|
||
catch (OperationCanceledException)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
//await Task.Delay(1000, token);
|
||
}
|
||
}
|
||
|
||
// ====================== 写入 PLC ======================
|
||
private void BindAllTextBoxWriteEvents()
|
||
{
|
||
foreach (var m in _paramMappings)
|
||
{
|
||
var tb = FindName(m.TextBoxName) as TextBox;
|
||
if (tb == null) continue;
|
||
|
||
if (tb.IsReadOnly)
|
||
continue;
|
||
|
||
NumericInput.SetIsEnabled(tb, true);
|
||
NumericInput.SetAllowDecimal(tb, m.Type != PlcParamType.Int);
|
||
NumericInput.SetAllowNegative(tb, false);
|
||
NumericInput.AddValueCommittedHandler(tb, async (_, _) => await WritePlcValueAsync(m, tb));
|
||
}
|
||
}
|
||
|
||
private async Task WritePlcValueAsync(PlcParamMapping mapping, TextBox textBox)
|
||
{
|
||
try
|
||
{
|
||
if (mapping.Type == PlcParamType.Int)
|
||
{
|
||
if (int.TryParse(textBox.Text, out int val))
|
||
await _plc.WriteRegisterAsync((ushort)mapping.Address, (ushort)Math.Clamp(val, 0, ushort.MaxValue));
|
||
}
|
||
else
|
||
{
|
||
if (double.TryParse(textBox.Text, out double val) && double.IsFinite(val))
|
||
{
|
||
textBox.Text = val.ToString("0.###");
|
||
await _plc.WriteFloatAsync((ushort)mapping.Address, (float)val);
|
||
}
|
||
}
|
||
}
|
||
catch { }
|
||
}
|
||
|
||
// ====================== 地址映射表 ======================
|
||
private static PlcParamMapping[] BuildParamMappings(PlcConfiguration plcConfig)
|
||
{
|
||
return new[]
|
||
{
|
||
new PlcParamMapping("txt_HardnessSpeed", AddressOrDefault(plcConfig.HardnessSudu, 300), PlcParamType.Float),
|
||
new PlcParamMapping("txt_HardnessDisplacement", AddressOrDefault(plcConfig.HardnessWeiyi, 310), PlcParamType.Float),
|
||
new PlcParamMapping("txt_HardnessMotorLimit", AddressOrDefault(plcConfig.HardnessLimit, 298), PlcParamType.Float),
|
||
new PlcParamMapping("txt_HardnessDamageThreshold", AddressOrDefault(plcConfig.HardnessPoSun, 400), PlcParamType.Float),
|
||
|
||
new PlcParamMapping("txt_FriabilityRounds", ResolveFriabilityRoundsRegister(plcConfig), PlcParamType.Int),
|
||
new PlcParamMapping("txt_PreBrittlenessMass", ResolveWeightRegister(plcConfig.FriabilityWeightBefore, plcConfig.WeightBefore, 412), PlcParamType.Float),
|
||
new PlcParamMapping("txt_PostBrittlenessMass", ResolveWeightRegister(plcConfig.FriabilityWeightAfter, plcConfig.WeightAfter, 414), PlcParamType.Float),
|
||
new PlcParamMapping("txt_WeightLossRate", 416, PlcParamType.Int),
|
||
|
||
new PlcParamMapping("txt_DisintegrationSpeed", AddressOrDefault(plcConfig.DisintegrationSpeed, 330), PlcParamType.Float),
|
||
new PlcParamMapping("txt_DisintegrationTime", AddressOrDefault(plcConfig.DisintegrationTime, 420), PlcParamType.Int),
|
||
|
||
new PlcParamMapping("txt_DissolutionTime", AddressOrDefault(plcConfig.Dissolution1Time, 430), PlcParamType.Int),
|
||
new PlcParamMapping("txt_Dissolution2Time", AddressOrDefault(plcConfig.Dissolution2Time, 440), PlcParamType.Int),
|
||
new PlcParamMapping("txt_Dissolution1SamplingInterval", AddressOrDefault(plcConfig.Dissolution1SampleInterval, 432), PlcParamType.Float),
|
||
new PlcParamMapping("txt_Dissolution2SamplingInterval", AddressOrDefault(plcConfig.Dissolution2SampleInterval, 442), PlcParamType.Float),
|
||
|
||
new PlcParamMapping("txt_ForceCoefficient", 1320, PlcParamType.Float),
|
||
new PlcParamMapping("txt_ForceProtection", 1322, PlcParamType.Float),
|
||
|
||
new PlcParamMapping("txt_TemperatureDisplay", AddressOrDefault(plcConfig.DisintegrationTemp, 1430), PlcParamType.Float),
|
||
|
||
new PlcParamMapping("txt_MaxForceCollect", AddressOrDefault(plcConfig.HardnessMax, 72), PlcParamType.Float),
|
||
new PlcParamMapping("txt_ForceDisplay", AddressOrDefault(plcConfig.HardnessShishilizhi, 1314), PlcParamType.Float),
|
||
new PlcParamMapping("txt_TemperatureCoefficient", 1428, PlcParamType.Float),
|
||
};
|
||
}
|
||
|
||
private static ushort AddressOrDefault(ushort configuredAddress, ushort fallbackAddress)
|
||
{
|
||
return configuredAddress != 0 ? configuredAddress : fallbackAddress;
|
||
}
|
||
|
||
private static ushort ResolveFriabilityRoundsRegister(PlcConfiguration plcConfig)
|
||
{
|
||
if (plcConfig.FriabilityRounds != 0)
|
||
return plcConfig.FriabilityRounds;
|
||
|
||
if (plcConfig.FriabilityRoundsBox != 0)
|
||
return plcConfig.FriabilityRoundsBox;
|
||
|
||
return AddressOrDefault(plcConfig.FriabilityTestTime, 410);
|
||
}
|
||
|
||
private static ushort ResolveWeightRegister(ushort primaryAddress, ushort compatibleAddress, ushort fallbackAddress)
|
||
{
|
||
if (primaryAddress != 0)
|
||
return primaryAddress;
|
||
|
||
return AddressOrDefault(compatibleAddress, fallbackAddress);
|
||
}
|
||
}
|
||
|
||
internal class PlcParamMapping
|
||
{
|
||
public string TextBoxName { get; }
|
||
public int Address { get; }
|
||
public PlcParamType Type { get; }
|
||
|
||
public PlcParamMapping(string textBoxName, int address, PlcParamType type)
|
||
{
|
||
TextBoxName = textBoxName;
|
||
Address = address;
|
||
Type = type;
|
||
}
|
||
}
|
||
|
||
internal enum PlcParamType
|
||
{
|
||
Int,
|
||
Float
|
||
}
|
||
}
|