更新4/28
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user