更新20260530
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -28,6 +28,8 @@ public sealed record MethodASampleRecord(
|
||||
double RelativeHumidityPercent,
|
||||
double WaterLossGramsPerHour,
|
||||
double PowerWatts,
|
||||
double VoltageVolts,
|
||||
double CurrentAmps,
|
||||
double MoistureHeatWatts,
|
||||
double DryHeatWatts,
|
||||
double MoistureResistance,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,17 +66,104 @@ 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 };
|
||||
SnapshotUpdated?.Invoke(this, LastSnapshot);
|
||||
if (_lastReadFailed)
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Modbus snapshot read recovered. Timestamp={Timestamp}", LastSnapshot.Timestamp);
|
||||
_lastReadFailed = false;
|
||||
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)
|
||||
{
|
||||
_logger.LogInformation("Modbus snapshot read recovered. Timestamp={Timestamp}", LastSnapshot.Timestamp);
|
||||
_lastReadFailed = false;
|
||||
}
|
||||
|
||||
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 未连接。");
|
||||
}
|
||||
|
||||
return Task.FromResult(LastSnapshot);
|
||||
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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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²/W,CV {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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user