更新20260530

This commit is contained in:
GukSang.Jin
2026-05-30 15:37:45 +08:00
parent 0a908d85df
commit 0236865ee6
12 changed files with 309 additions and 43 deletions

View File

@@ -9,6 +9,8 @@ public sealed record DeviceSnapshot(
double EnvironmentHumidityPercent,
double AirSpeedMetersPerSecond,
double PowerWatts,
double VoltageVolts,
double CurrentAmps,
double EnergyKilojoules,
double WaterLossGramsPerHour,
double PumpSpeedCubicCentimetersPerHour,
@@ -24,6 +26,8 @@ public sealed record DeviceSnapshot(
50.0,
1.10,
4.5,
220.0,
0.02,
0.0,
5.0,
5.0,

View File

@@ -28,6 +28,8 @@ public sealed record MethodASampleRecord(
double RelativeHumidityPercent,
double WaterLossGramsPerHour,
double PowerWatts,
double VoltageVolts,
double CurrentAmps,
double MoistureHeatWatts,
double DryHeatWatts,
double MoistureResistance,

View File

@@ -16,4 +16,8 @@ public sealed class DeviceSettings
public double MethodBAirSpeedMetersPerSecond { get; set; } = 1.10;
public double PumpSpeedCubicCentimetersPerHour { get; set; } = 5.0;
public double CoefficientOfVariationLimitPercent { get; set; } = 8.0;
public int VoltageRegisterAddress { get; set; } = -1;
public double VoltageRegisterScale { get; set; } = 0.1;
public int CurrentRegisterAddress { get; set; } = -1;
public double CurrentRegisterScale { get; set; } = 0.001;
}

View File

@@ -151,7 +151,7 @@ public sealed class ExcelReportService
{
var rows = new List<IReadOnlyList<object?>>
{
Row("序号", "时间", "假脚温度 ℃", "环境温度 ℃", "相对湿度 %", "出汗量 g/h", "加热功率 W", "湿热量 He W", "干热量 Hd W", "湿阻 Pa·m2/W", "热阻 m2·℃/W"),
Row("序号", "时间", "假脚温度 ℃", "环境温度 ℃", "相对湿度 %", "出汗量 g/h", "电压 V", "电流 A", "加热功率 W", "湿热量 He W", "干热量 Hd W", "湿阻 Pa·m2/W", "热阻 m2·℃/W"),
};
if (root.TryGetProperty("Samples", out var samples) && samples.ValueKind == JsonValueKind.Array)
@@ -169,6 +169,8 @@ public sealed class ExcelReportService
GetDouble(sample, "EnvironmentTemperatureC"),
GetDouble(sample, "RelativeHumidityPercent"),
waterLoss,
GetDouble(sample, "VoltageVolts"),
GetDouble(sample, "CurrentAmps"),
power,
moistureHeat,
dryHeat,

View File

@@ -1,4 +1,6 @@
using System;
using System.Buffers.Binary;
using System.IO;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
@@ -9,10 +11,14 @@ namespace FootwearTest.Services;
public sealed class ModbusTcpDeviceClient : IDeviceClient
{
private const byte UnitId = 1;
private const byte ReadHoldingRegistersFunction = 0x03;
private readonly DeviceSettings _settings;
private readonly ILogger<ModbusTcpDeviceClient> _logger;
private TcpClient? _tcpClient;
private bool _lastReadFailed;
private ushort _transactionId;
public ModbusTcpDeviceClient(DeviceSettings settings, ILogger<ModbusTcpDeviceClient> logger)
{
@@ -60,9 +66,38 @@ public sealed class ModbusTcpDeviceClient : IDeviceClient
return Task.CompletedTask;
}
public Task<DeviceSnapshot> ReadSnapshotAsync(CancellationToken cancellationToken = default)
public async Task<DeviceSnapshot> ReadSnapshotAsync(CancellationToken cancellationToken = default)
{
LastSnapshot = LastSnapshot with { Timestamp = DateTime.Now };
try
{
var voltage = LastSnapshot.VoltageVolts;
var current = LastSnapshot.CurrentAmps;
if (_settings.VoltageRegisterAddress >= 0)
{
voltage = await ReadScaledHoldingRegisterAsync(
_settings.VoltageRegisterAddress,
_settings.VoltageRegisterScale,
cancellationToken);
}
if (_settings.CurrentRegisterAddress >= 0)
{
current = await ReadScaledHoldingRegisterAsync(
_settings.CurrentRegisterAddress,
_settings.CurrentRegisterScale,
cancellationToken);
}
var power = voltage > 0 && current > 0 ? voltage * current : LastSnapshot.PowerWatts;
LastSnapshot = LastSnapshot with
{
Timestamp = DateTime.Now,
VoltageVolts = Math.Round(voltage, 2),
CurrentAmps = Math.Round(current, 3),
PowerWatts = Math.Round(power, 2),
AlarmText = "Modbus 电压/电流寄存器读取正常"
};
SnapshotUpdated?.Invoke(this, LastSnapshot);
if (_lastReadFailed)
{
@@ -70,7 +105,65 @@ public sealed class ModbusTcpDeviceClient : IDeviceClient
_lastReadFailed = false;
}
return Task.FromResult(LastSnapshot);
return LastSnapshot;
}
catch (Exception ex)
{
_lastReadFailed = true;
LastSnapshot = LastSnapshot with
{
Timestamp = DateTime.Now,
AlarmText = $"Modbus 读取失败: {ex.Message}"
};
SnapshotUpdated?.Invoke(this, LastSnapshot);
_logger.LogError(ex, "Failed to read Modbus snapshot");
throw;
}
}
private async Task<double> ReadScaledHoldingRegisterAsync(
int registerAddress,
double scale,
CancellationToken cancellationToken)
{
if (!IsConnected || _tcpClient is null)
{
throw new InvalidOperationException("Modbus TCP 未连接。");
}
var stream = _tcpClient.GetStream();
var request = new byte[12];
var transactionId = unchecked(++_transactionId);
BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(0, 2), transactionId);
BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(2, 2), 0);
BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(4, 2), 6);
request[6] = UnitId;
request[7] = ReadHoldingRegistersFunction;
BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(8, 2), checked((ushort)registerAddress));
BinaryPrimitives.WriteUInt16BigEndian(request.AsSpan(10, 2), 1);
await stream.WriteAsync(request, cancellationToken);
var response = new byte[11];
await stream.ReadExactlyAsync(response, cancellationToken);
var responseTransactionId = BinaryPrimitives.ReadUInt16BigEndian(response.AsSpan(0, 2));
if (responseTransactionId != transactionId)
{
throw new InvalidDataException($"Modbus 事务号不匹配,期望 {transactionId},实际 {responseTransactionId}。");
}
if (response[7] == (ReadHoldingRegistersFunction | 0x80))
{
throw new InvalidDataException($"Modbus 异常响应码 {response[8]}。");
}
if (response[7] != ReadHoldingRegistersFunction || response[8] != 2)
{
throw new InvalidDataException("Modbus 响应格式不正确。");
}
var raw = BinaryPrimitives.ReadUInt16BigEndian(response.AsSpan(9, 2));
return raw * scale;
}
public Task SetOutputsAsync(bool pumpRunning, bool fanRunning, bool heaterRunning, CancellationToken cancellationToken = default)

