更新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 EnvironmentHumidityPercent,
double AirSpeedMetersPerSecond, double AirSpeedMetersPerSecond,
double PowerWatts, double PowerWatts,
double VoltageVolts,
double CurrentAmps,
double EnergyKilojoules, double EnergyKilojoules,
double WaterLossGramsPerHour, double WaterLossGramsPerHour,
double PumpSpeedCubicCentimetersPerHour, double PumpSpeedCubicCentimetersPerHour,
@@ -24,6 +26,8 @@ public sealed record DeviceSnapshot(
50.0, 50.0,
1.10, 1.10,
4.5, 4.5,
220.0,
0.02,
0.0, 0.0,
5.0, 5.0,
5.0, 5.0,

View File

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

View File

@@ -16,4 +16,8 @@ public sealed class DeviceSettings
public double MethodBAirSpeedMetersPerSecond { get; set; } = 1.10; public double MethodBAirSpeedMetersPerSecond { get; set; } = 1.10;
public double PumpSpeedCubicCentimetersPerHour { get; set; } = 5.0; public double PumpSpeedCubicCentimetersPerHour { get; set; } = 5.0;
public double CoefficientOfVariationLimitPercent { get; set; } = 8.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?>> 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) if (root.TryGetProperty("Samples", out var samples) && samples.ValueKind == JsonValueKind.Array)
@@ -169,6 +169,8 @@ public sealed class ExcelReportService
GetDouble(sample, "EnvironmentTemperatureC"), GetDouble(sample, "EnvironmentTemperatureC"),
GetDouble(sample, "RelativeHumidityPercent"), GetDouble(sample, "RelativeHumidityPercent"),
waterLoss, waterLoss,
GetDouble(sample, "VoltageVolts"),
GetDouble(sample, "CurrentAmps"),
power, power,
moistureHeat, moistureHeat,
dryHeat, dryHeat,

View File

@@ -1,4 +1,6 @@
using System; using System;
using System.Buffers.Binary;
using System.IO;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -9,10 +11,14 @@ namespace FootwearTest.Services;
public sealed class ModbusTcpDeviceClient : IDeviceClient public sealed class ModbusTcpDeviceClient : IDeviceClient
{ {
private const byte UnitId = 1;
private const byte ReadHoldingRegistersFunction = 0x03;
private readonly DeviceSettings _settings; private readonly DeviceSettings _settings;
private readonly ILogger<ModbusTcpDeviceClient> _logger; private readonly ILogger<ModbusTcpDeviceClient> _logger;
private TcpClient? _tcpClient; private TcpClient? _tcpClient;
private bool _lastReadFailed; private bool _lastReadFailed;
private ushort _transactionId;
public ModbusTcpDeviceClient(DeviceSettings settings, ILogger<ModbusTcpDeviceClient> logger) public ModbusTcpDeviceClient(DeviceSettings settings, ILogger<ModbusTcpDeviceClient> logger)
{ {
@@ -60,9 +66,38 @@ public sealed class ModbusTcpDeviceClient : IDeviceClient
return Task.CompletedTask; 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); SnapshotUpdated?.Invoke(this, LastSnapshot);
if (_lastReadFailed) if (_lastReadFailed)
{ {
@@ -70,7 +105,65 @@ public sealed class ModbusTcpDeviceClient : IDeviceClient
_lastReadFailed = false; _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) 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 airSpeed = (_fanRunning ? _settings.MethodBAirSpeedMetersPerSecond : 0.05) + Noise(0.03);
var waterLoss = _pumpRunning ? _settings.PumpSpeedCubicCentimetersPerHour + Noise(0.12) : Math.Max(0, 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 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; _energyKilojoules += power * _settings.PollIntervalMilliseconds / 1_000_000.0;
LastSnapshot = new DeviceSnapshot( LastSnapshot = new DeviceSnapshot(
@@ -81,6 +83,8 @@ public sealed class SimulatedDeviceClient : IDeviceClient
Math.Round(humidity, 2), Math.Round(humidity, 2),
Math.Round(Math.Max(0, airSpeed), 2), Math.Round(Math.Max(0, airSpeed), 2),
Math.Round(Math.Max(0, power), 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(_energyKilojoules, 3),
Math.Round(Math.Max(0, waterLoss), 2), Math.Round(Math.Max(0, waterLoss), 2),
_settings.PumpSpeedCubicCentimetersPerHour, _settings.PumpSpeedCubicCentimetersPerHour,

View File

@@ -26,6 +26,38 @@ public sealed class TestFormulaService
(VaporizationHeatWhPerGram * nakedFootSweatGramsPerHour); (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( public MethodASampleRecord CalculateMethodASample(
int index, int index,
DeviceSnapshot snapshot, DeviceSnapshot snapshot,
@@ -53,6 +85,8 @@ public sealed class TestFormulaService
snapshot.EnvironmentHumidityPercent, snapshot.EnvironmentHumidityPercent,
snapshot.WaterLossGramsPerHour, snapshot.WaterLossGramsPerHour,
snapshot.PowerWatts, snapshot.PowerWatts,
snapshot.VoltageVolts,
snapshot.CurrentAmps,
Round(he, 3), Round(he, 3),
Round(hd, 3), Round(hd, 3),
Round(re, 3), Round(re, 3),

View File

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

View File

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

View File

@@ -30,6 +30,8 @@
<StackPanel> <StackPanel>
<TextBlock Text="功率 / 出汗量" Foreground="#5B6775"/> <TextBlock Text="功率 / 出汗量" Foreground="#5B6775"/>
<TextBlock Text="{Binding Snapshot.PowerWatts, StringFormat='{}{0:F2} W'}" FontSize="28" FontWeight="SemiBold"/> <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"/> <TextBlock Text="{Binding Snapshot.WaterLossGramsPerHour, StringFormat='{}{0:F2} g/h'}" Foreground="#5B6775"/>
</StackPanel> </StackPanel>
</Border> </Border>

View File

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

View File

@@ -26,6 +26,14 @@
<TextBox Text="{Binding CoefficientOfVariationLimitPercent}" sukiTheme:TextBoxExtensions.Unit="%"/> <TextBox Text="{Binding CoefficientOfVariationLimitPercent}" sukiTheme:TextBoxExtensions.Unit="%"/>
<TextBlock Text="方法 B 泵速 (cm³/h)" Foreground="#5B6775"/> <TextBlock Text="方法 B 泵速 (cm³/h)" Foreground="#5B6775"/>
<TextBox Text="{Binding PumpSpeedCubicCentimetersPerHour}" sukiTheme:TextBoxExtensions.Unit="cm³/h"/> <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"/> <Button Classes="primary" Content="保存参数" Command="{Binding SaveCommand}" HorizontalAlignment="Left"/>
<TextBlock Text="{Binding SaveStatus}" Foreground="#256F46"/> <TextBlock Text="{Binding SaveStatus}" Foreground="#256F46"/>
</StackPanel> </StackPanel>