更新20260602版本
This commit is contained in:
@@ -7,6 +7,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using A = DocumentFormat.OpenXml.Drawing;
|
||||
using C = DocumentFormat.OpenXml.Drawing.Charts;
|
||||
using Xdr = DocumentFormat.OpenXml.Drawing.Spreadsheet;
|
||||
@@ -18,6 +19,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
private const string InfoSheetName = "试验信息";
|
||||
private const string ResultSheetName = "结果汇总";
|
||||
private const string DataSheetName = "实时数据";
|
||||
private const int StandardTrialCount = 3;
|
||||
|
||||
public string Export(SlipReportExport report)
|
||||
{
|
||||
@@ -100,12 +102,26 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
{
|
||||
result.Index,
|
||||
result.Time,
|
||||
result.StaticCoefficientValue,
|
||||
result.DynamicCoefficientValue,
|
||||
result.StaticCoefficient,
|
||||
result.DynamicCoefficient,
|
||||
result.Verdict
|
||||
});
|
||||
}
|
||||
|
||||
var latest = results.Take(StandardTrialCount).ToList();
|
||||
if (latest.Count == StandardTrialCount)
|
||||
{
|
||||
rows.Add(new object?[] { string.Empty, string.Empty, string.Empty, string.Empty, string.Empty });
|
||||
rows.Add(new object?[]
|
||||
{
|
||||
"近3次平均",
|
||||
string.Empty,
|
||||
latest.Average(result => result.StaticCoefficientValue).ToString("F2", CultureInfo.InvariantCulture),
|
||||
latest.Average(result => result.DynamicCoefficientValue).ToString("F2", CultureInfo.InvariantCulture),
|
||||
"GB/T 3903.6-2024 8.2"
|
||||
});
|
||||
}
|
||||
|
||||
AddWorksheet(workbookPart, sheets, sheetId, ResultSheetName, rows, "A", "E");
|
||||
}
|
||||
|
||||
@@ -264,23 +280,30 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
{
|
||||
const uint xAxisId = 48650112U;
|
||||
const uint yAxisId = 48672768U;
|
||||
const uint coefficientAxisId = 48689152U;
|
||||
|
||||
var scatterChart = new C.ScatterChart(
|
||||
var forceChart = new C.ScatterChart(
|
||||
new C.ScatterStyle { Val = C.ScatterStyleValues.LineMarker },
|
||||
CreateSeries(0, "正压力(N)", 2, lastRow),
|
||||
CreateSeries(1, "摩擦力(N)", 3, lastRow),
|
||||
CreateSeries(2, "位移量(mm)", 4, lastRow),
|
||||
CreateSeries(3, "摩擦系数", 5, lastRow),
|
||||
new C.AxisId { Val = xAxisId },
|
||||
new C.AxisId { Val = yAxisId });
|
||||
var coefficientChart = new C.ScatterChart(
|
||||
new C.ScatterStyle { Val = C.ScatterStyleValues.LineMarker },
|
||||
CreateSeries(3, "摩擦系数", 5, lastRow),
|
||||
new C.AxisId { Val = xAxisId },
|
||||
new C.AxisId { Val = coefficientAxisId });
|
||||
|
||||
var chart = new C.Chart(
|
||||
CreateTitle("防滑性能实时曲线"),
|
||||
new C.PlotArea(
|
||||
new C.Layout(),
|
||||
scatterChart,
|
||||
forceChart,
|
||||
coefficientChart,
|
||||
CreateValueAxis(xAxisId, yAxisId, C.AxisPositionValues.Bottom, "时间(s)"),
|
||||
CreateValueAxis(yAxisId, xAxisId, C.AxisPositionValues.Left, "载荷 / 摩擦力 / 位移 / 系数")),
|
||||
CreateValueAxis(yAxisId, xAxisId, C.AxisPositionValues.Left, "载荷 / 摩擦力 / 位移"),
|
||||
CreateValueAxis(coefficientAxisId, xAxisId, C.AxisPositionValues.Right, "摩擦系数")),
|
||||
new C.Legend(
|
||||
new C.LegendPosition { Val = C.LegendPositionValues.Bottom },
|
||||
new C.Layout()),
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
private const ushort ManualDisplacementRegister = 320;
|
||||
|
||||
private readonly object sync = new();
|
||||
private readonly object plcIoLock = new();
|
||||
private readonly object adcIoLock = new();
|
||||
private readonly ModbusFactory modbusFactory = new();
|
||||
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
@@ -50,8 +52,10 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
private bool hasAdcRawValues;
|
||||
private bool isTestRunning;
|
||||
private bool isResetting;
|
||||
private bool isConnected;
|
||||
private string lastError = string.Empty;
|
||||
private bool isAdcConnected;
|
||||
private bool isPlcConnected;
|
||||
private string adcLastError = string.Empty;
|
||||
private string plcLastError = string.Empty;
|
||||
|
||||
public SlipDeviceSnapshot CurrentSnapshot
|
||||
{
|
||||
@@ -90,7 +94,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
|
||||
cancellationTokenSource = new CancellationTokenSource();
|
||||
var token = cancellationTokenSource.Token;
|
||||
SetConnected(true, string.Empty);
|
||||
SetAllDisconnected(string.Empty);
|
||||
adcTask = Task.Run(() => PollAdc(token), token);
|
||||
plcTask = Task.Run(() => PollPlc(token), token);
|
||||
Log.Information("防滑测试设备连接成功,ADC/PLC 轮询已启动");
|
||||
@@ -103,7 +107,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
settings.PlcPortName,
|
||||
settings.AdcPortName,
|
||||
settings.BaudRate);
|
||||
SetConnected(false, ex.Message);
|
||||
SetAllDisconnected(ex.Message);
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
@@ -197,10 +201,17 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
public Task<PlcDeviceParameters> ReadDeviceParametersAsync() =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
var manualSpeedWords = master.ReadHoldingRegisters(SlaveId, ManualSpeedRegister, 2);
|
||||
var manualDisplacementWords = master.ReadHoldingRegisters(SlaveId, ManualDisplacementRegister, 2);
|
||||
var testSpeedWords = master.ReadHoldingRegisters(SlaveId, TestSpeedRegister, 2);
|
||||
ushort[] manualSpeedWords;
|
||||
ushort[] manualDisplacementWords;
|
||||
ushort[] testSpeedWords;
|
||||
lock (plcIoLock)
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
manualSpeedWords = master.ReadHoldingRegisters(SlaveId, ManualSpeedRegister, 2);
|
||||
manualDisplacementWords = master.ReadHoldingRegisters(SlaveId, ManualDisplacementRegister, 2);
|
||||
testSpeedWords = master.ReadHoldingRegisters(SlaveId, TestSpeedRegister, 2);
|
||||
}
|
||||
|
||||
var parameters = new PlcDeviceParameters(
|
||||
UshortToFloat(manualSpeedWords[1], manualSpeedWords[0]),
|
||||
UshortToFloat(manualDisplacementWords[1], manualDisplacementWords[0]),
|
||||
@@ -263,6 +274,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
ClosePort(adcPort);
|
||||
plcPort = null;
|
||||
adcPort = null;
|
||||
SetAllDisconnected(string.Empty);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -304,13 +316,18 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var master = adcMaster;
|
||||
if (master is null)
|
||||
ushort[] data;
|
||||
lock (adcIoLock)
|
||||
{
|
||||
return;
|
||||
var master = adcMaster;
|
||||
if (master is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
data = master.ReadHoldingRegisters(SlaveId, 0, 8);
|
||||
}
|
||||
|
||||
var data = master.ReadHoldingRegisters(SlaveId, 0, 8);
|
||||
var pressureRaw = UshortToInt(data[0], data[1]);
|
||||
var friction1Raw = UshortToInt(data[6], data[7]);
|
||||
var friction2Raw = UshortToInt(data[2], data[3]);
|
||||
@@ -331,8 +348,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
hasAdcRawValues = true;
|
||||
verticalLoadN = pressure;
|
||||
horizontalFrictionN = friction;
|
||||
lastError = string.Empty;
|
||||
isConnected = true;
|
||||
adcLastError = string.Empty;
|
||||
isAdcConnected = true;
|
||||
RefreshSnapshotLocked();
|
||||
}
|
||||
|
||||
@@ -341,7 +358,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogPollingError("ADC", ex, ref lastAdcErrorLoggedAt);
|
||||
SetConnected(false, ex.Message);
|
||||
SetAdcConnected(false, ex.Message);
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
}
|
||||
@@ -353,22 +370,27 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
var master = plcMaster;
|
||||
if (master is null)
|
||||
ushort[] displacementWords;
|
||||
bool[] coils;
|
||||
lock (plcIoLock)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var master = plcMaster;
|
||||
if (master is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var displacementWords = master.ReadHoldingRegisters(SlaveId, PlcDisplacementRegister, 2);
|
||||
var coils = master.ReadCoils(SlaveId, PlcStateCoilStart, 10);
|
||||
displacementWords = master.ReadHoldingRegisters(SlaveId, PlcDisplacementRegister, 2);
|
||||
coils = master.ReadCoils(SlaveId, PlcStateCoilStart, 10);
|
||||
}
|
||||
|
||||
lock (sync)
|
||||
{
|
||||
displacementMm = UshortToFloat(displacementWords[1], displacementWords[0]);
|
||||
isTestRunning = coils.Length > 0 && coils[0];
|
||||
isResetting = coils.Length > 9 && coils[9];
|
||||
lastError = string.Empty;
|
||||
isConnected = true;
|
||||
plcLastError = string.Empty;
|
||||
isPlcConnected = true;
|
||||
RefreshSnapshotLocked();
|
||||
}
|
||||
|
||||
@@ -377,7 +399,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogPollingError("PLC", ex, ref lastPlcErrorLoggedAt);
|
||||
SetConnected(false, ex.Message);
|
||||
SetPlcConnected(false, ex.Message);
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
}
|
||||
@@ -394,26 +416,35 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
private Task ToggleCoilAsync(ushort coil) =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
var current = master.ReadCoils(SlaveId, coil, 1)[0];
|
||||
master.WriteSingleCoil(SlaveId, coil, !current);
|
||||
Log.Information("切换 PLC 线圈:M{Coil}, Before={Before}, After={After}", coil, current, !current);
|
||||
lock (plcIoLock)
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
var current = master.ReadCoils(SlaveId, coil, 1)[0];
|
||||
master.WriteSingleCoil(SlaveId, coil, !current);
|
||||
Log.Information("切换 PLC 线圈:M{Coil}, Before={Before}, After={After}", coil, current, !current);
|
||||
}
|
||||
});
|
||||
|
||||
private Task WriteCoilAsync(ushort coil, bool value) =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
master.WriteSingleCoil(SlaveId, coil, value);
|
||||
Log.Debug("写入 PLC 线圈:M{Coil}={Value}", coil, value);
|
||||
lock (plcIoLock)
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
master.WriteSingleCoil(SlaveId, coil, value);
|
||||
Log.Debug("写入 PLC 线圈:M{Coil}={Value}", coil, value);
|
||||
}
|
||||
});
|
||||
|
||||
private Task WriteFloatRegisterAsync(ushort register, double value) =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
master.WriteMultipleRegisters(SlaveId, register, SplitFloatToUShortArray((float)value));
|
||||
Log.Information("写入 PLC 浮点寄存器:D{Register}={Value:F3}", register, value);
|
||||
lock (plcIoLock)
|
||||
{
|
||||
var master = plcMaster ?? throw new InvalidOperationException("PLC 未连接");
|
||||
master.WriteMultipleRegisters(SlaveId, register, SplitFloatToUShortArray((float)value));
|
||||
Log.Information("写入 PLC 浮点寄存器:D{Register}={Value:F3}", register, value);
|
||||
}
|
||||
});
|
||||
|
||||
private void LogPollingError(string source, Exception exception, ref DateTime lastLoggedAt)
|
||||
@@ -434,18 +465,62 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
settings.BaudRate);
|
||||
}
|
||||
|
||||
private void SetConnected(bool connected, string error)
|
||||
private void SetAdcConnected(bool connected, string error)
|
||||
{
|
||||
lock (sync)
|
||||
{
|
||||
isConnected = connected;
|
||||
lastError = error;
|
||||
isAdcConnected = connected;
|
||||
adcLastError = error;
|
||||
RefreshSnapshotLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPlcConnected(bool connected, string error)
|
||||
{
|
||||
lock (sync)
|
||||
{
|
||||
isPlcConnected = connected;
|
||||
plcLastError = error;
|
||||
if (!connected)
|
||||
{
|
||||
isTestRunning = false;
|
||||
isResetting = false;
|
||||
}
|
||||
|
||||
RefreshSnapshotLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAllDisconnected(string error)
|
||||
{
|
||||
lock (sync)
|
||||
{
|
||||
isAdcConnected = false;
|
||||
isPlcConnected = false;
|
||||
adcLastError = error;
|
||||
plcLastError = error;
|
||||
isTestRunning = false;
|
||||
isResetting = false;
|
||||
RefreshSnapshotLocked();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshSnapshotLocked()
|
||||
{
|
||||
var connected = isAdcConnected && isPlcConnected;
|
||||
var error = string.Empty;
|
||||
if (!isAdcConnected && !string.IsNullOrWhiteSpace(adcLastError))
|
||||
{
|
||||
error = "ADC: " + adcLastError;
|
||||
}
|
||||
|
||||
if (!isPlcConnected && !string.IsNullOrWhiteSpace(plcLastError))
|
||||
{
|
||||
error = string.IsNullOrWhiteSpace(error)
|
||||
? "PLC: " + plcLastError
|
||||
: error + "; PLC: " + plcLastError;
|
||||
}
|
||||
|
||||
snapshot = new SlipDeviceSnapshot(
|
||||
DateTime.Now,
|
||||
verticalLoadN,
|
||||
@@ -453,8 +528,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
displacementMm,
|
||||
isTestRunning,
|
||||
isResetting,
|
||||
isConnected,
|
||||
lastError);
|
||||
connected,
|
||||
error);
|
||||
}
|
||||
|
||||
private static double ConvertAdc(int rawValue, string zeroText, string coefficientText)
|
||||
|
||||
@@ -23,6 +23,12 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
{
|
||||
public partial class MainWindowViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
private const double SampleIntervalSeconds = 1.0 / 50.0;
|
||||
private const double DynamicWindowStartSeconds = 0.3;
|
||||
private const double DynamicWindowEndSeconds = 0.6;
|
||||
private const int MinimumDynamicWindowPointCount = 10;
|
||||
private const int StandardTrialCount = 3;
|
||||
|
||||
private readonly SlipResistanceDeviceService deviceService = new();
|
||||
private readonly SlipExcelExportService excelExportService = new();
|
||||
private readonly DispatcherTimer refreshTimer;
|
||||
@@ -131,10 +137,10 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private string resultSummary = "等待 3 次有效试验";
|
||||
|
||||
[ObservableProperty]
|
||||
private string staticCoefficient = "0.000";
|
||||
private string staticCoefficient = "0.00";
|
||||
|
||||
[ObservableProperty]
|
||||
private string dynamicCoefficient = "0.000";
|
||||
private string dynamicCoefficient = "0.00";
|
||||
|
||||
[ObservableProperty]
|
||||
private string verticalPressure = "0.0";
|
||||
@@ -149,7 +155,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private string distance = "0.0";
|
||||
|
||||
public string TestSpeedText => $"{TestSpeed} m/s";
|
||||
public string SampleRateText { get; } = "30 Hz";
|
||||
public string SampleRateText { get; } = "50 Hz";
|
||||
public string BatchNumber { get; } = DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
|
||||
public string UploadProgressText => $"{UploadProgress}%";
|
||||
public string StandardReference { get; } = "GB/T 3903.6-2024 5.1.3、7.3.1.4、8.1-8.3:实时采集、静/动摩擦系数、三次平均与重测判定";
|
||||
@@ -219,10 +225,10 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
Log.Information("初始化主页面 ViewModel");
|
||||
Series =
|
||||
[
|
||||
CreateLineSeries("垂直压力(N)", verticalLoadPoints, "#DC2626", 0, 0.65),
|
||||
CreateLineSeries("水平摩擦力(N)", horizontalFrictionPoints, "#16A34A", 0, 0.65),
|
||||
CreateLineSeries("摩擦系数", frictionCoefficientPoints, "#C026D3", 1, 0.65),
|
||||
CreateLineSeries("位移(mm)", displacementPoints, "#2563EB", 0, 0.35)
|
||||
CreateLineSeries("垂直压力(N)", verticalLoadPoints, "#DC2626", 0, 0),
|
||||
CreateLineSeries("水平摩擦力(N)", horizontalFrictionPoints, "#16A34A", 0, 0),
|
||||
CreateLineSeries("摩擦系数", frictionCoefficientPoints, "#C026D3", 1, 0),
|
||||
CreateLineSeries("位移(mm)", displacementPoints, "#2563EB", 0, 0)
|
||||
];
|
||||
|
||||
LoadDeviceSettings();
|
||||
@@ -230,7 +236,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
deviceService.Start(CurrentSettings());
|
||||
_ = LoadPlcParametersAsync();
|
||||
|
||||
refreshTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(33) };
|
||||
refreshTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(20) };
|
||||
refreshTimer.Tick += (_, _) => RefreshFromDevice();
|
||||
refreshTimer.Start();
|
||||
}
|
||||
@@ -461,22 +467,30 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
CurrentStatus = $"设备离线:{device.LastError}";
|
||||
}
|
||||
|
||||
if (!wasRunning && device.IsTestRunning)
|
||||
var isRecording = device.IsConnected && device.IsTestRunning;
|
||||
if (!wasRunning && isRecording)
|
||||
{
|
||||
BeginRun();
|
||||
}
|
||||
|
||||
if (device.IsTestRunning)
|
||||
if (isRecording)
|
||||
{
|
||||
RecordPoint(device);
|
||||
}
|
||||
|
||||
if (wasRunning && !device.IsTestRunning)
|
||||
if (wasRunning && !isRecording)
|
||||
{
|
||||
CompleteRun();
|
||||
if (device.IsConnected)
|
||||
{
|
||||
CompleteRun();
|
||||
}
|
||||
else
|
||||
{
|
||||
AbortRun("设备离线,本次采集已停止,未生成试验结果");
|
||||
}
|
||||
}
|
||||
|
||||
wasRunning = device.IsTestRunning;
|
||||
wasRunning = isRecording;
|
||||
}
|
||||
|
||||
private void BeginRun()
|
||||
@@ -495,7 +509,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private void RecordPoint(SlipDeviceSnapshot device)
|
||||
{
|
||||
var time = runStopwatch.Elapsed.TotalSeconds;
|
||||
if (currentRun.Count > 0 && time - currentRun[^1].TimeSeconds < 1.0 / 30.0)
|
||||
if (currentRun.Count > 0 && time - currentRun[^1].TimeSeconds < SampleIntervalSeconds)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -530,11 +544,17 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
lastCompletedRun = currentRun.ToList();
|
||||
var peak = FindStaticPeak(currentRun);
|
||||
var dynamicWindow = currentRun
|
||||
.Where(point => point.TimeSeconds >= 0.3 && point.TimeSeconds <= 0.6)
|
||||
.Where(point => point.TimeSeconds >= DynamicWindowStartSeconds && point.TimeSeconds <= DynamicWindowEndSeconds)
|
||||
.ToList();
|
||||
if (dynamicWindow.Count == 0)
|
||||
if (dynamicWindow.Count < MinimumDynamicWindowPointCount)
|
||||
{
|
||||
dynamicWindow = currentRun.ToList();
|
||||
Log.Warning(
|
||||
"测试停止但动摩擦窗口采样点不足:TestNumber={TestNumber}, PointCount={PointCount}, DynamicWindowPointCount={DynamicWindowPointCount}",
|
||||
TestNumber,
|
||||
currentRun.Count,
|
||||
dynamicWindow.Count);
|
||||
CurrentStatus = "测试已停止,0.3 s~0.6 s 有效采样点不足,未生成结果";
|
||||
return;
|
||||
}
|
||||
|
||||
var staticCoefficientValue = CalculateCoefficient(peak.HorizontalFrictionN, peak.VerticalLoadN);
|
||||
@@ -547,14 +567,14 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
Samples.Insert(0, new TestSample(
|
||||
nextIndex,
|
||||
DateTime.Now.ToString("HH:mm:ss", CultureInfo.InvariantCulture),
|
||||
staticCoefficientValue.ToString("F3", CultureInfo.InvariantCulture),
|
||||
dynamicCoefficientValue.ToString("F3", CultureInfo.InvariantCulture),
|
||||
staticCoefficientValue.ToString("F2", CultureInfo.InvariantCulture),
|
||||
dynamicCoefficientValue.ToString("F2", CultureInfo.InvariantCulture),
|
||||
verdict,
|
||||
staticCoefficientValue,
|
||||
dynamicCoefficientValue));
|
||||
|
||||
StaticCoefficient = staticCoefficientValue.ToString("F3", CultureInfo.InvariantCulture);
|
||||
DynamicCoefficient = dynamicCoefficientValue.ToString("F3", CultureInfo.InvariantCulture);
|
||||
StaticCoefficient = staticCoefficientValue.ToString("F2", CultureInfo.InvariantCulture);
|
||||
DynamicCoefficient = dynamicCoefficientValue.ToString("F2", CultureInfo.InvariantCulture);
|
||||
UpdateResultSummary();
|
||||
UploadProgress = 100;
|
||||
CurrentStatus = verdict == "有效"
|
||||
@@ -569,21 +589,39 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
verdict);
|
||||
}
|
||||
|
||||
private void AbortRun(string message)
|
||||
{
|
||||
runStopwatch.Stop();
|
||||
Log.Warning(
|
||||
"测试采集中止:TestNumber={TestNumber}, PointCount={PointCount}, Message={Message}",
|
||||
TestNumber,
|
||||
currentRun.Count,
|
||||
message);
|
||||
CurrentStatus = message;
|
||||
}
|
||||
|
||||
private bool NeedsRetest(double staticCoefficientValue, double dynamicCoefficientValue)
|
||||
{
|
||||
var latest = Samples.Take(2).ToList();
|
||||
if (latest.Count < 2)
|
||||
var previous = Samples.Take(StandardTrialCount - 1).Reverse().ToList();
|
||||
if (previous.Count < StandardTrialCount - 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var staticValues = latest.Select(sample => sample.StaticCoefficientValue).Append(staticCoefficientValue).ToArray();
|
||||
var dynamicValues = latest.Select(sample => sample.DynamicCoefficientValue).Append(dynamicCoefficientValue).ToArray();
|
||||
return ExceedsTenPercent(staticValues) || ExceedsTenPercent(dynamicValues);
|
||||
var staticValues = previous.Select(sample => sample.StaticCoefficientValue).Append(staticCoefficientValue).ToArray();
|
||||
var dynamicValues = previous.Select(sample => sample.DynamicCoefficientValue).Append(dynamicCoefficientValue).ToArray();
|
||||
return HasSystematicTrendExceedingTenPercent(staticValues) || HasSystematicTrendExceedingTenPercent(dynamicValues);
|
||||
}
|
||||
|
||||
private static bool ExceedsTenPercent(double[] values)
|
||||
private static bool HasSystematicTrendExceedingTenPercent(double[] values)
|
||||
{
|
||||
var isIncreasing = values.Zip(values.Skip(1), (left, right) => right > left).All(result => result);
|
||||
var isDecreasing = values.Zip(values.Skip(1), (left, right) => right < left).All(result => result);
|
||||
if (!isIncreasing && !isDecreasing)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var average = values.Average();
|
||||
if (Math.Abs(average) < 0.0001)
|
||||
{
|
||||
@@ -595,32 +633,32 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
|
||||
private void UpdateResultSummary()
|
||||
{
|
||||
var latest = Samples.Take(3).ToList();
|
||||
var latest = Samples.Take(StandardTrialCount).ToList();
|
||||
if (latest.Count == 0)
|
||||
{
|
||||
ResultSummary = "等待 3 次有效试验";
|
||||
return;
|
||||
}
|
||||
|
||||
if (latest.Count < StandardTrialCount)
|
||||
{
|
||||
ResultSummary = $"已完成 {latest.Count}/3 次有效试验";
|
||||
return;
|
||||
}
|
||||
|
||||
var staticAverage = latest.Average(sample => sample.StaticCoefficientValue);
|
||||
var dynamicAverage = latest.Average(sample => sample.DynamicCoefficientValue);
|
||||
ResultSummary = $"近 {latest.Count} 次平均 静 {staticAverage:F3} / 动 {dynamicAverage:F3}";
|
||||
ResultSummary = $"近 3 次平均 静 {staticAverage:F2} / 动 {dynamicAverage:F2}";
|
||||
}
|
||||
|
||||
private static SlipDataPoint FindStaticPeak(IReadOnlyList<SlipDataPoint> points)
|
||||
{
|
||||
for (var index = 1; index < points.Count - 1; index++)
|
||||
{
|
||||
var previous = points[index - 1].HorizontalFrictionN;
|
||||
var current = points[index].HorizontalFrictionN;
|
||||
var next = points[index + 1].HorizontalFrictionN;
|
||||
if (current >= previous && current >= next && current > 0)
|
||||
{
|
||||
return points[index];
|
||||
}
|
||||
}
|
||||
|
||||
return points.MaxBy(point => point.HorizontalFrictionN) ?? points[0];
|
||||
var preDynamicWindow = points
|
||||
.Where(point => point.TimeSeconds <= DynamicWindowStartSeconds)
|
||||
.ToList();
|
||||
return (preDynamicWindow.Count > 0 ? preDynamicWindow : points)
|
||||
.MaxBy(point => point.HorizontalFrictionN)
|
||||
?? points[0];
|
||||
}
|
||||
|
||||
private static double CalculateCoefficient(double frictionForce, double verticalLoad) =>
|
||||
|
||||
@@ -430,47 +430,47 @@
|
||||
RowSpacing="10"
|
||||
ColumnSpacing="12">
|
||||
<Label Content="手动速度" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="1" Classes="setting-value" Text="{Binding ManualSpeed, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Column="1" Classes="setting-value" Text="{Binding ManualSpeed, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
<TextBlock Grid.Column="2" Text="m/s" Classes="setting-unit" VerticalAlignment="Center"/>
|
||||
|
||||
<Label Grid.Row="1" Content="手动位移" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Classes="setting-value" Text="{Binding ManualDisplacement, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="1" Grid.Column="1" Classes="setting-value" Text="{Binding ManualDisplacement, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="2" Text="m" Classes="setting-unit" VerticalAlignment="Center"/>
|
||||
|
||||
<Label Grid.Row="2" Content="测试速度" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Classes="setting-value" Text="{Binding TestSpeed, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="2" Grid.Column="1" Classes="setting-value" Text="{Binding TestSpeed, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="2" Text="m/s" Classes="setting-unit" VerticalAlignment="Center"/>
|
||||
|
||||
<Label Grid.Row="3" Content="PLC串口" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Classes="setting-value" Text="{Binding PlcPortName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="1" Classes="setting-value" Text="{Binding PlcPortName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="4" Content="ADC串口" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="5" Classes="setting-value" Text="{Binding AdcPortName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="3" Grid.Column="5" Classes="setting-value" Text="{Binding AdcPortName, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
|
||||
<Label Grid.Row="4" Content="波特率" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Classes="setting-value" Text="{Binding BaudRate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="4" Grid.Column="1" Classes="setting-value" Text="{Binding BaudRate, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
|
||||
<Border Grid.Row="5" Grid.ColumnSpan="6" Height="1" Background="{StaticResource LineBrush}" Margin="0,4"/>
|
||||
|
||||
<Label Grid.Row="6" Content="正压力零点" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Classes="setting-value" Text="{Binding NormalPressureZero, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="1" Classes="setting-value" Text="{Binding NormalPressureZero, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
<Button Grid.Row="6" Grid.Column="2" Content="采零" Classes="compact" Command="{Binding CalibrateNormalPressureZeroCommand}"/>
|
||||
|
||||
<Label Grid.Row="6" Grid.Column="4" Content="正压力系数" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="5" Classes="setting-value" Text="{Binding NormalPressureCoefficient, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="6" Grid.Column="5" Classes="setting-value" Text="{Binding NormalPressureCoefficient, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
|
||||
<Label Grid.Row="7" Content="摩擦1零点" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Classes="setting-value" Text="{Binding FrictionZero1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="1" Classes="setting-value" Text="{Binding FrictionZero1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
<Button Grid.Row="7" Grid.Column="2" Content="摩擦采零" Classes="compact" Command="{Binding CalibrateFrictionZeroCommand}"/>
|
||||
|
||||
<Label Grid.Row="7" Grid.Column="4" Content="摩擦1系数" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="5" Classes="setting-value" Text="{Binding FrictionCoefficient1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="7" Grid.Column="5" Classes="setting-value" Text="{Binding FrictionCoefficient1, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
|
||||
<Label Grid.Row="8" Content="摩擦2零点" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="8" Grid.Column="1" Classes="setting-value" Text="{Binding FrictionZero2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="8" Grid.Column="1" Classes="setting-value" Text="{Binding FrictionZero2, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
|
||||
<Label Grid.Row="8" Grid.Column="4" Content="摩擦2系数" Classes="setting-label" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Row="8" Grid.Column="5" Classes="setting-value" Text="{Binding FrictionCoefficient2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
<TextBox Grid.Row="8" Grid.Column="5" Classes="setting-value" Text="{Binding FrictionCoefficient2, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="2"
|
||||
|
||||
Reference in New Issue
Block a user