View File

@@ -72,6 +72,8 @@ public sealed class SimulatedDeviceClient : IDeviceClient
var airSpeed = (_fanRunning ? _settings.MethodBAirSpeedMetersPerSecond : 0.05) + Noise(0.03);
var waterLoss = _pumpRunning ? _settings.PumpSpeedCubicCentimetersPerHour + Noise(0.12) : Math.Max(0, Noise(0.03));
var power = _heaterRunning ? 4.2 + Math.Max(0, targetFoot - chamber) * 0.03 + Noise(0.12) : 0.0;
var voltage = _heaterRunning ? 220.0 + Noise(1.5) : 0.0;
var current = voltage > 0 ? power / voltage : 0.0;
_energyKilojoules += power * _settings.PollIntervalMilliseconds / 1_000_000.0;
LastSnapshot = new DeviceSnapshot(
@@ -81,6 +83,8 @@ public sealed class SimulatedDeviceClient : IDeviceClient
Math.Round(humidity, 2),
Math.Round(Math.Max(0, airSpeed), 2),
Math.Round(Math.Max(0, power), 2),
Math.Round(Math.Max(0, voltage), 2),
Math.Round(Math.Max(0, current), 3),
Math.Round(_energyKilojoules, 3),
Math.Round(Math.Max(0, waterLoss), 2),
_settings.PumpSpeedCubicCentimetersPerHour,

View File

@@ -26,6 +26,38 @@ public sealed class TestFormulaService
(VaporizationHeatWhPerGram * nakedFootSweatGramsPerHour);
}
public MethodASampleRecord CalculateSkinMoistureResistanceSample(
int index,
DeviceSnapshot snapshot,
double footAreaSquareMeters,
double skinSaturatedVaporPressurePa = 5620.0,
double environmentSaturatedVaporPressurePa = 2809.0)
{
var resistance = CalculateSkinMoistureResistance(
footAreaSquareMeters,
skinSaturatedVaporPressurePa,
1.0,
environmentSaturatedVaporPressurePa,
snapshot.EnvironmentHumidityPercent / 100.0,
snapshot.WaterLossGramsPerHour);
var he = VaporizationHeatWhPerGram * snapshot.WaterLossGramsPerHour;
return new MethodASampleRecord(
index,
snapshot.Timestamp,
snapshot.FootTemperatureC,
snapshot.EnvironmentTemperatureC,
snapshot.EnvironmentHumidityPercent,
snapshot.WaterLossGramsPerHour,
snapshot.PowerWatts,
snapshot.VoltageVolts,
snapshot.CurrentAmps,
Round(he, 3),
0,
Round(resistance, 3),
0);
}
public MethodASampleRecord CalculateMethodASample(
int index,
DeviceSnapshot snapshot,
@@ -53,6 +85,8 @@ public sealed class TestFormulaService
snapshot.EnvironmentHumidityPercent,
snapshot.WaterLossGramsPerHour,
snapshot.PowerWatts,
snapshot.VoltageVolts,
snapshot.CurrentAmps,
Round(he, 3),
Round(hd, 3),
Round(re, 3),

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.Json;
@@ -12,6 +13,8 @@ namespace FootwearTest.ViewModels;
public partial class MethodAViewModel : ViewModelBase
{
private const int MethodAMinimumSampleCount = 30;
private readonly IDeviceClient _deviceClient;
private readonly DeviceSettings _settings;
private readonly TestFormulaService _formulaService;
@@ -116,28 +119,46 @@ public partial class MethodAViewModel : ViewModelBase
_logger.LogInformation("Method A skin moisture resistance started. SampleDescription={SampleDescription}", SampleDescription);
await EnsureConnectedAsync();
IsRunning = true;
StatusText = "皮肤湿阻测定中";
StatusText = "等待假脚/环境稳定";
Samples.Clear();
await _deviceClient.SetOutputsAsync(true, true, true);
var skinResistanceValues = new double[30];
for (var i = 1; i <= 30; i++)
await WaitForMethodAStabilityAsync();
if (!IsRunning)
{
await Task.Delay(60);
StatusText = "已停止";
return;
}
StatusText = "皮肤湿阻测定中";
while (IsRunning)
{
await Task.Delay(GetMethodASamplingInterval());
if (!IsRunning)
{
break;
}
var snapshot = await _deviceClient.ReadSnapshotAsync();
var value = _formulaService.CalculateSkinMoistureResistance(
_settings.FootAreaSquareMeters,
5620.0,
1.0,
2809.0,
snapshot.EnvironmentHumidityPercent / 100.0,
snapshot.WaterLossGramsPerHour);
skinResistanceValues[i - 1] = value;
SkinMoistureResistance = Round(_formulaService.Average(skinResistanceValues.Take(i)), 3);
var sample = _formulaService.CalculateSkinMoistureResistanceSample(Samples.Count + 1, snapshot, _settings.FootAreaSquareMeters);
Samples.Add(sample);
SkinMoistureResistance = Round(_formulaService.Average(Samples.Select(item => item.MoistureResistance)), 3);
MoistureCvPercent = Round(_formulaService.CoefficientOfVariationPercent(Samples.Select(item => item.MoistureResistance)), 2);
ResultText = $"皮肤湿阻: 已采集 {Samples.Count} 次Res {SkinMoistureResistance:F3} Pa·m²/WCV {MoistureCvPercent:F2}%";
if (HasReachedVariationCoefficient(Samples.Select(item => item.MoistureResistance)))
{
break;
}
}
if (!IsRunning)
{
StatusText = "已停止";
return;
}
await _deviceClient.SetOutputsAsync(false, true, false);
MoistureCvPercent = Round(_formulaService.CoefficientOfVariationPercent(skinResistanceValues), 2);
ThermalCvPercent = 0;
AverageMoistureResistance = 0;
AverageThermalResistance = 0;
@@ -170,16 +191,40 @@ public partial class MethodAViewModel : ViewModelBase
_logger.LogInformation("Method A whole shoe test started. SampleDescription={SampleDescription}", SampleDescription);
await EnsureConnectedAsync();
IsRunning = true;
StatusText = "整鞋热阻/湿阻测定中";
StatusText = "等待假脚/环境稳定";
Samples.Clear();
await _deviceClient.SetOutputsAsync(true, true, true);
for (var i = 1; i <= 30; i++)
await WaitForMethodAStabilityAsync();
if (!IsRunning)
{
await Task.Delay(80);
StatusText = "已停止";
return;
}
StatusText = "整鞋热阻/湿阻测定中";
while (IsRunning)
{
await Task.Delay(GetMethodASamplingInterval());
if (!IsRunning)
{
break;
}
var snapshot = await _deviceClient.ReadSnapshotAsync();
Samples.Add(_formulaService.CalculateMethodASample(i, snapshot, _settings.FootAreaSquareMeters, SkinMoistureResistance));
Samples.Add(_formulaService.CalculateMethodASample(Samples.Count + 1, snapshot, _settings.FootAreaSquareMeters, SkinMoistureResistance));
UpdateMethodAResult("整鞋热阻/湿阻");
if (HasReachedWholeShoeVariationCoefficient())
{
break;
}
}
if (!IsRunning)
{
StatusText = "已停止";
return;
}
await _deviceClient.SetOutputsAsync(false, true, false);
@@ -219,6 +264,52 @@ public partial class MethodAViewModel : ViewModelBase
}
}
private TimeSpan GetMethodASamplingInterval()
{
return _settings.UseSimulator ? TimeSpan.FromMilliseconds(100) : TimeSpan.FromMinutes(1);
}
private TimeSpan GetMethodAStabilityPollInterval()
{
return _settings.UseSimulator ? TimeSpan.FromMilliseconds(100) : TimeSpan.FromSeconds(5);
}
private async Task WaitForMethodAStabilityAsync()
{
while (IsRunning)
{
var snapshot = await _deviceClient.ReadSnapshotAsync();
if (IsMethodAStable(snapshot))
{
return;
}
StatusText = $"等待稳定: 假脚 {snapshot.FootTemperatureC:F1} ℃,环境 {snapshot.EnvironmentTemperatureC:F1} ℃ / {snapshot.EnvironmentHumidityPercent:F0}%";
await Task.Delay(GetMethodAStabilityPollInterval());
}
}
private bool IsMethodAStable(DeviceSnapshot snapshot)
{
return Math.Abs(snapshot.FootTemperatureC - _settings.MethodATargetTemperatureC) <= 0.3 &&
Math.Abs(snapshot.EnvironmentTemperatureC - _settings.EnvironmentTemperatureC) <= 2.0 &&
Math.Abs(snapshot.EnvironmentHumidityPercent - _settings.EnvironmentHumidityPercent) <= 5.0;
}
private bool HasReachedVariationCoefficient(IEnumerable<double> values)
{
var list = values.ToArray();
return list.Length >= MethodAMinimumSampleCount &&
_formulaService.CoefficientOfVariationPercent(list) <= _settings.CoefficientOfVariationLimitPercent;
}
private bool HasReachedWholeShoeVariationCoefficient()
{
return Samples.Count >= MethodAMinimumSampleCount &&
HasReachedVariationCoefficient(Samples.Select(sample => sample.MoistureResistance)) &&
HasReachedVariationCoefficient(Samples.Select(sample => sample.ThermalResistance));
}
private async Task EnsureConnectedAsync()
{
if (!_deviceClient.IsConnected)

View File

@@ -19,6 +19,10 @@ public partial class SettingsViewModel : ViewModelBase
FootAreaSquareMeters = settings.FootAreaSquareMeters;
CoefficientOfVariationLimitPercent = settings.CoefficientOfVariationLimitPercent;
PumpSpeedCubicCentimetersPerHour = settings.PumpSpeedCubicCentimetersPerHour;
VoltageRegisterAddress = settings.VoltageRegisterAddress;
VoltageRegisterScale = settings.VoltageRegisterScale;
CurrentRegisterAddress = settings.CurrentRegisterAddress;
CurrentRegisterScale = settings.CurrentRegisterScale;
SaveCommand = new RelayCommand(Save);
}
@@ -30,6 +34,10 @@ public partial class SettingsViewModel : ViewModelBase
private double _footAreaSquareMeters;
private double _coefficientOfVariationLimitPercent;
private double _pumpSpeedCubicCentimetersPerHour;
private int _voltageRegisterAddress;
private double _voltageRegisterScale;
private int _currentRegisterAddress;
private double _currentRegisterScale;
private string _saveStatus = "参数未保存";
public string Host { get => _host; set => SetProperty(ref _host, value); }
@@ -38,6 +46,10 @@ public partial class SettingsViewModel : ViewModelBase
public double FootAreaSquareMeters { get => _footAreaSquareMeters; set => SetProperty(ref _footAreaSquareMeters, value); }
public double CoefficientOfVariationLimitPercent { get => _coefficientOfVariationLimitPercent; set => SetProperty(ref _coefficientOfVariationLimitPercent, value); }
public double PumpSpeedCubicCentimetersPerHour { get => _pumpSpeedCubicCentimetersPerHour; set => SetProperty(ref _pumpSpeedCubicCentimetersPerHour, value); }
public int VoltageRegisterAddress { get => _voltageRegisterAddress; set => SetProperty(ref _voltageRegisterAddress, value); }
public double VoltageRegisterScale { get => _voltageRegisterScale; set => SetProperty(ref _voltageRegisterScale, value); }
public int CurrentRegisterAddress { get => _currentRegisterAddress; set => SetProperty(ref _currentRegisterAddress, value); }
public double CurrentRegisterScale { get => _currentRegisterScale; set => SetProperty(ref _currentRegisterScale, value); }
public string SaveStatus { get => _saveStatus; set => SetProperty(ref _saveStatus, value); }
private void Save()
@@ -48,13 +60,19 @@ public partial class SettingsViewModel : ViewModelBase
_settings.FootAreaSquareMeters = FootAreaSquareMeters;
_settings.CoefficientOfVariationLimitPercent = CoefficientOfVariationLimitPercent;
_settings.PumpSpeedCubicCentimetersPerHour = PumpSpeedCubicCentimetersPerHour;
_settings.VoltageRegisterAddress = VoltageRegisterAddress;
_settings.VoltageRegisterScale = VoltageRegisterScale;
_settings.CurrentRegisterAddress = CurrentRegisterAddress;
_settings.CurrentRegisterScale = CurrentRegisterScale;
SaveStatus = "参数已保存,新的试验流程将使用当前设置";
_logger.LogInformation(
"Settings saved. UseSimulator={UseSimulator}, Host={Host}, Port={Port}, FootAreaSquareMeters={FootArea}, PumpSpeed={PumpSpeed}",
"Settings saved. UseSimulator={UseSimulator}, Host={Host}, Port={Port}, FootAreaSquareMeters={FootArea}, PumpSpeed={PumpSpeed}, VoltageRegister={VoltageRegister}, CurrentRegister={CurrentRegister}",
UseSimulator,
Host,
Port,
FootAreaSquareMeters,
PumpSpeedCubicCentimetersPerHour);
PumpSpeedCubicCentimetersPerHour,
VoltageRegisterAddress,
CurrentRegisterAddress);
}
}

