From 6763bc65c324388db8874b6dc7376093e8687e14 Mon Sep 17 00:00:00 2001 From: "GukSang.Jin" Date: Fri, 5 Jun 2026 15:40:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B020260605?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/SlipResistanceDeviceService.cs | 11 +- .../ViewModels/MainWindowViewModel.cs | 143 +++++++++++++++++- 2 files changed, 146 insertions(+), 8 deletions(-) diff --git a/Footwear Test methodsfor wholeshoe Slipresistanceperformance/Services/SlipResistanceDeviceService.cs b/Footwear Test methodsfor wholeshoe Slipresistanceperformance/Services/SlipResistanceDeviceService.cs index a3e83e2..d4bb00a 100644 --- a/Footwear Test methodsfor wholeshoe Slipresistanceperformance/Services/SlipResistanceDeviceService.cs +++ b/Footwear Test methodsfor wholeshoe Slipresistanceperformance/Services/SlipResistanceDeviceService.cs @@ -301,8 +301,8 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services } var pressureRaw = UshortToInt(data[0], data[1]); - var friction1Raw = UshortToInt(data[6], data[7]); - var friction2Raw = UshortToInt(data[2], data[3]); + var friction1Raw = UshortToInt(data[2], data[3]); + var friction2Raw = UshortToInt(data[6], data[7]); var conversion = ConvertAdcReadings(pressureRaw, friction1Raw, friction2Raw); LogAdcDiagnostic(pressureRaw, friction1Raw, friction2Raw, conversion); @@ -525,10 +525,9 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services 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); + // Keep each settings row paired with the same ADC channel used by the legacy zero-capture button. + var friction1 = ConvertAdc(friction1Raw, frictionZero1, frictionCoefficient1); + var friction2 = ConvertAdc(friction2Raw, frictionZero2, frictionCoefficient2); var friction = (friction1 + friction2) * -1.0; return AdcConversionResult.Valid(pressure, friction1, friction2, friction); } diff --git a/Footwear Test methodsfor wholeshoe Slipresistanceperformance/ViewModels/MainWindowViewModel.cs b/Footwear Test methodsfor wholeshoe Slipresistanceperformance/ViewModels/MainWindowViewModel.cs index 6310ee3..c8f1117 100644 --- a/Footwear Test methodsfor wholeshoe Slipresistanceperformance/ViewModels/MainWindowViewModel.cs +++ b/Footwear Test methodsfor wholeshoe Slipresistanceperformance/ViewModels/MainWindowViewModel.cs @@ -36,6 +36,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel private const string LegacyPlcPortName = "COM7"; private const string LegacyAdcPortName = "COM8"; private static readonly TimeSpan ResetButtonPendingTimeout = TimeSpan.FromMilliseconds(800); + private static readonly TimeSpan RealtimeCurveTraceInterval = TimeSpan.FromSeconds(1); private readonly SlipResistanceDeviceService deviceService = new(); private readonly SlipExcelExportService excelExportService = new(); @@ -56,6 +57,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel private string activeAdcPortName = string.Empty; private int activeBaudRate; private List lastCompletedRun = []; + private DateTime lastRealtimeCurveTraceLoggedAt = DateTime.MinValue; [ObservableProperty] private string testNumber = $"SLIP-{DateTime.Now:yyyyMMdd-HHmm}"; @@ -672,6 +674,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel displacementPoints.Clear(); runStopwatch.Restart(); UploadProgress = 0; + lastRealtimeCurveTraceLoggedAt = DateTime.MinValue; CurrentStatus = "测试运行:按标准采集垂直载荷、摩擦力、位移与摩擦系数"; Log.Information("测试开始:TestNumber={TestNumber}, TargetLoad={TargetLoad}, TestSpeed={TestSpeed}", TestNumber, TargetLoadText, TestSpeedText); } @@ -699,11 +702,13 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel displacementPoints.Add(new ObservablePoint(time, point.DisplacementMm)); UploadProgress = Math.Min(99, currentRun.Count); + TraceRealtimeCurvePoint(point); } private void CompleteRun() { runStopwatch.Stop(); + LogRealtimeCurveSummary("测试停止"); if (currentRun.Count < 3) { Log.Warning("测试停止但采样点不足:TestNumber={TestNumber}, PointCount={PointCount}", TestNumber, currentRun.Count); @@ -718,11 +723,18 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel .ToList(); if (dynamicWindow.Count < MinimumDynamicWindowPointCount) { + var firstWindowPoint = dynamicWindow.FirstOrDefault(); + var lastWindowPoint = dynamicWindow.LastOrDefault(); Log.Warning( - "测试停止但动摩擦窗口采样点不足:TestNumber={TestNumber}, PointCount={PointCount}, DynamicWindowPointCount={DynamicWindowPointCount}", + "测试停止但动摩擦窗口采样点不足:TestNumber={TestNumber}, PointCount={PointCount}, DynamicWindow=0.300-0.600s, DynamicWindowPointCount={DynamicWindowPointCount}, RequiredPointCount={RequiredPointCount}, ActualWindowStart={ActualWindowStart}, ActualWindowEnd={ActualWindowEnd}, FirstSampleTime={FirstSampleTime:F3}s, LastSampleTime={LastSampleTime:F3}s", TestNumber, currentRun.Count, - dynamicWindow.Count); + dynamicWindow.Count, + MinimumDynamicWindowPointCount, + FormatNullable(firstWindowPoint?.TimeSeconds), + FormatNullable(lastWindowPoint?.TimeSeconds), + currentRun[0].TimeSeconds, + currentRun[^1].TimeSeconds); CurrentStatus = "测试已停止,0.3 s~0.6 s 有效采样点不足,未生成结果"; return; } @@ -733,6 +745,28 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel var dynamicCoefficientValue = CalculateCoefficient(dynamicForce, dynamicLoad); var verdict = NeedsRetest(staticCoefficientValue, dynamicCoefficientValue) ? "需重测" : "有效"; var nextIndex = Samples.Count == 0 ? 1 : Samples.Max(sample => sample.Index) + 1; + var peakIndex = currentRun.IndexOf(peak) + 1; + var dynamicStart = dynamicWindow[0].TimeSeconds; + var dynamicEnd = dynamicWindow[^1].TimeSeconds; + + Log.Information( + "静/动摩擦计算明细:TestNumber={TestNumber}, PointCount={PointCount}, StaticSearchWindow=0.000-{StaticSearchEnd:F3}s, StaticPointIndex={StaticPointIndex}, StaticTime={StaticTime:F3}s, StaticFriction={StaticFriction:F3}N, StaticLoad={StaticLoad:F3}N, StaticCoefficient={StaticCoefficient:F5}, DynamicWindow={DynamicWindowStart:F3}-{DynamicWindowEnd:F3}s, DynamicActualWindow={DynamicActualStart:F3}-{DynamicActualEnd:F3}s, DynamicPointCount={DynamicPointCount}, DynamicAvgFriction={DynamicAvgFriction:F3}N, DynamicAvgLoad={DynamicAvgLoad:F3}N, DynamicCoefficient={DynamicCoefficient:F5}", + TestNumber, + currentRun.Count, + DynamicWindowStartSeconds, + peakIndex, + peak.TimeSeconds, + peak.HorizontalFrictionN, + peak.VerticalLoadN, + staticCoefficientValue, + DynamicWindowStartSeconds, + DynamicWindowEndSeconds, + dynamicStart, + dynamicEnd, + dynamicWindow.Count, + dynamicForce, + dynamicLoad, + dynamicCoefficientValue); Samples.Insert(0, new TestSample( nextIndex, @@ -834,6 +868,111 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel private static double CalculateCoefficient(double frictionForce, double verticalLoad) => Math.Abs(verticalLoad) > 0.0001 ? frictionForce / verticalLoad : 0; + private void LogRealtimeCurveSummary(string stage) + { + var expectedCount = currentRun.Count; + var countsMatch = + verticalLoadPoints.Count == expectedCount + && horizontalFrictionPoints.Count == expectedCount + && frictionCoefficientPoints.Count == expectedCount + && displacementPoints.Count == expectedCount; + var lastPoint = currentRun.Count > 0 ? currentRun[^1] : null; + + Log.Information( + "实时曲线同步汇总:Stage={Stage}, TestNumber={TestNumber}, DataPointCount={DataPointCount}, ChartCounts=[Vertical:{VerticalCount}, Friction:{FrictionCount}, Coefficient:{CoefficientCount}, Displacement:{DisplacementCount}], CountsMatch={CountsMatch}, LastDataPoint={LastDataPoint}, ChartLast=[Vertical:{VerticalChart}, Friction:{FrictionChart}, Coefficient:{CoefficientChart}, Displacement:{DisplacementChart}]", + stage, + TestNumber, + expectedCount, + verticalLoadPoints.Count, + horizontalFrictionPoints.Count, + frictionCoefficientPoints.Count, + displacementPoints.Count, + countsMatch, + FormatDataPoint(lastPoint), + FormatChartPoint(verticalLoadPoints.Count > 0 ? verticalLoadPoints[^1] : null), + FormatChartPoint(horizontalFrictionPoints.Count > 0 ? horizontalFrictionPoints[^1] : null), + FormatChartPoint(frictionCoefficientPoints.Count > 0 ? frictionCoefficientPoints[^1] : null), + FormatChartPoint(displacementPoints.Count > 0 ? displacementPoints[^1] : null)); + } + + private void TraceRealtimeCurvePoint(SlipDataPoint point) + { + var expectedCount = currentRun.Count; + var countsMatch = + verticalLoadPoints.Count == expectedCount + && horizontalFrictionPoints.Count == expectedCount + && frictionCoefficientPoints.Count == expectedCount + && displacementPoints.Count == expectedCount; + var valuesMatch = + countsMatch + && ChartPointMatches(verticalLoadPoints[^1], point.TimeSeconds, point.VerticalLoadN) + && ChartPointMatches(horizontalFrictionPoints[^1], point.TimeSeconds, point.HorizontalFrictionN) + && ChartPointMatches(frictionCoefficientPoints[^1], point.TimeSeconds, point.FrictionCoefficient) + && ChartPointMatches(displacementPoints[^1], point.TimeSeconds, point.DisplacementMm); + + if (!countsMatch || !valuesMatch) + { + Log.Warning( + "实时曲线数据不同步:TestNumber={TestNumber}, PointIndex={PointIndex}, ExpectedCount={ExpectedCount}, Counts=[Vertical:{VerticalCount}, Friction:{FrictionCount}, Coefficient:{CoefficientCount}, Displacement:{DisplacementCount}], ValuesMatch={ValuesMatch}, DataPoint=[Time:{Time:F3}s, Vertical:{Vertical:F3}N, Friction:{Friction:F3}N, Coefficient:{Coefficient:F5}, Displacement:{Displacement:F3}mm], ChartLast=[Vertical:{VerticalChart}, Friction:{FrictionChart}, Coefficient:{CoefficientChart}, Displacement:{DisplacementChart}]", + TestNumber, + expectedCount, + expectedCount, + verticalLoadPoints.Count, + horizontalFrictionPoints.Count, + frictionCoefficientPoints.Count, + displacementPoints.Count, + valuesMatch, + point.TimeSeconds, + point.VerticalLoadN, + point.HorizontalFrictionN, + point.FrictionCoefficient, + point.DisplacementMm, + FormatChartPoint(verticalLoadPoints.Count > 0 ? verticalLoadPoints[^1] : null), + FormatChartPoint(horizontalFrictionPoints.Count > 0 ? horizontalFrictionPoints[^1] : null), + FormatChartPoint(frictionCoefficientPoints.Count > 0 ? frictionCoefficientPoints[^1] : null), + FormatChartPoint(displacementPoints.Count > 0 ? displacementPoints[^1] : null)); + return; + } + + var now = DateTime.UtcNow; + if (expectedCount > 3 && now - lastRealtimeCurveTraceLoggedAt < RealtimeCurveTraceInterval) + { + return; + } + + lastRealtimeCurveTraceLoggedAt = now; + Log.Debug( + "实时曲线点同步:TestNumber={TestNumber}, PointIndex={PointIndex}, Time={Time:F3}s, Vertical={Vertical:F3}N, Friction={Friction:F3}N, Coefficient={Coefficient:F5}, Displacement={Displacement:F3}mm, InDynamicWindow={InDynamicWindow}, ChartCount={ChartCount}", + TestNumber, + expectedCount, + point.TimeSeconds, + point.VerticalLoadN, + point.HorizontalFrictionN, + point.FrictionCoefficient, + point.DisplacementMm, + point.TimeSeconds >= DynamicWindowStartSeconds && point.TimeSeconds <= DynamicWindowEndSeconds, + expectedCount); + } + + private static bool ChartPointMatches(ObservablePoint chartPoint, double expectedX, double expectedY) => + NearlyEquals(chartPoint.X, expectedX) && NearlyEquals(chartPoint.Y, expectedY); + + private static bool NearlyEquals(double? actual, double expected) => + actual.HasValue && Math.Abs(actual.Value - expected) < 0.000001; + + private static string FormatChartPoint(ObservablePoint? point) => + point is null + ? "null" + : $"({FormatNullable(point.X)},{FormatNullable(point.Y)})"; + + private static string FormatNullable(double? value) => + value.HasValue ? value.Value.ToString("F3", CultureInfo.InvariantCulture) : "null"; + + private static string FormatDataPoint(SlipDataPoint? point) => + point is null + ? "null" + : $"Time={point.TimeSeconds.ToString("F3", CultureInfo.InvariantCulture)}s, Vertical={point.VerticalLoadN.ToString("F3", CultureInfo.InvariantCulture)}N, Friction={point.HorizontalFrictionN.ToString("F3", CultureInfo.InvariantCulture)}N, Coefficient={point.FrictionCoefficient.ToString("F5", CultureInfo.InvariantCulture)}, Displacement={point.DisplacementMm.ToString("F3", CultureInfo.InvariantCulture)}mm"; + private async Task RunDeviceCommand(Task command, string successMessage) { try