更新
This commit is contained in:
@@ -797,6 +797,8 @@
|
||||
LegendPosition="Hidden"
|
||||
ZoomMode="None"
|
||||
TooltipPosition="Hidden"
|
||||
AnimationsSpeed="0:0:0"
|
||||
EasingFunction="{x:Null}"
|
||||
DrawMarginFrame="{x:Null}" />
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
@@ -115,8 +115,7 @@ public sealed class RunExportService
|
||||
{
|
||||
return sample.SampleIndex > 0
|
||||
&& IsFinite(sample.DisplacementMm)
|
||||
&& IsFinite(sample.ForceN)
|
||||
&& IsFinite(sample.SpeedMmPerMin);
|
||||
&& IsFinite(sample.ForceN);
|
||||
}
|
||||
|
||||
private static bool IsFinite(double value)
|
||||
@@ -305,7 +304,10 @@ public sealed class RunExportService
|
||||
worksheet.Cell(rowIndex, 16).Style.DateFormat.Format = "yyyy-mm-dd hh:mm:ss.000";
|
||||
worksheet.Cell(rowIndex, 17).Value = sample.DisplacementMm;
|
||||
worksheet.Cell(rowIndex, 18).Value = sample.ForceN;
|
||||
worksheet.Cell(rowIndex, 19).Value = sample.SpeedMmPerMin;
|
||||
if (IsFinite(sample.SpeedMmPerMin))
|
||||
{
|
||||
worksheet.Cell(rowIndex, 19).Value = sample.SpeedMmPerMin;
|
||||
}
|
||||
|
||||
worksheet.Cell(rowIndex, 4).Style.Fill.BackgroundColor = ToXlColor(item.CurveColorHex);
|
||||
worksheet.Cell(rowIndex, 4).Style.Font.FontColor = XLColor.White;
|
||||
@@ -458,15 +460,13 @@ public sealed class RunExportService
|
||||
var charts = new List<ChartDefinition>();
|
||||
var perRunStartRow = Math.Max(10, runs.Count + 8);
|
||||
const int chartHeight = 11;
|
||||
const int chartWidth = 9;
|
||||
const int chartWidth = 19;
|
||||
const int rowGap = 2;
|
||||
|
||||
for (var index = 0; index < perRunSeries.Count; index++)
|
||||
{
|
||||
var chartRowGroup = index / 2;
|
||||
var chartColumnGroup = index % 2;
|
||||
var fromColumn = chartColumnGroup == 0 ? 0 : 10;
|
||||
var fromRow = perRunStartRow + chartRowGroup * (chartHeight + rowGap);
|
||||
var fromColumn = 0;
|
||||
var fromRow = perRunStartRow + index * (chartHeight + rowGap);
|
||||
var toColumn = fromColumn + chartWidth;
|
||||
var toRow = fromRow + chartHeight;
|
||||
var runSeries = perRunSeries[index];
|
||||
@@ -492,7 +492,7 @@ public sealed class RunExportService
|
||||
private static int CalculateChartSheetLastRow(IReadOnlyList<ExportCurveData> runs)
|
||||
{
|
||||
var chartCount = runs.Count(IsChartableRun);
|
||||
return Math.Max(24, runs.Count + 22 + ((chartCount + 1) / 2) * 13);
|
||||
return Math.Max(24, runs.Count + 22 + chartCount * 13);
|
||||
}
|
||||
|
||||
private static void ApplyDefaultWorksheetStyle(IXLWorksheet worksheet)
|
||||
|
||||
@@ -130,8 +130,11 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
private double _runningPeakForceN;
|
||||
private double _kineticForceSum;
|
||||
private int _kineticSampleCount;
|
||||
private double _runStartHorizontalPositionMm = double.NaN;
|
||||
private double _lastRealtimeChartDisplacementMm = double.NaN;
|
||||
private DateTime _lastRealtimeChartRefreshAt = DateTime.MinValue;
|
||||
private double _lastForceXAxisMaxLimit;
|
||||
private double _lastForceYAxisMaxLimit;
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
@@ -179,10 +182,6 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
|
||||
var seriesStroke = new SolidColorPaint(new SKColor(0, 105, 180), 3);
|
||||
var seriesGeometryFill = new SolidColorPaint(new SKColor(0, 131, 154));
|
||||
var axisLabelPaint = new SolidColorPaint(new SKColor(74, 92, 108));
|
||||
var axisNamePaint = new SolidColorPaint(new SKColor(33, 49, 61));
|
||||
var separatorPaint = new SolidColorPaint(new SKColor(209, 219, 227), 1);
|
||||
var subsectionPaint = new SolidColorPaint(new SKColor(229, 236, 242), 1);
|
||||
var peakStroke = new SolidColorPaint(new SKColor(198, 107, 88), 1.5f);
|
||||
var averageStroke = new SolidColorPaint(new SKColor(196, 150, 69), 1.5f);
|
||||
var currentPointFill = new SolidColorPaint(new SKColor(0, 105, 180));
|
||||
@@ -230,39 +229,10 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
}
|
||||
];
|
||||
|
||||
ForceXAxes =
|
||||
[
|
||||
new Axis
|
||||
{
|
||||
Name = "位移 / mm",
|
||||
NameTextSize = 12,
|
||||
TextSize = 11,
|
||||
MinLimit = 0,
|
||||
MinStep = 25,
|
||||
LabelsPaint = axisLabelPaint,
|
||||
NamePaint = axisNamePaint,
|
||||
SeparatorsPaint = separatorPaint,
|
||||
SubseparatorsPaint = subsectionPaint,
|
||||
Padding = new Padding(8, 0, 0, 0)
|
||||
}
|
||||
];
|
||||
|
||||
ForceYAxes =
|
||||
[
|
||||
new Axis
|
||||
{
|
||||
Name = "力值 / N",
|
||||
NameTextSize = 12,
|
||||
TextSize = 11,
|
||||
MinLimit = 0,
|
||||
MinStep = 0.1,
|
||||
LabelsPaint = axisLabelPaint,
|
||||
NamePaint = axisNamePaint,
|
||||
SeparatorsPaint = separatorPaint,
|
||||
SubseparatorsPaint = subsectionPaint,
|
||||
Padding = new Padding(0, 0, 8, 0)
|
||||
}
|
||||
];
|
||||
_lastForceXAxisMaxLimit = 1;
|
||||
_lastForceYAxisMaxLimit = 0.5;
|
||||
ForceXAxes = CreateForceXAxes(_lastForceXAxisMaxLimit);
|
||||
ForceYAxes = CreateForceYAxes(_lastForceYAxisMaxLimit);
|
||||
|
||||
_kineticBand = new RectangularSection
|
||||
{
|
||||
@@ -841,6 +811,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
InterlockMessage = "试验已停止,可重新装样或调整参数。";
|
||||
AddWarningEvent("试验被人工停止。");
|
||||
RaiseStatusProperties();
|
||||
RaiseCommandStates();
|
||||
}
|
||||
|
||||
private async void Rise()
|
||||
@@ -900,6 +871,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
StateBrush = BrushFromHex("#6C8E78");
|
||||
InterlockMessage = "设备已复位,可重新开始试验。";
|
||||
RaiseStatusProperties();
|
||||
RaiseCommandStates();
|
||||
}
|
||||
|
||||
private void CompleteCalibration()
|
||||
@@ -938,37 +910,15 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
_currentRunSamples.Add(new RawSampleRecord
|
||||
if (!AppendRunningSample(frame, out var isCompleted))
|
||||
{
|
||||
RunId = _activeRunId,
|
||||
SampleIndex = _currentRunSamples.Count + 1,
|
||||
CapturedAt = DateTime.Now,
|
||||
DisplacementMm = frame.DisplacementMm,
|
||||
ForceN = CurrentForceN,
|
||||
SpeedMmPerMin = frame.SpeedMmPerMin
|
||||
});
|
||||
AddRealtimeChartSample(frame.DisplacementMm, CurrentForceN);
|
||||
UpdateCurrentPoint(frame.DisplacementMm, CurrentForceN);
|
||||
UpdatePreviewResult(frame.DisplacementMm, CurrentForceN);
|
||||
StaticCoefficient1 = frame.StaticCoefficient1;
|
||||
KineticCoefficient1 = frame.KineticCoefficient1;
|
||||
StandardDeviation1 = frame.StandardDeviation1;
|
||||
StaticCoefficient2 = frame.StaticCoefficient2;
|
||||
KineticCoefficient2 = frame.KineticCoefficient2;
|
||||
StandardDeviation2 = frame.StandardDeviation2;
|
||||
var activeReplicateCount = GetActiveReplicateCount();
|
||||
CurrentStaticCoefficient = ResolveRepresentativeValue(activeReplicateCount, StaticCoefficient1, StaticCoefficient2);
|
||||
CurrentKineticCoefficient = ResolveRepresentativeValue(activeReplicateCount, KineticCoefficient1, KineticCoefficient2);
|
||||
TrialProgressPercent = Math.Min(100, frame.DisplacementMm / Math.Max(Recipe.TravelMm, 1) * 100);
|
||||
|
||||
if (frame.IsCompleted)
|
||||
{
|
||||
RefreshRealtimeChartPresentation(force: true);
|
||||
FinalizeRun();
|
||||
RaiseStatusProperties();
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (isCompleted)
|
||||
{
|
||||
RefreshRealtimeChartPresentation(force: false);
|
||||
FinalizeRun();
|
||||
}
|
||||
|
||||
RaiseStatusProperties();
|
||||
@@ -993,7 +943,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
private void UpdateLiveProcessSnapshot(ProcessFrame frame)
|
||||
{
|
||||
CurrentForceN = Math.Max(frame.ForceN, 0.001);
|
||||
CurrentDisplacementMm = frame.DisplacementMm;
|
||||
CurrentDisplacementMm = frame.HorizontalPosition;
|
||||
CurrentSpeedMmPerMin = frame.SpeedMmPerMin;
|
||||
CurrentLiftSpeed = frame.LiftSpeed;
|
||||
CurrentLiftDisplacement = frame.LiftDisplacement;
|
||||
@@ -1063,6 +1013,76 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
_forceSamples[^1] = sample;
|
||||
}
|
||||
|
||||
private bool AppendRunningSample(ProcessFrame frame, out bool isCompleted)
|
||||
{
|
||||
isCompleted = false;
|
||||
if (!TryResolveRunningPosition(frame, out var horizontalPositionMm, out var travelDeltaMm))
|
||||
{
|
||||
AddWarningEvent("实时采样包含无效水平位置,已跳过本次曲线点。");
|
||||
return false;
|
||||
}
|
||||
|
||||
var forceN = CurrentForceN;
|
||||
if (!IsFinite(forceN))
|
||||
{
|
||||
AddWarningEvent("实时采样包含无效力值,已跳过本次曲线点。");
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentDisplacementMm = horizontalPositionMm;
|
||||
var isFirstChartSample = _forceSamples.Count == 0;
|
||||
_currentRunSamples.Add(new RawSampleRecord
|
||||
{
|
||||
RunId = _activeRunId,
|
||||
SampleIndex = _currentRunSamples.Count + 1,
|
||||
CapturedAt = DateTime.Now,
|
||||
DisplacementMm = horizontalPositionMm,
|
||||
ForceN = forceN,
|
||||
SpeedMmPerMin = frame.SpeedMmPerMin
|
||||
});
|
||||
|
||||
AddRealtimeChartSample(horizontalPositionMm, forceN);
|
||||
UpdateCurrentPoint(horizontalPositionMm, forceN);
|
||||
UpdatePreviewResult(travelDeltaMm, forceN);
|
||||
StaticCoefficient1 = frame.StaticCoefficient1;
|
||||
KineticCoefficient1 = frame.KineticCoefficient1;
|
||||
StandardDeviation1 = frame.StandardDeviation1;
|
||||
StaticCoefficient2 = frame.StaticCoefficient2;
|
||||
KineticCoefficient2 = frame.KineticCoefficient2;
|
||||
StandardDeviation2 = frame.StandardDeviation2;
|
||||
var activeReplicateCount = GetActiveReplicateCount();
|
||||
CurrentStaticCoefficient = ResolveRepresentativeValue(activeReplicateCount, StaticCoefficient1, StaticCoefficient2);
|
||||
CurrentKineticCoefficient = ResolveRepresentativeValue(activeReplicateCount, KineticCoefficient1, KineticCoefficient2);
|
||||
TrialProgressPercent = Math.Min(100, travelDeltaMm / Math.Max(Recipe.TravelMm, 1) * 100);
|
||||
isCompleted = travelDeltaMm >= GetActiveTravelMm();
|
||||
RefreshRealtimeChartPresentation(force: isFirstChartSample || isCompleted);
|
||||
if (isFirstChartSample)
|
||||
{
|
||||
_exportReportCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryResolveRunningPosition(ProcessFrame frame, out double horizontalPositionMm, out double travelDeltaMm)
|
||||
{
|
||||
horizontalPositionMm = 0;
|
||||
travelDeltaMm = 0;
|
||||
if (!IsFinite(frame.HorizontalPosition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
horizontalPositionMm = frame.HorizontalPosition;
|
||||
if (!IsFinite(_runStartHorizontalPositionMm))
|
||||
{
|
||||
_runStartHorizontalPositionMm = horizontalPositionMm;
|
||||
}
|
||||
|
||||
travelDeltaMm = Math.Abs(horizontalPositionMm - _runStartHorizontalPositionMm);
|
||||
return IsFinite(horizontalPositionMm) && IsFinite(travelDeltaMm);
|
||||
}
|
||||
|
||||
private void UpdatePreviewResult(double displacementMm, double forceN)
|
||||
{
|
||||
_runningPeakForceN = Math.Max(_runningPeakForceN, forceN);
|
||||
@@ -1116,12 +1136,155 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
_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);
|
||||
var visibleForceN = Math.Max(CurrentPeakForceN, CurrentForceN);
|
||||
var yMax = Math.Max(visibleForceN * 1.15, 0.5);
|
||||
var xMax = CalculateRealtimeXAxisMaxLimit();
|
||||
UpdateAxisLimits(xMax, yMax);
|
||||
if (UpdateKineticBand(yMax))
|
||||
{
|
||||
OnPropertyChanged(nameof(ForceSections));
|
||||
}
|
||||
}
|
||||
|
||||
private double CalculateRealtimeXAxisMaxLimit()
|
||||
{
|
||||
var sampleMax = _forceSamples.Count == 0
|
||||
? 0
|
||||
: _forceSamples.Max(sample => sample.X ?? 0);
|
||||
var currentMax = Math.Max(CurrentDisplacementMm, CurrentHorizontalPosition);
|
||||
var visibleMax = Math.Max(sampleMax, currentMax);
|
||||
return Math.Max(visibleMax + 5, 1);
|
||||
}
|
||||
|
||||
private void UpdateAxisLimits(double xMax, double yMax)
|
||||
{
|
||||
var changed = false;
|
||||
if (ShouldUpdateAxisLimit(_lastForceXAxisMaxLimit, xMax))
|
||||
{
|
||||
_lastForceXAxisMaxLimit = xMax;
|
||||
ForceXAxes[0].MaxLimit = xMax;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (ShouldUpdateAxisLimit(_lastForceYAxisMaxLimit, yMax))
|
||||
{
|
||||
_lastForceYAxisMaxLimit = yMax;
|
||||
ForceYAxes[0].MaxLimit = yMax;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
OnPropertyChanged(nameof(ForceXAxes));
|
||||
OnPropertyChanged(nameof(ForceYAxes));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetAxisLimits(double xMax, double yMax)
|
||||
{
|
||||
_lastForceXAxisMaxLimit = xMax;
|
||||
_lastForceYAxisMaxLimit = yMax;
|
||||
ForceXAxes[0].MaxLimit = xMax;
|
||||
ForceYAxes[0].MaxLimit = yMax;
|
||||
OnPropertyChanged(nameof(ForceXAxes));
|
||||
OnPropertyChanged(nameof(ForceYAxes));
|
||||
OnPropertyChanged(nameof(ForceSections));
|
||||
}
|
||||
|
||||
private static bool ShouldUpdateAxisLimit(double currentLimit, double nextLimit)
|
||||
{
|
||||
if (!IsFinite(currentLimit) || !IsFinite(nextLimit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return nextLimit > currentLimit || currentLimit - nextLimit > Math.Max(2, currentLimit * 0.1);
|
||||
}
|
||||
|
||||
private bool UpdateKineticBand(double yMax)
|
||||
{
|
||||
var travelMm = GetActiveTravelMm();
|
||||
double xi;
|
||||
double xj;
|
||||
if (IsFinite(_runStartHorizontalPositionMm))
|
||||
{
|
||||
var direction = CurrentHorizontalPosition >= _runStartHorizontalPositionMm ? 1 : -1;
|
||||
var kineticStart = _runStartHorizontalPositionMm + direction * travelMm * 0.35;
|
||||
var kineticEnd = _runStartHorizontalPositionMm + direction * travelMm;
|
||||
xi = Math.Min(kineticStart, kineticEnd);
|
||||
xj = Math.Max(kineticStart, kineticEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
xi = travelMm * 0.35;
|
||||
xj = travelMm;
|
||||
}
|
||||
|
||||
var changed = HasMeaningfulChange(_kineticBand.Xi ?? double.NaN, xi)
|
||||
|| HasMeaningfulChange(_kineticBand.Xj ?? double.NaN, xj)
|
||||
|| HasMeaningfulChange(_kineticBand.Yj ?? double.NaN, yMax)
|
||||
|| HasMeaningfulChange(_kineticBand.Yi ?? double.NaN, 0);
|
||||
if (!changed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_kineticBand.Xi = xi;
|
||||
_kineticBand.Xj = xj;
|
||||
_kineticBand.Yi = 0;
|
||||
_kineticBand.Yj = yMax;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HasMeaningfulChange(double current, double next)
|
||||
{
|
||||
if (!IsFinite(current) || !IsFinite(next))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return Math.Abs(current - next) > 0.001;
|
||||
}
|
||||
|
||||
private static Axis[] CreateForceXAxes(double maxLimit)
|
||||
{
|
||||
return
|
||||
[
|
||||
new Axis
|
||||
{
|
||||
Name = "位移 / mm",
|
||||
NameTextSize = 12,
|
||||
TextSize = 11,
|
||||
MinLimit = 0,
|
||||
MaxLimit = Math.Max(maxLimit, 1),
|
||||
MinStep = 25,
|
||||
LabelsPaint = new SolidColorPaint(new SKColor(74, 92, 108)),
|
||||
NamePaint = new SolidColorPaint(new SKColor(33, 49, 61)),
|
||||
SeparatorsPaint = new SolidColorPaint(new SKColor(209, 219, 227), 1),
|
||||
SubseparatorsPaint = new SolidColorPaint(new SKColor(229, 236, 242), 1),
|
||||
Padding = new Padding(8, 0, 0, 0)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private static Axis[] CreateForceYAxes(double maxLimit)
|
||||
{
|
||||
return
|
||||
[
|
||||
new Axis
|
||||
{
|
||||
Name = "力值 / N",
|
||||
NameTextSize = 12,
|
||||
TextSize = 11,
|
||||
MinLimit = 0,
|
||||
MaxLimit = Math.Max(maxLimit, 0.5),
|
||||
MinStep = 0.1,
|
||||
LabelsPaint = new SolidColorPaint(new SKColor(74, 92, 108)),
|
||||
NamePaint = new SolidColorPaint(new SKColor(33, 49, 61)),
|
||||
SeparatorsPaint = new SolidColorPaint(new SKColor(209, 219, 227), 1),
|
||||
SubseparatorsPaint = new SolidColorPaint(new SKColor(229, 236, 242), 1),
|
||||
Padding = new Padding(0, 0, 8, 0)
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
private void UpdateReferenceLine(ObservableCollection<ObservablePoint> target, double y)
|
||||
@@ -1132,7 +1295,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
var xEnd = Math.Max(GetDisplayRecipeSnapshot().TravelMm, CurrentDisplacementMm);
|
||||
var xEnd = Math.Max(_lastForceXAxisMaxLimit, CurrentDisplacementMm);
|
||||
if (target.Count == 2)
|
||||
{
|
||||
target[0] = new ObservablePoint(0, y);
|
||||
@@ -1150,6 +1313,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
_runningPeakForceN = 0;
|
||||
_kineticForceSum = 0;
|
||||
_kineticSampleCount = 0;
|
||||
_runStartHorizontalPositionMm = double.NaN;
|
||||
_lastRealtimeChartDisplacementMm = double.NaN;
|
||||
_lastRealtimeChartRefreshAt = DateTime.MinValue;
|
||||
}
|
||||
@@ -1343,7 +1507,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
|
||||
private bool CanExportHistoricalReport()
|
||||
{
|
||||
return RunHistory.Count > 0;
|
||||
return RunHistory.Count > 0 || HasActiveExportSamples();
|
||||
}
|
||||
|
||||
private bool CanClearHistoryStable()
|
||||
@@ -1441,14 +1605,16 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
TrialProgressPercent = Math.Min(100, CurrentDisplacementMm / Math.Max(data.Recipe.TravelMm, 1) * 100);
|
||||
|
||||
UpdateReferenceLines();
|
||||
ForceXAxes[0].MaxLimit = Math.Max(data.Recipe.TravelMm, CurrentDisplacementMm + 5);
|
||||
ForceYAxes[0].MaxLimit = Math.Max(CurrentPeakForceN * 1.15, 0.5);
|
||||
_kineticBand.Xi = data.Recipe.TravelMm * 0.35;
|
||||
_kineticBand.Xj = data.Recipe.TravelMm;
|
||||
var historyMaxX = data.Samples.Count == 0
|
||||
? CurrentDisplacementMm
|
||||
: data.Samples.Max(sample => sample.DisplacementMm);
|
||||
var yMax = Math.Max(CurrentPeakForceN * 1.15, 0.5);
|
||||
SetAxisLimits(Math.Max(historyMaxX + 5, 1), yMax);
|
||||
var historyStartX = data.Samples.Count == 0 ? 0 : data.Samples.Min(sample => sample.DisplacementMm);
|
||||
_kineticBand.Xi = historyStartX + data.Recipe.TravelMm * 0.35;
|
||||
_kineticBand.Xj = historyStartX + data.Recipe.TravelMm;
|
||||
_kineticBand.Yi = 0;
|
||||
_kineticBand.Yj = Math.Max(CurrentPeakForceN * 1.15, 0.5);
|
||||
OnPropertyChanged(nameof(ForceXAxes));
|
||||
OnPropertyChanged(nameof(ForceYAxes));
|
||||
_kineticBand.Yj = yMax;
|
||||
OnPropertyChanged(nameof(ForceSections));
|
||||
RaiseStatusProperties();
|
||||
OnPropertyChanged(nameof(AnalysisModeSummary));
|
||||
@@ -1524,15 +1690,12 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
CurrentKineticCoefficient = 0;
|
||||
TrialProgressPercent = 0;
|
||||
|
||||
ForceXAxes[0].MaxLimit = Math.Max(Recipe.TravelMm, 50);
|
||||
ForceYAxes[0].MaxLimit = 0.5;
|
||||
SetAxisLimits(1, 0.5);
|
||||
_kineticBand.Xi = Recipe.TravelMm * 0.35;
|
||||
_kineticBand.Xj = Recipe.TravelMm;
|
||||
_kineticBand.Yi = 0;
|
||||
_kineticBand.Yj = 0.5;
|
||||
|
||||
OnPropertyChanged(nameof(ForceXAxes));
|
||||
OnPropertyChanged(nameof(ForceYAxes));
|
||||
OnPropertyChanged(nameof(ForceSections));
|
||||
}
|
||||
|
||||
@@ -1541,6 +1704,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
_activeRunId = Guid.Empty;
|
||||
_activeRecipeSnapshot = null;
|
||||
_activeRunStartedByPlc = false;
|
||||
_runStartHorizontalPositionMm = double.NaN;
|
||||
_currentRunSamples.Clear();
|
||||
}
|
||||
|
||||
@@ -1743,6 +1907,13 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
var activeRunData = BuildActiveRunExportData();
|
||||
if (activeRunData is not null)
|
||||
{
|
||||
historyData.Add(activeRunData);
|
||||
hasExportableSamples = true;
|
||||
}
|
||||
|
||||
exportData = historyData;
|
||||
if (!hasExportableSamples)
|
||||
{
|
||||
@@ -1781,7 +1952,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
}
|
||||
|
||||
var workbookPath = _runExportService.ExportHistoricalComparisonReport(exportData, saveFileDialog.FileName);
|
||||
foreach (var item in exportData.Where(data => data.Samples.Any(RunExportService.IsValidSample)))
|
||||
foreach (var item in exportData.Where(data =>
|
||||
data.Samples.Any(RunExportService.IsValidSample)
|
||||
&& RunHistory.Any(run => run.RunId == data.Run.RunId)))
|
||||
{
|
||||
_dataRepository.UpdateExportPaths(item.Run.RunId, null, workbookPath);
|
||||
item.Run.ReportExportPath = workbookPath;
|
||||
@@ -1797,6 +1970,74 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private bool HasActiveExportSamples()
|
||||
{
|
||||
return _activeRunId != Guid.Empty
|
||||
&& _activeRecipeSnapshot is not null
|
||||
&& _currentRunSamples.Any(RunExportService.IsValidSample);
|
||||
}
|
||||
|
||||
private PersistedRunData? BuildActiveRunExportData()
|
||||
{
|
||||
if (!HasActiveExportSamples())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var activeRecipeSnapshot = _activeRecipeSnapshot;
|
||||
if (activeRecipeSnapshot is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var activeSamples = _currentRunSamples
|
||||
.Where(RunExportService.IsValidSample)
|
||||
.Select(sample => new RawSampleRecord
|
||||
{
|
||||
RunId = sample.RunId,
|
||||
SampleIndex = sample.SampleIndex,
|
||||
CapturedAt = sample.CapturedAt,
|
||||
DisplacementMm = sample.DisplacementMm,
|
||||
ForceN = sample.ForceN,
|
||||
SpeedMmPerMin = sample.SpeedMmPerMin
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
if (activeSamples.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var activeRun = new RunRecord
|
||||
{
|
||||
RunId = _activeRunId,
|
||||
RunIndex = NextRunIndex,
|
||||
CompletedAt = DateTime.Now,
|
||||
BatchNumber = activeRecipeSnapshot.BatchNumber,
|
||||
TestMode = activeRecipeSnapshot.TestMode,
|
||||
StaticCoefficient = CurrentStaticCoefficient,
|
||||
StaticCoefficient1 = StaticCoefficient1,
|
||||
StaticCoefficient2 = StaticCoefficient2,
|
||||
KineticCoefficient = CurrentKineticCoefficient,
|
||||
KineticCoefficient1 = KineticCoefficient1,
|
||||
KineticCoefficient2 = KineticCoefficient2,
|
||||
StandardDeviation = ResolveRepresentativeValue(GetActiveReplicateCount(), StandardDeviation1, StandardDeviation2),
|
||||
StandardDeviation1 = StandardDeviation1,
|
||||
StandardDeviation2 = StandardDeviation2,
|
||||
PeakForceN = CurrentPeakForceN,
|
||||
AverageForceN = CurrentAverageForceN,
|
||||
Judgement = "采样中",
|
||||
SampleCount = activeSamples.Length
|
||||
};
|
||||
|
||||
return new PersistedRunData
|
||||
{
|
||||
Run = activeRun,
|
||||
Recipe = activeRecipeSnapshot,
|
||||
Samples = activeSamples
|
||||
};
|
||||
}
|
||||
|
||||
private void ClearHistoryData()
|
||||
{
|
||||
var historyCount = RunHistory.Count;
|
||||
|
||||
Reference in New Issue
Block a user