数据更新112
This commit is contained in:
@@ -3,6 +3,7 @@ using NModbus;
|
||||
using NModbus.IO;
|
||||
using Serilog;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
@@ -38,10 +39,13 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
private SerialPort? adcPort;
|
||||
private IModbusSerialMaster? plcMaster;
|
||||
private IModbusSerialMaster? adcMaster;
|
||||
private DeviceSettings settings = new("0.00", "0.00", "0.30", "0", "0.00", "0", "0.00", "0", "0.00", "COM7", "COM8", 115200);
|
||||
private DeviceSettings settings = new("0.00", "0.00", "0.30", "0", "0.00", "0", "0.00", "0", "0.00", "COM3", "COM4", 115200);
|
||||
private SlipDeviceSnapshot snapshot = SlipDeviceSnapshot.Offline();
|
||||
private DateTime lastAdcErrorLoggedAt = DateTime.MinValue;
|
||||
private DateTime lastPlcErrorLoggedAt = DateTime.MinValue;
|
||||
private DateTime lastAdcDiagnosticLoggedAt = DateTime.MinValue;
|
||||
private DateTime lastAdcCalibrationWarningLoggedAt = DateTime.MinValue;
|
||||
private string lastAdcCalibrationWarning = string.Empty;
|
||||
|
||||
private double verticalLoadN;
|
||||
private double horizontalFrictionN;
|
||||
@@ -71,6 +75,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
public void Start(DeviceSettings deviceSettings)
|
||||
{
|
||||
settings = deviceSettings;
|
||||
LogAdcCalibrationWarningIfNeeded();
|
||||
Stop();
|
||||
|
||||
try
|
||||
@@ -115,6 +120,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
public void UpdateSettings(DeviceSettings deviceSettings)
|
||||
{
|
||||
settings = deviceSettings;
|
||||
LogAdcCalibrationWarningIfNeeded();
|
||||
Log.Debug(
|
||||
"设备设置已更新:PLC={PlcPort}, ADC={AdcPort}, BaudRate={BaudRate}, TestSpeed={TestSpeed}, ManualSpeed={ManualSpeed}, ManualDisplacement={ManualDisplacement}",
|
||||
settings.PlcPortName,
|
||||
@@ -131,57 +137,23 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
|
||||
public Task PulseResetAsync() => PulseCoilAsync(ResetCoil);
|
||||
|
||||
public async Task StartLiftAsync()
|
||||
public async Task ApplyOldLiftAsync()
|
||||
{
|
||||
await WriteCoilAsync(LowerCoil, false);
|
||||
await WriteCoilAsync(LiftCoil, true);
|
||||
Log.Information("提升按下运行:M{LiftCoil}=1, M{LowerCoil}=0", LiftCoil, LowerCoil);
|
||||
Log.Information("提升按钮按老代码执行:M{LowerCoil}=0, M{LiftCoil}=1", LowerCoil, LiftCoil);
|
||||
}
|
||||
|
||||
public async Task StopLiftAsync()
|
||||
{
|
||||
await WriteCoilAsync(LiftCoil, false);
|
||||
Log.Information("提升松开停止:M{LiftCoil}=0", LiftCoil);
|
||||
}
|
||||
|
||||
public async Task StartLowerAsync()
|
||||
public async Task ApplyOldLowerAsync()
|
||||
{
|
||||
await WriteCoilAsync(LiftCoil, false);
|
||||
await WriteCoilAsync(LowerCoil, true);
|
||||
Log.Information("下降按下运行:M{LowerCoil}=1, M{LiftCoil}=0", LowerCoil, LiftCoil);
|
||||
Log.Information("下降按钮按老代码执行:M{LiftCoil}=0, M{LowerCoil}=1", LiftCoil, LowerCoil);
|
||||
}
|
||||
|
||||
public async Task StopLowerAsync()
|
||||
{
|
||||
await WriteCoilAsync(LowerCoil, false);
|
||||
Log.Information("下降松开停止:M{LowerCoil}=0", LowerCoil);
|
||||
}
|
||||
public Task ToggleOldMoveLeftAsync() => ToggleCoilAsync(MoveLeftCoil);
|
||||
|
||||
public async Task StartMoveLeftAsync()
|
||||
{
|
||||
await WriteCoilAsync(MoveRightCoil, false);
|
||||
await WriteCoilAsync(MoveLeftCoil, true);
|
||||
Log.Information("左移按下运行:M{MoveLeftCoil}=1, M{MoveRightCoil}=0", MoveLeftCoil, MoveRightCoil);
|
||||
}
|
||||
|
||||
public async Task StopMoveLeftAsync()
|
||||
{
|
||||
await WriteCoilAsync(MoveLeftCoil, false);
|
||||
Log.Information("左移松开停止:M{MoveLeftCoil}=0", MoveLeftCoil);
|
||||
}
|
||||
|
||||
public async Task StartMoveRightAsync()
|
||||
{
|
||||
await WriteCoilAsync(MoveLeftCoil, false);
|
||||
await WriteCoilAsync(MoveRightCoil, true);
|
||||
Log.Information("右移按下运行:M{MoveRightCoil}=1, M{MoveLeftCoil}=0", MoveRightCoil, MoveLeftCoil);
|
||||
}
|
||||
|
||||
public async Task StopMoveRightAsync()
|
||||
{
|
||||
await WriteCoilAsync(MoveRightCoil, false);
|
||||
Log.Information("右移松开停止:M{MoveRightCoil}=0", MoveRightCoil);
|
||||
}
|
||||
public Task ToggleOldMoveRightAsync() => ToggleCoilAsync(MoveRightCoil);
|
||||
|
||||
public async Task StopAllMotionAsync()
|
||||
{
|
||||
@@ -332,13 +304,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
var friction1Raw = UshortToInt(data[6], data[7]);
|
||||
var friction2Raw = UshortToInt(data[2], data[3]);
|
||||
|
||||
var pressure = ConvertAdc(pressureRaw, settings.NormalPressureZero, settings.NormalPressureCoefficient);
|
||||
|
||||
// Keep the old instrument channel wiring: ADC 6/7 uses zero 1 with coefficient 2,
|
||||
// and ADC 2/3 uses zero 2 with coefficient 1.
|
||||
var friction1 = ConvertAdc(friction1Raw, settings.FrictionZero1, settings.FrictionCoefficient2);
|
||||
var friction2 = ConvertAdc(friction2Raw, settings.FrictionZero2, settings.FrictionCoefficient1);
|
||||
var friction = (friction1 + friction2) * -1.0;
|
||||
var conversion = ConvertAdcReadings(pressureRaw, friction1Raw, friction2Raw);
|
||||
LogAdcDiagnostic(pressureRaw, friction1Raw, friction2Raw, conversion);
|
||||
|
||||
lock (sync)
|
||||
{
|
||||
@@ -346,14 +313,25 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
frictionRawValue1 = friction1Raw;
|
||||
frictionRawValue2 = friction2Raw;
|
||||
hasAdcRawValues = true;
|
||||
verticalLoadN = pressure;
|
||||
horizontalFrictionN = friction;
|
||||
adcLastError = string.Empty;
|
||||
isAdcConnected = true;
|
||||
if (conversion.IsValid)
|
||||
{
|
||||
verticalLoadN = conversion.Pressure;
|
||||
horizontalFrictionN = conversion.Friction;
|
||||
adcLastError = string.Empty;
|
||||
isAdcConnected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
verticalLoadN = 0;
|
||||
horizontalFrictionN = 0;
|
||||
adcLastError = conversion.Error;
|
||||
isAdcConnected = false;
|
||||
}
|
||||
|
||||
RefreshSnapshotLocked();
|
||||
}
|
||||
|
||||
Thread.Sleep(10);
|
||||
Thread.Sleep(conversion.IsValid ? 10 : 100);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -405,13 +383,19 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PulseCoilAsync(ushort coil)
|
||||
{
|
||||
Log.Information("发送 PLC 脉冲线圈:M{Coil}", coil);
|
||||
await WriteCoilAsync(coil, true);
|
||||
await Task.Delay(80);
|
||||
await WriteCoilAsync(coil, false);
|
||||
}
|
||||
private Task PulseCoilAsync(ushort coil) =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
lock (plcIoLock)
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
Log.Information("按老代码逻辑发送 PLC 脉冲线圈:M{Coil}=true -> false", coil);
|
||||
master.WriteSingleCoil(SlaveId, coil, true);
|
||||
Log.Debug("写入 PLC 线圈:M{Coil}=true", coil);
|
||||
master.WriteSingleCoil(SlaveId, coil, false);
|
||||
Log.Debug("写入 PLC 线圈:M{Coil}=false", coil);
|
||||
}
|
||||
});
|
||||
|
||||
private Task ToggleCoilAsync(ushort coil) =>
|
||||
Task.Run(() =>
|
||||
@@ -532,24 +516,141 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
error);
|
||||
}
|
||||
|
||||
private static double ConvertAdc(int rawValue, string zeroText, string coefficientText)
|
||||
private AdcConversionResult ConvertAdcReadings(int pressureRaw, int friction1Raw, int friction2Raw)
|
||||
{
|
||||
var zero = ParseDouble(zeroText);
|
||||
var coefficient = ParseDouble(coefficientText);
|
||||
if (Math.Abs(coefficient) < 0.0001)
|
||||
if (!ValidateAdcSettings(out var pressureZero, out var pressureCoefficient, out var frictionZero1, out var frictionCoefficient1, out var frictionZero2, out var frictionCoefficient2, out var error))
|
||||
{
|
||||
return 0;
|
||||
return AdcConversionResult.Invalid(error);
|
||||
}
|
||||
|
||||
return (rawValue - zero) / coefficient;
|
||||
var pressure = ConvertAdc(pressureRaw, pressureZero, pressureCoefficient);
|
||||
|
||||
// Keep the old instrument channel wiring: ADC 6/7 uses zero 1 with coefficient 2,
|
||||
// and ADC 2/3 uses zero 2 with coefficient 1.
|
||||
var friction1 = ConvertAdc(friction1Raw, frictionZero1, frictionCoefficient2);
|
||||
var friction2 = ConvertAdc(friction2Raw, frictionZero2, frictionCoefficient1);
|
||||
var friction = (friction1 + friction2) * -1.0;
|
||||
return AdcConversionResult.Valid(pressure, friction1, friction2, friction);
|
||||
}
|
||||
|
||||
private static double ParseDouble(string value) =>
|
||||
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var invariantValue)
|
||||
? invariantValue
|
||||
: double.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out var localValue)
|
||||
? localValue
|
||||
: 0;
|
||||
private bool ValidateAdcSettings(
|
||||
out double pressureZero,
|
||||
out double pressureCoefficient,
|
||||
out double frictionZero1,
|
||||
out double frictionCoefficient1,
|
||||
out double frictionZero2,
|
||||
out double frictionCoefficient2,
|
||||
out string error)
|
||||
{
|
||||
var invalid = new List<string>();
|
||||
TryParseSetting(settings.NormalPressureZero, "正压力零点", false, invalid, out pressureZero);
|
||||
TryParseSetting(settings.NormalPressureCoefficient, "正压力校准系数", true, invalid, out pressureCoefficient);
|
||||
TryParseSetting(settings.FrictionZero1, "摩擦1零点", false, invalid, out frictionZero1);
|
||||
TryParseSetting(settings.FrictionCoefficient1, "摩擦1校准系数", true, invalid, out frictionCoefficient1);
|
||||
TryParseSetting(settings.FrictionZero2, "摩擦2零点", false, invalid, out frictionZero2);
|
||||
TryParseSetting(settings.FrictionCoefficient2, "摩擦2校准系数", true, invalid, out frictionCoefficient2);
|
||||
|
||||
if (invalid.Count == 0)
|
||||
{
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
error = "ADC 校准参数无效:" + string.Join("、", invalid) + ";请填写旧机实际标定值后再测试";
|
||||
return false;
|
||||
}
|
||||
|
||||
private void LogAdcCalibrationWarningIfNeeded()
|
||||
{
|
||||
if (!ValidateAdcSettings(out _, out _, out _, out _, out _, out _, out var error))
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if (error == lastAdcCalibrationWarning && now - lastAdcCalibrationWarningLoggedAt < TimeSpan.FromSeconds(5))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastAdcCalibrationWarning = error;
|
||||
lastAdcCalibrationWarningLoggedAt = now;
|
||||
Log.Warning(
|
||||
"{Error}. CurrentSettings: NormalPressureZero={NormalPressureZero}, NormalPressureCoefficient={NormalPressureCoefficient}, FrictionZero1={FrictionZero1}, FrictionCoefficient1={FrictionCoefficient1}, FrictionZero2={FrictionZero2}, FrictionCoefficient2={FrictionCoefficient2}",
|
||||
error,
|
||||
settings.NormalPressureZero,
|
||||
settings.NormalPressureCoefficient,
|
||||
settings.FrictionZero1,
|
||||
settings.FrictionCoefficient1,
|
||||
settings.FrictionZero2,
|
||||
settings.FrictionCoefficient2);
|
||||
return;
|
||||
}
|
||||
|
||||
lastAdcCalibrationWarning = string.Empty;
|
||||
}
|
||||
|
||||
private void LogAdcDiagnostic(int pressureRaw, int friction1Raw, int friction2Raw, AdcConversionResult conversion)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if (now - lastAdcDiagnosticLoggedAt < TimeSpan.FromSeconds(conversion.IsValid ? 1 : 5))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastAdcDiagnosticLoggedAt = now;
|
||||
if (conversion.IsValid)
|
||||
{
|
||||
Log.Debug(
|
||||
"ADC 采样:RawPressure={RawPressure}, RawFriction1={RawFriction1}, RawFriction2={RawFriction2}, Pressure={Pressure:F3} N, Friction1={Friction1:F3} N, Friction2={Friction2:F3} N, Friction={Friction:F3} N, Coefficients=[P:{PressureCoefficient}, F1:{FrictionCoefficient1}, F2:{FrictionCoefficient2}], Zeros=[P:{PressureZero}, F1:{FrictionZero1}, F2:{FrictionZero2}]",
|
||||
pressureRaw,
|
||||
friction1Raw,
|
||||
friction2Raw,
|
||||
conversion.Pressure,
|
||||
conversion.Friction1,
|
||||
conversion.Friction2,
|
||||
conversion.Friction,
|
||||
settings.NormalPressureCoefficient,
|
||||
settings.FrictionCoefficient1,
|
||||
settings.FrictionCoefficient2,
|
||||
settings.NormalPressureZero,
|
||||
settings.FrictionZero1,
|
||||
settings.FrictionZero2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning(
|
||||
"ADC 采样无效:{Error}. RawPressure={RawPressure}, RawFriction1={RawFriction1}, RawFriction2={RawFriction2}, Coefficients=[P:{PressureCoefficient}, F1:{FrictionCoefficient1}, F2:{FrictionCoefficient2}], Zeros=[P:{PressureZero}, F1:{FrictionZero1}, F2:{FrictionZero2}]",
|
||||
conversion.Error,
|
||||
pressureRaw,
|
||||
friction1Raw,
|
||||
friction2Raw,
|
||||
settings.NormalPressureCoefficient,
|
||||
settings.FrictionCoefficient1,
|
||||
settings.FrictionCoefficient2,
|
||||
settings.NormalPressureZero,
|
||||
settings.FrictionZero1,
|
||||
settings.FrictionZero2);
|
||||
}
|
||||
}
|
||||
|
||||
private static double ConvertAdc(int rawValue, double zero, double coefficient) =>
|
||||
(rawValue - zero) / coefficient;
|
||||
|
||||
private static void TryParseSetting(string value, string label, bool requireNonZero, List<string> invalid, out double numericValue)
|
||||
{
|
||||
if (!TryParseDouble(value, out numericValue))
|
||||
{
|
||||
invalid.Add($"{label}=\"{value}\"");
|
||||
return;
|
||||
}
|
||||
|
||||
if (requireNonZero && Math.Abs(numericValue) < 0.0001)
|
||||
{
|
||||
invalid.Add($"{label}=0");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseDouble(string value, out double numericValue) =>
|
||||
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out numericValue)
|
||||
|| double.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out numericValue);
|
||||
|
||||
private static int UshortToInt(ushort first, ushort second)
|
||||
{
|
||||
@@ -605,5 +706,20 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private readonly record struct AdcConversionResult(
|
||||
bool IsValid,
|
||||
double Pressure,
|
||||
double Friction1,
|
||||
double Friction2,
|
||||
double Friction,
|
||||
string Error)
|
||||
{
|
||||
public static AdcConversionResult Valid(double pressure, double friction1, double friction2, double friction) =>
|
||||
new(true, pressure, friction1, friction2, friction, string.Empty);
|
||||
|
||||
public static AdcConversionResult Invalid(string error) =>
|
||||
new(false, 0, 0, 0, 0, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private const double DynamicWindowEndSeconds = 0.6;
|
||||
private const int MinimumDynamicWindowPointCount = 10;
|
||||
private const int StandardTrialCount = 3;
|
||||
private const string DefaultPlcPortName = "COM3";
|
||||
private const string DefaultAdcPortName = "COM4";
|
||||
private const string LegacyPlcPortName = "COM7";
|
||||
private const string LegacyAdcPortName = "COM8";
|
||||
|
||||
private readonly SlipResistanceDeviceService deviceService = new();
|
||||
private readonly SlipExcelExportService excelExportService = new();
|
||||
@@ -41,6 +45,9 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
|
||||
private bool isLoadingDeviceSettings;
|
||||
private bool wasRunning;
|
||||
private string activePlcPortName = string.Empty;
|
||||
private string activeAdcPortName = string.Empty;
|
||||
private int activeBaudRate;
|
||||
private List<SlipDataPoint> lastCompletedRun = [];
|
||||
|
||||
[ObservableProperty]
|
||||
@@ -101,10 +108,10 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private string frictionCoefficient2 = "0.00";
|
||||
|
||||
[ObservableProperty]
|
||||
private string plcPortName = "COM7";
|
||||
private string plcPortName = DefaultPlcPortName;
|
||||
|
||||
[ObservableProperty]
|
||||
private string adcPortName = "COM8";
|
||||
private string adcPortName = DefaultAdcPortName;
|
||||
|
||||
[ObservableProperty]
|
||||
private int baudRate = 115200;
|
||||
@@ -136,6 +143,12 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
[ObservableProperty]
|
||||
private string resultSummary = "等待 3 次有效试验";
|
||||
|
||||
[ObservableProperty]
|
||||
private string testButtonText = "测试";
|
||||
|
||||
[ObservableProperty]
|
||||
private string resetButtonText = "复位";
|
||||
|
||||
[ObservableProperty]
|
||||
private string staticCoefficient = "0.00";
|
||||
|
||||
@@ -233,7 +246,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
|
||||
LoadDeviceSettings();
|
||||
UpdateTargetLoad();
|
||||
deviceService.Start(CurrentSettings());
|
||||
RestartDeviceConnection();
|
||||
_ = LoadPlcParametersAsync();
|
||||
|
||||
refreshTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(20) };
|
||||
@@ -244,7 +257,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
[RelayCommand]
|
||||
private async Task Clear()
|
||||
{
|
||||
await RunDeviceCommand(deviceService.PulseResetAsync(), "已发送复位指令 M90");
|
||||
ApplyConnectionSettings();
|
||||
await RunDeviceCommand(deviceService.PulseResetAsync(), "已按老代码逻辑发送复位指令 M90");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -256,8 +270,20 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
[RelayCommand]
|
||||
private async Task StartTest()
|
||||
{
|
||||
BeginRun();
|
||||
await RunDeviceCommand(deviceService.PulseStartTestAsync(), "已发送测试启动指令 M80,等待 M81 运行状态");
|
||||
ApplyConnectionSettings();
|
||||
var device = deviceService.CurrentSnapshot;
|
||||
if (device.IsTestRunning)
|
||||
{
|
||||
await RunDeviceCommand(deviceService.PulseStopTestAsync(), "已按老代码逻辑发送停止指令 M83");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateAdcCoefficientsBeforeTest())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await RunDeviceCommand(deviceService.PulseStartTestAsync(), "已按老代码逻辑发送测试启动指令 M80,等待 M81 运行状态");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -307,21 +333,17 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartLiftMotionAsync() => RunDeviceCommand(deviceService.StartLiftAsync(), "垂直架提升中,松开停止");
|
||||
[RelayCommand]
|
||||
private Task LiftMotion() => RunDeviceCommand(deviceService.ApplyOldLiftAsync(), "提升按老代码逻辑执行:M4=0, M5=1");
|
||||
|
||||
public Task StopLiftMotionAsync() => RunDeviceCommand(deviceService.StopLiftAsync(), "垂直架提升已停止");
|
||||
[RelayCommand]
|
||||
private Task LowerMotion() => RunDeviceCommand(deviceService.ApplyOldLowerAsync(), "下降按老代码逻辑执行:M5=0, M4=1");
|
||||
|
||||
public Task StartLowerMotionAsync() => RunDeviceCommand(deviceService.StartLowerAsync(), "垂直架下降中,松开停止");
|
||||
[RelayCommand]
|
||||
private Task MoveLeftMotion() => RunDeviceCommand(deviceService.ToggleOldMoveLeftAsync(), "左移按老代码逻辑切换 M1");
|
||||
|
||||
public Task StopLowerMotionAsync() => RunDeviceCommand(deviceService.StopLowerAsync(), "垂直架下降已停止");
|
||||
|
||||
public Task StartMoveLeftMotionAsync() => RunDeviceCommand(deviceService.StartMoveLeftAsync(), "水平板左移中,松开停止");
|
||||
|
||||
public Task StopMoveLeftMotionAsync() => RunDeviceCommand(deviceService.StopMoveLeftAsync(), "水平板左移已停止");
|
||||
|
||||
public Task StartMoveRightMotionAsync() => RunDeviceCommand(deviceService.StartMoveRightAsync(), "水平板右移中,松开停止");
|
||||
|
||||
public Task StopMoveRightMotionAsync() => RunDeviceCommand(deviceService.StopMoveRightAsync(), "水平板右移已停止");
|
||||
[RelayCommand]
|
||||
private Task MoveRightMotion() => RunDeviceCommand(deviceService.ToggleOldMoveRightAsync(), "右移按老代码逻辑切换 M2");
|
||||
|
||||
public Task StopAllMotionAsync() => RunDeviceCommand(deviceService.StopAllMotionAsync(), "全部运动已停止");
|
||||
|
||||
@@ -350,7 +372,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private void CloseSettingsDialog()
|
||||
{
|
||||
IsSettingsDialogOpen = false;
|
||||
deviceService.UpdateSettings(CurrentSettings());
|
||||
ApplyConnectionSettings();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -452,19 +474,34 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private void RefreshFromDevice()
|
||||
{
|
||||
var device = deviceService.CurrentSnapshot;
|
||||
DeviceStatus = device.IsConnected
|
||||
? device.IsTestRunning ? "联机 / 测试中" : device.IsResetting ? "联机 / 复位中" : "联机 / 待机"
|
||||
: "离线";
|
||||
|
||||
VerticalPressure = device.VerticalLoadN.ToString("F1", CultureInfo.InvariantCulture);
|
||||
HorizontalForce = device.HorizontalFrictionN.ToString("F1", CultureInfo.InvariantCulture);
|
||||
FrictionCoefficient = device.FrictionCoefficient.ToString("F3", CultureInfo.InvariantCulture);
|
||||
Distance = device.DisplacementMm.ToString("F1", CultureInfo.InvariantCulture);
|
||||
ActualLoadText = $"{VerticalPressure} N";
|
||||
if (device.IsConnected)
|
||||
{
|
||||
DeviceStatus = device.IsTestRunning ? "联机 / 测试中" : device.IsResetting ? "联机 / 复位中" : "联机 / 待机";
|
||||
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
||||
ResetButtonText = device.IsResetting ? "复位中" : "复位";
|
||||
VerticalPressure = device.VerticalLoadN.ToString("F1", CultureInfo.InvariantCulture);
|
||||
HorizontalForce = device.HorizontalFrictionN.ToString("F1", CultureInfo.InvariantCulture);
|
||||
FrictionCoefficient = device.FrictionCoefficient.ToString("F3", CultureInfo.InvariantCulture);
|
||||
Distance = device.DisplacementMm.ToString("F1", CultureInfo.InvariantCulture);
|
||||
ActualLoadText = $"{VerticalPressure} N";
|
||||
}
|
||||
else
|
||||
{
|
||||
DeviceStatus = IsAdcCalibrationError(device.LastError) ? "数据无效" : "离线";
|
||||
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
||||
ResetButtonText = device.IsResetting ? "复位中" : "复位";
|
||||
VerticalPressure = "--";
|
||||
HorizontalForce = "--";
|
||||
FrictionCoefficient = "--";
|
||||
Distance = "--";
|
||||
ActualLoadText = "-- N";
|
||||
}
|
||||
|
||||
if (!device.IsConnected && !string.IsNullOrWhiteSpace(device.LastError))
|
||||
{
|
||||
CurrentStatus = $"设备离线:{device.LastError}";
|
||||
CurrentStatus = IsAdcCalibrationError(device.LastError)
|
||||
? $"数据无效:{device.LastError}"
|
||||
: $"设备离线:{device.LastError}";
|
||||
}
|
||||
|
||||
var isRecording = device.IsConnected && device.IsTestRunning;
|
||||
@@ -694,6 +731,54 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
deviceService.UpdateSettings(CurrentSettings());
|
||||
}
|
||||
|
||||
private void ApplyConnectionSettings()
|
||||
{
|
||||
var settings = CurrentSettings();
|
||||
if (ConnectionSettingsChanged(settings))
|
||||
{
|
||||
RestartDeviceConnection(settings);
|
||||
return;
|
||||
}
|
||||
|
||||
deviceService.UpdateSettings(settings);
|
||||
}
|
||||
|
||||
private bool ConnectionSettingsChanged(DeviceSettings settings) =>
|
||||
!string.Equals(activePlcPortName, settings.PlcPortName, StringComparison.OrdinalIgnoreCase)
|
||||
|| !string.Equals(activeAdcPortName, settings.AdcPortName, StringComparison.OrdinalIgnoreCase)
|
||||
|| activeBaudRate != settings.BaudRate;
|
||||
|
||||
private void RestartDeviceConnection()
|
||||
{
|
||||
RestartDeviceConnection(CurrentSettings());
|
||||
}
|
||||
|
||||
private void RestartDeviceConnection(DeviceSettings settings)
|
||||
{
|
||||
deviceService.Start(settings);
|
||||
activePlcPortName = settings.PlcPortName;
|
||||
activeAdcPortName = settings.AdcPortName;
|
||||
activeBaudRate = settings.BaudRate;
|
||||
}
|
||||
|
||||
private bool ValidateAdcCoefficientsBeforeTest()
|
||||
{
|
||||
var invalid = new List<string>();
|
||||
AddInvalidCoefficient(NormalPressureCoefficient, "正压力校准系数", invalid);
|
||||
AddInvalidCoefficient(FrictionCoefficient1, "摩擦1校准系数", invalid);
|
||||
AddInvalidCoefficient(FrictionCoefficient2, "摩擦2校准系数", invalid);
|
||||
|
||||
if (invalid.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var message = "ADC 校准系数无效:" + string.Join("、", invalid) + ";请填写旧机实际标定系数后再开始测试";
|
||||
Log.Warning("阻止测试启动:{Message}", message);
|
||||
CurrentStatus = message;
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task LoadPlcParametersAsync()
|
||||
{
|
||||
try
|
||||
@@ -739,8 +824,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
FrictionCoefficient1 = settings.FrictionCoefficient1 ?? FrictionCoefficient1;
|
||||
FrictionZero2 = settings.FrictionZero2 ?? FrictionZero2;
|
||||
FrictionCoefficient2 = settings.FrictionCoefficient2 ?? FrictionCoefficient2;
|
||||
PlcPortName = settings.PlcPortName ?? PlcPortName;
|
||||
AdcPortName = settings.AdcPortName ?? AdcPortName;
|
||||
PlcPortName = NormalizeSavedPort(settings.PlcPortName, DefaultPlcPortName, LegacyPlcPortName);
|
||||
AdcPortName = NormalizeSavedPort(settings.AdcPortName, DefaultAdcPortName, LegacyAdcPortName);
|
||||
BaudRate = settings.BaudRate > 0 ? settings.BaudRate : BaudRate;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -831,6 +916,27 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out numericValue)
|
||||
|| double.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out numericValue);
|
||||
|
||||
private static void AddInvalidCoefficient(string value, string label, List<string> invalid)
|
||||
{
|
||||
if (!TryParseDouble(value, out var coefficient) || Math.Abs(coefficient) < 0.0001)
|
||||
{
|
||||
invalid.Add($"{label}={value}");
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAdcCalibrationError(string error) =>
|
||||
error.Contains("ADC 校准参数无效", StringComparison.Ordinal);
|
||||
|
||||
private static string NormalizeSavedPort(string? savedPort, string defaultPort, string legacyPort)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(savedPort) || string.Equals(savedPort, legacyPort, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return defaultPort;
|
||||
}
|
||||
|
||||
return savedPort;
|
||||
}
|
||||
|
||||
private static LineSeries<ObservablePoint> CreateLineSeries(
|
||||
string name,
|
||||
ObservableCollection<ObservablePoint> values,
|
||||
|
||||
@@ -325,8 +325,8 @@
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,*" RowDefinitions="Auto,Auto" ColumnSpacing="10" RowSpacing="10">
|
||||
<Button Content="复位" Classes="action" Command="{Binding ClearCommand}"/>
|
||||
<Button Grid.Column="1" Content="测试" Classes="success action" Command="{Binding StartTestCommand}"/>
|
||||
<Button Content="{Binding ResetButtonText}" Classes="action" Command="{Binding ClearCommand}"/>
|
||||
<Button Grid.Column="1" Content="{Binding TestButtonText}" Classes="success action" Command="{Binding StartTestCommand}"/>
|
||||
<Button Grid.Row="1" Content="停止" Classes="danger action" Command="{Binding StopTestCommand}"/>
|
||||
<Button Grid.Row="1" Grid.Column="1" Content="导出报告" Classes="primary action" Command="{Binding ExportReportCommand}"/>
|
||||
</Grid>
|
||||
@@ -336,17 +336,11 @@
|
||||
<Grid ColumnDefinitions="*,*" ColumnSpacing="8">
|
||||
<Button Content="提升"
|
||||
Classes="motion compact"
|
||||
Tag="Lift"
|
||||
PointerPressed="MotionButton_PointerPressed"
|
||||
PointerReleased="MotionButton_PointerReleased"
|
||||
PointerCaptureLost="MotionButton_PointerCaptureLost"/>
|
||||
Command="{Binding LiftMotionCommand}"/>
|
||||
<Button Grid.Column="1"
|
||||
Content="下降"
|
||||
Classes="motion compact"
|
||||
Tag="Lower"
|
||||
PointerPressed="MotionButton_PointerPressed"
|
||||
PointerReleased="MotionButton_PointerReleased"
|
||||
PointerCaptureLost="MotionButton_PointerCaptureLost"/>
|
||||
Command="{Binding LowerMotionCommand}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
@@ -355,17 +349,11 @@
|
||||
<Grid ColumnDefinitions="*,*" ColumnSpacing="8">
|
||||
<Button Content="左移"
|
||||
Classes="motion compact"
|
||||
Tag="MoveLeft"
|
||||
PointerPressed="MotionButton_PointerPressed"
|
||||
PointerReleased="MotionButton_PointerReleased"
|
||||
PointerCaptureLost="MotionButton_PointerCaptureLost"/>
|
||||
Command="{Binding MoveLeftMotionCommand}"/>
|
||||
<Button Grid.Column="1"
|
||||
Content="右移"
|
||||
Classes="motion compact"
|
||||
Tag="MoveRight"
|
||||
PointerPressed="MotionButton_PointerPressed"
|
||||
PointerReleased="MotionButton_PointerReleased"
|
||||
PointerCaptureLost="MotionButton_PointerCaptureLost"/>
|
||||
Command="{Binding MoveRightMotionCommand}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -1,111 +1,23 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModels;
|
||||
using SukiUI.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Views
|
||||
{
|
||||
public partial class MainWindow : SukiWindow
|
||||
{
|
||||
private readonly HashSet<string> activeMotionTags = [];
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
Closed += OnClosed;
|
||||
Deactivated += OnDeactivated;
|
||||
}
|
||||
|
||||
private async void MotionButton_PointerPressed(object? sender, PointerPressedEventArgs e)
|
||||
private void OnClosed(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is not Button button || button.Tag is not string tag || DataContext is not MainWindowViewModel viewModel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!activeMotionTags.Add(tag))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Pointer.Capture(button);
|
||||
e.Handled = true;
|
||||
await StartMotionAsync(viewModel, tag);
|
||||
}
|
||||
|
||||
private async void MotionButton_PointerReleased(object? sender, PointerReleasedEventArgs e)
|
||||
{
|
||||
if (sender is not Button button || button.Tag is not string tag || DataContext is not MainWindowViewModel viewModel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
e.Pointer.Capture(null);
|
||||
e.Handled = true;
|
||||
await StopMotionAsync(viewModel, tag);
|
||||
}
|
||||
|
||||
private async void MotionButton_PointerCaptureLost(object? sender, PointerCaptureLostEventArgs e)
|
||||
{
|
||||
if (sender is not Button button || button.Tag is not string tag || DataContext is not MainWindowViewModel viewModel)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await StopMotionAsync(viewModel, tag);
|
||||
}
|
||||
|
||||
private async void OnDeactivated(object? sender, EventArgs e)
|
||||
{
|
||||
await StopAllMotionAsync();
|
||||
}
|
||||
|
||||
private async void OnClosed(object? sender, EventArgs e)
|
||||
{
|
||||
await StopAllMotionAsync();
|
||||
if (DataContext is MainWindowViewModel viewModel)
|
||||
{
|
||||
viewModel.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static Task StartMotionAsync(MainWindowViewModel viewModel, string tag) =>
|
||||
tag switch
|
||||
{
|
||||
"Lift" => viewModel.StartLiftMotionAsync(),
|
||||
"Lower" => viewModel.StartLowerMotionAsync(),
|
||||
"MoveLeft" => viewModel.StartMoveLeftMotionAsync(),
|
||||
"MoveRight" => viewModel.StartMoveRightMotionAsync(),
|
||||
_ => Task.CompletedTask
|
||||
};
|
||||
|
||||
private async Task StopMotionAsync(MainWindowViewModel viewModel, string tag)
|
||||
{
|
||||
if (!activeMotionTags.Remove(tag))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await (tag switch
|
||||
{
|
||||
"Lift" => viewModel.StopLiftMotionAsync(),
|
||||
"Lower" => viewModel.StopLowerMotionAsync(),
|
||||
"MoveLeft" => viewModel.StopMoveLeftMotionAsync(),
|
||||
"MoveRight" => viewModel.StopMoveRightMotionAsync(),
|
||||
_ => Task.CompletedTask
|
||||
});
|
||||
}
|
||||
|
||||
private async Task StopAllMotionAsync()
|
||||
{
|
||||
activeMotionTags.Clear();
|
||||
if (DataContext is MainWindowViewModel viewModel)
|
||||
{
|
||||
await viewModel.StopAllMotionAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user