View File

@@ -30,6 +30,8 @@
<StackPanel>
<TextBlock Text="功率 / 出汗量" Foreground="#5B6775"/>
<TextBlock Text="{Binding Snapshot.PowerWatts, StringFormat='{}{0:F2} W'}" FontSize="28" FontWeight="SemiBold"/>
<TextBlock Text="{Binding Snapshot.VoltageVolts, StringFormat='电压 {0:F1} V'}" Foreground="#5B6775"/>
<TextBlock Text="{Binding Snapshot.CurrentAmps, StringFormat='电流 {0:F3} A'}" Foreground="#5B6775"/>
<TextBlock Text="{Binding Snapshot.WaterLossGramsPerHour, StringFormat='{}{0:F2} g/h'}" Foreground="#5B6775"/>
</StackPanel>
</Border>

View File

@@ -46,34 +46,38 @@
<Grid RowDefinitions="Auto,Auto,*">
<TextBlock Text="连续采样记录" FontSize="18" FontWeight="SemiBold"/>
<ScrollViewer Grid.Row="1" Grid.RowSpan="2" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Margin="0,12,0,0">
<Grid RowDefinitions="Auto,*" MinWidth="820">
<Grid ColumnDefinitions="46,96,82,82,84,82,82,82,98,98" Margin="0,0,0,6">
<Grid RowDefinitions="Auto,*" MinWidth="980">
<Grid ColumnDefinitions="46,96,82,82,84,76,76,82,82,82,98,98" Margin="0,0,0,6">
<TextBlock Text="序号" Foreground="#5B6775"/>
<TextBlock Grid.Column="1" Text="假脚℃" Foreground="#5B6775"/>
<TextBlock Grid.Column="2" Text="环境℃" Foreground="#5B6775"/>
<TextBlock Grid.Column="3" Text="湿度%" Foreground="#5B6775"/>
<TextBlock Grid.Column="4" Text="出汗g/h" Foreground="#5B6775"/>
<TextBlock Grid.Column="5" Text="功率W" Foreground="#5B6775"/>
<TextBlock Grid.Column="6" Text="He W" Foreground="#5B6775"/>
<TextBlock Grid.Column="7" Text="Hd W" Foreground="#5B6775"/>
<TextBlock Grid.Column="8" Text="湿阻" Foreground="#5B6775"/>
<TextBlock Grid.Column="9" Text="热阻" Foreground="#5B6775"/>
<TextBlock Grid.Column="5" Text="电压V" Foreground="#5B6775"/>
<TextBlock Grid.Column="6" Text="电流A" Foreground="#5B6775"/>
<TextBlock Grid.Column="7" Text="功率W" Foreground="#5B6775"/>
<TextBlock Grid.Column="8" Text="He W" Foreground="#5B6775"/>
<TextBlock Grid.Column="9" Text="Hd W" Foreground="#5B6775"/>
<TextBlock Grid.Column="10" Text="湿阻" Foreground="#5B6775"/>
<TextBlock Grid.Column="11" Text="热阻" Foreground="#5B6775"/>
</Grid>
<ScrollViewer Grid.Row="1">
<ItemsControl ItemsSource="{Binding Samples}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="models:MethodASampleRecord">
<Grid ColumnDefinitions="46,96,82,82,84,82,82,82,98,98" Margin="0,0,0,6">
<Grid ColumnDefinitions="46,96,82,82,84,76,76,82,82,82,98,98" Margin="0,0,0,6">
<TextBlock Text="{Binding Index}"/>
<TextBlock Grid.Column="1" Text="{Binding FootTemperatureC, StringFormat='{}{0:F2}'}"/>
<TextBlock Grid.Column="2" Text="{Binding EnvironmentTemperatureC, StringFormat='{}{0:F2}'}"/>
<TextBlock Grid.Column="3" Text="{Binding RelativeHumidityPercent, StringFormat='{}{0:F1}'}"/>
<TextBlock Grid.Column="4" Text="{Binding WaterLossGramsPerHour, StringFormat='{}{0:F2}'}"/>
<TextBlock Grid.Column="5" Text="{Binding PowerWatts, StringFormat='{}{0:F2}'}"/>
<TextBlock Grid.Column="6" Text="{Binding MoistureHeatWatts, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="7" Text="{Binding DryHeatWatts, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="8" Text="{Binding MoistureResistance, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="9" Text="{Binding ThermalResistance, StringFormat='{}{0:F4}'}"/>
<TextBlock Grid.Column="5" Text="{Binding VoltageVolts, StringFormat='{}{0:F2}'}"/>
<TextBlock Grid.Column="6" Text="{Binding CurrentAmps, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="7" Text="{Binding PowerWatts, StringFormat='{}{0:F2}'}"/>
<TextBlock Grid.Column="8" Text="{Binding MoistureHeatWatts, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="9" Text="{Binding DryHeatWatts, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="10" Text="{Binding MoistureResistance, StringFormat='{}{0:F3}'}"/>
<TextBlock Grid.Column="11" Text="{Binding ThermalResistance, StringFormat='{}{0:F4}'}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>

View File

@@ -26,6 +26,14 @@
<TextBox Text="{Binding CoefficientOfVariationLimitPercent}" sukiTheme:TextBoxExtensions.Unit="%"/>
<TextBlock Text="方法 B 泵速 (cm³/h)" Foreground="#5B6775"/>
<TextBox Text="{Binding PumpSpeedCubicCentimetersPerHour}" sukiTheme:TextBoxExtensions.Unit="cm³/h"/>
<TextBlock Text="电压寄存器地址" Foreground="#5B6775"/>
<TextBox Text="{Binding VoltageRegisterAddress}" sukiTheme:TextBoxExtensions.Prefix="D"/>
<TextBlock Text="电压缩放系数" Foreground="#5B6775"/>
<TextBox Text="{Binding VoltageRegisterScale}" sukiTheme:TextBoxExtensions.Unit="V/count"/>
<TextBlock Text="电流寄存器地址" Foreground="#5B6775"/>
<TextBox Text="{Binding CurrentRegisterAddress}" sukiTheme:TextBoxExtensions.Prefix="D"/>
<TextBlock Text="电流缩放系数" Foreground="#5B6775"/>
<TextBox Text="{Binding CurrentRegisterScale}" sukiTheme:TextBoxExtensions.Unit="A/count"/>
<Button Classes="primary" Content="保存参数" Command="{Binding SaveCommand}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding SaveStatus}" Foreground="#256F46"/>
</StackPanel>