更新4/28

This commit is contained in:
GukSang.Jin
2026-04-28 18:39:05 +08:00
parent b8cdf9ac9a
commit 52795a3b34

View File

@@ -51,6 +51,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
private const ushort RegisterHorizontalDisplacement = 360;
private const ushort RegisterLiftSpeed = 310;
private const ushort RegisterLiftDisplacement = 320;
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
private const double RealtimeChartDisplacementStepMm = 0.5;
private readonly DispatcherTimer _timer;
private readonly DispatcherTimer _deviceReconnectTimer;
@@ -125,6 +127,11 @@ public sealed class MainViewModel : ObservableObject, IDisposable
private ushort? _pendingTableMotionStopCoil;
private MachineRuntimeState _machineRuntimeState = MachineRuntimeState.Idle;
private string _plcCommandSummary = "等待 PLC 控制指令。";
private double _runningPeakForceN;
private double _kineticForceSum;
private int _kineticSampleCount;
private double _lastRealtimeChartDisplacementMm = double.NaN;
private DateTime _lastRealtimeChartRefreshAt = DateTime.MinValue;
public MainViewModel()
{
@@ -758,6 +765,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
_peakLineSamples.Clear();
_averageLineSamples.Clear();
_currentPointSample.Clear();
ResetRealtimeSamplingMetrics();
CurrentForceN = 0;
CurrentDisplacementMm = 0;
CurrentPeakForceN = 0;
@@ -930,7 +938,6 @@ public sealed class MainViewModel : ObservableObject, IDisposable
return;
}
_forceSamples.Add(new ObservablePoint(frame.DisplacementMm, CurrentForceN));
_currentRunSamples.Add(new RawSampleRecord
{
RunId = _activeRunId,
@@ -940,8 +947,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
ForceN = CurrentForceN,
SpeedMmPerMin = frame.SpeedMmPerMin
});
AddRealtimeChartSample(frame.DisplacementMm, CurrentForceN);
UpdateCurrentPoint(frame.DisplacementMm, CurrentForceN);
UpdatePreviewResult();
UpdatePreviewResult(frame.DisplacementMm, CurrentForceN);
StaticCoefficient1 = frame.StaticCoefficient1;
KineticCoefficient1 = frame.KineticCoefficient1;
StandardDeviation1 = frame.StandardDeviation1;
@@ -952,19 +960,16 @@ public sealed class MainViewModel : ObservableObject, IDisposable
CurrentStaticCoefficient = ResolveRepresentativeValue(activeReplicateCount, StaticCoefficient1, StaticCoefficient2);
CurrentKineticCoefficient = ResolveRepresentativeValue(activeReplicateCount, KineticCoefficient1, KineticCoefficient2);
TrialProgressPercent = Math.Min(100, frame.DisplacementMm / Math.Max(Recipe.TravelMm, 1) * 100);
UpdateReferenceLines();
ForceXAxes[0].MaxLimit = Math.Max(Recipe.TravelMm, frame.DisplacementMm + 5);
ForceYAxes[0].MaxLimit = Math.Max(CurrentPeakForceN * 1.15, 0.5);
_kineticBand.Yj = Math.Max(CurrentPeakForceN * 1.15, 0.5);
OnPropertyChanged(nameof(ForceXAxes));
OnPropertyChanged(nameof(ForceYAxes));
OnPropertyChanged(nameof(ForceSections));
if (frame.IsCompleted)
{
RefreshRealtimeChartPresentation(force: true);
FinalizeRun();
}
else
{
RefreshRealtimeChartPresentation(force: false);
}
RaiseStatusProperties();
}
@@ -1038,15 +1043,43 @@ public sealed class MainViewModel : ObservableObject, IDisposable
RaiseStatusProperties();
}
private void UpdatePreviewResult()
private void AddRealtimeChartSample(double displacementMm, double forceN)
{
CurrentPeakForceN = _forceSamples.Count == 0 ? 0 : _forceSamples.Max(sample => sample.Coordinate.SecondaryValue);
if (_forceSamples.Count > 5)
var sample = new ObservablePoint(displacementMm, forceN);
if (_forceSamples.Count == 0 || !IsFinite(_lastRealtimeChartDisplacementMm))
{
var kineticWindow = _forceSamples.Skip((int)(_forceSamples.Count * 0.35)).ToArray();
CurrentAverageForceN = kineticWindow.Average(sample => sample.Coordinate.SecondaryValue);
_forceSamples.Add(sample);
_lastRealtimeChartDisplacementMm = displacementMm;
return;
}
if (Math.Abs(displacementMm - _lastRealtimeChartDisplacementMm) >= RealtimeChartDisplacementStepMm)
{
_forceSamples.Add(sample);
_lastRealtimeChartDisplacementMm = displacementMm;
return;
}
_forceSamples[^1] = sample;
}
private void UpdatePreviewResult(double displacementMm, double forceN)
{
_runningPeakForceN = Math.Max(_runningPeakForceN, forceN);
CurrentPeakForceN = _runningPeakForceN;
var kineticStartMm = GetActiveTravelMm() * 0.35;
if (displacementMm >= kineticStartMm)
{
_kineticForceSum += forceN;
_kineticSampleCount++;
CurrentAverageForceN = _kineticForceSum / _kineticSampleCount;
}
}
private double GetActiveTravelMm()
{
return _activeRecipeSnapshot?.TravelMm ?? Recipe.TravelMm;
}
private int GetActiveReplicateCount()
@@ -1072,6 +1105,25 @@ public sealed class MainViewModel : ObservableObject, IDisposable
UpdateReferenceLine(_averageLineSamples, CurrentAverageForceN);
}
private void RefreshRealtimeChartPresentation(bool force)
{
var now = DateTime.UtcNow;
if (!force && now - _lastRealtimeChartRefreshAt < RealtimeChartRefreshInterval)
{
return;
}
_lastRealtimeChartRefreshAt = now;
UpdateReferenceLines();
ForceXAxes[0].MaxLimit = Math.Max(GetActiveTravelMm(), CurrentDisplacementMm + 5);
ForceYAxes[0].MaxLimit = Math.Max(CurrentPeakForceN * 1.15, 0.5);
_kineticBand.Yj = Math.Max(CurrentPeakForceN * 1.15, 0.5);
OnPropertyChanged(nameof(ForceXAxes));
OnPropertyChanged(nameof(ForceYAxes));
OnPropertyChanged(nameof(ForceSections));
}
private void UpdateReferenceLine(ObservableCollection<ObservablePoint> target, double y)
{
if (y <= 0)
@@ -1080,12 +1132,28 @@ public sealed class MainViewModel : ObservableObject, IDisposable
return;
}
var xEnd = Math.Max(Recipe.TravelMm, CurrentDisplacementMm);
var xEnd = Math.Max(GetDisplayRecipeSnapshot().TravelMm, CurrentDisplacementMm);
if (target.Count == 2)
{
target[0] = new ObservablePoint(0, y);
target[1] = new ObservablePoint(xEnd, y);
return;
}
target.Clear();
target.Add(new ObservablePoint(0, y));
target.Add(new ObservablePoint(xEnd, y));
}
private void ResetRealtimeSamplingMetrics()
{
_runningPeakForceN = 0;
_kineticForceSum = 0;
_kineticSampleCount = 0;
_lastRealtimeChartDisplacementMm = double.NaN;
_lastRealtimeChartRefreshAt = DateTime.MinValue;
}
private TestRecipeSnapshot GetDisplayRecipeSnapshot()
{
return _isShowingHistoricalRun && _displayedRecipeSnapshot is not null
@@ -1435,6 +1503,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
_peakLineSamples.Clear();
_averageLineSamples.Clear();
_currentPointSample.Clear();
ResetRealtimeSamplingMetrics();
CurrentForceN = 0;
CurrentDisplacementMm = 0;