diff --git a/COFTester/MainWindow.xaml b/COFTester/MainWindow.xaml
index 4359273..c93e9e5 100644
--- a/COFTester/MainWindow.xaml
+++ b/COFTester/MainWindow.xaml
@@ -251,31 +251,96 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -601,8 +666,11 @@
+
+
+
@@ -615,90 +683,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
diff --git a/COFTester/MainWindow.xaml.cs b/COFTester/MainWindow.xaml.cs
index eff589c..b8ca469 100644
--- a/COFTester/MainWindow.xaml.cs
+++ b/COFTester/MainWindow.xaml.cs
@@ -98,6 +98,17 @@ public partial class MainWindow : Window
_isRealtimeChartAutoScrollPinned = IsRealtimeChartScrolledToEnd();
}
+ private void RealtimeChartHost_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ if ((Keyboard.Modifiers & ModifierKeys.Control) != ModifierKeys.Control)
+ {
+ return;
+ }
+
+ _viewModel.ZoomRealtimeChartFromWheel(e.Delta);
+ e.Handled = true;
+ }
+
private void RealtimeForceChart_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (e.NewSize.Width <= 0 || e.NewSize.Height <= 0)
diff --git a/COFTester/Services/TestDataRepository.cs b/COFTester/Services/TestDataRepository.cs
index 3d33457..5e34f46 100644
--- a/COFTester/Services/TestDataRepository.cs
+++ b/COFTester/Services/TestDataRepository.cs
@@ -260,20 +260,41 @@ public sealed class TestDataRepository
command.CommandText =
"""
SELECT run_id, run_index, completed_at, batch_number, test_mode,
- COALESCE(NULLIF(static_coefficient, 0), (
+ COALESCE((
SELECT AVG(static_coefficient)
FROM reciprocating_records
WHERE run_id = test_runs.run_id AND static_coefficient IS NOT NULL
- ), static_coefficient) AS static_coefficient,
+ ), NULLIF(static_coefficient, 0), static_coefficient) AS static_coefficient,
static_coefficient_1, static_coefficient_2,
- COALESCE(NULLIF(kinetic_coefficient, 0), (
+ COALESCE((
SELECT AVG(kinetic_coefficient)
FROM reciprocating_records
WHERE run_id = test_runs.run_id AND kinetic_coefficient IS NOT NULL
- ), kinetic_coefficient) AS kinetic_coefficient,
+ ), NULLIF(kinetic_coefficient, 0), kinetic_coefficient) AS kinetic_coefficient,
kinetic_coefficient_1, kinetic_coefficient_2, standard_deviation,
standard_deviation_1, standard_deviation_2, peak_force_n, average_force_n,
- judgement, csv_export_path, report_export_path, sample_count
+ judgement, csv_export_path, report_export_path, sample_count,
+ (
+ SELECT AVG(static_force_n)
+ FROM reciprocating_records
+ WHERE run_id = test_runs.run_id AND static_force_n IS NOT NULL
+ ) AS average_static_force_n,
+ (
+ SELECT AVG(kinetic_force_n)
+ FROM reciprocating_records
+ WHERE run_id = test_runs.run_id AND kinetic_force_n IS NOT NULL
+ ) AS average_kinetic_force_n,
+ COALESCE((
+ SELECT MAX(record_index)
+ FROM reciprocating_records
+ WHERE run_id = test_runs.run_id
+ AND (
+ static_coefficient IS NOT NULL OR
+ kinetic_coefficient IS NOT NULL OR
+ static_force_n IS NOT NULL OR
+ kinetic_force_n IS NOT NULL
+ )
+ ), 0) AS reciprocating_average_end_index
FROM test_runs
ORDER BY completed_at DESC
LIMIT $limit;
@@ -302,22 +323,43 @@ public sealed class TestDataRepository
counterface_material, direction, sled_mass_grams, lift_speed_mm_per_min,
lift_displacement_mm, speed_mm_per_min, travel_mm, replicate_count,
reciprocating_count, specimen_description,
- COALESCE(NULLIF(static_coefficient, 0), (
+ COALESCE((
SELECT AVG(static_coefficient)
FROM reciprocating_records
WHERE run_id = test_runs.run_id AND static_coefficient IS NOT NULL
- ), static_coefficient) AS static_coefficient,
+ ), NULLIF(static_coefficient, 0), static_coefficient) AS static_coefficient,
static_coefficient_1,
static_coefficient_2,
- COALESCE(NULLIF(kinetic_coefficient, 0), (
+ COALESCE((
SELECT AVG(kinetic_coefficient)
FROM reciprocating_records
WHERE run_id = test_runs.run_id AND kinetic_coefficient IS NOT NULL
- ), kinetic_coefficient) AS kinetic_coefficient,
+ ), NULLIF(kinetic_coefficient, 0), kinetic_coefficient) AS kinetic_coefficient,
kinetic_coefficient_1,
kinetic_coefficient_2, standard_deviation, standard_deviation_1, standard_deviation_2,
peak_force_n, average_force_n, judgement, csv_export_path,
- report_export_path, sample_count
+ report_export_path, sample_count,
+ (
+ SELECT AVG(static_force_n)
+ FROM reciprocating_records
+ WHERE run_id = test_runs.run_id AND static_force_n IS NOT NULL
+ ) AS average_static_force_n,
+ (
+ SELECT AVG(kinetic_force_n)
+ FROM reciprocating_records
+ WHERE run_id = test_runs.run_id AND kinetic_force_n IS NOT NULL
+ ) AS average_kinetic_force_n,
+ COALESCE((
+ SELECT MAX(record_index)
+ FROM reciprocating_records
+ WHERE run_id = test_runs.run_id
+ AND (
+ static_coefficient IS NOT NULL OR
+ kinetic_coefficient IS NOT NULL OR
+ static_force_n IS NOT NULL OR
+ kinetic_force_n IS NOT NULL
+ )
+ ), 0) AS reciprocating_average_end_index
FROM test_runs
WHERE run_id = $runId;
""";
@@ -350,7 +392,10 @@ public sealed class TestDataRepository
Judgement = reader.GetString(27),
CsvExportPath = reader.GetString(28),
ReportExportPath = reader.GetString(29),
- SampleCount = reader.GetInt32(30)
+ SampleCount = reader.GetInt32(30),
+ AverageStaticForceN = ReadNullableDouble(reader, 31),
+ AverageKineticForceN = ReadNullableDouble(reader, 32),
+ ReciprocatingAverageEndIndex = reader.GetInt32(33)
};
var recipe = new TestRecipeSnapshot
{
@@ -813,7 +858,11 @@ public sealed class TestDataRepository
Judgement = reader.GetString(16),
CsvExportPath = reader.GetString(17),
ReportExportPath = reader.GetString(18),
- SampleCount = reader.GetInt32(19)
+ SampleCount = reader.GetInt32(19),
+ AverageStaticForceN = ReadNullableDouble(reader, 20),
+ AverageKineticForceN = ReadNullableDouble(reader, 21),
+ ReciprocatingAverageEndIndex = reader.GetInt32(22)
};
}
+
}
diff --git a/COFTester/ViewModels/MainViewModel.cs b/COFTester/ViewModels/MainViewModel.cs
index 669d9cc..8703f6e 100644
--- a/COFTester/ViewModels/MainViewModel.cs
+++ b/COFTester/ViewModels/MainViewModel.cs
@@ -1071,6 +1071,13 @@ public sealed class MainViewModel : ObservableObject, IDisposable
var finalStandardDeviation = ResolveCompletedMeasurementValue(
ResolveRepresentativeValue(GetActiveReplicateCount(), StandardDeviation1, StandardDeviation2),
BuildStandardDeviationFallback(ReciprocatingRecords.Select(record => record.StaticCoefficient)));
+ var averageStaticForceN = AverageFiniteNullableValues(ReciprocatingRecords.Select(record => record.StaticForceN));
+ var averageKineticForceN = AverageFiniteNullableValues(ReciprocatingRecords.Select(record => record.KineticForceN));
+ var reciprocatingAverageEndIndex = ReciprocatingRecords
+ .Where(record => record.HasData)
+ .Select(record => record.Index)
+ .DefaultIfEmpty(0)
+ .Max();
CurrentStaticCoefficient = finalStaticCoefficient;
CurrentKineticCoefficient = finalKineticCoefficient;
@@ -1093,6 +1100,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
StandardDeviation2 = StandardDeviation2,
PeakForceN = CurrentPeakForceN,
AverageForceN = CurrentAverageForceN,
+ AverageStaticForceN = averageStaticForceN,
+ AverageKineticForceN = averageKineticForceN,
+ ReciprocatingAverageEndIndex = reciprocatingAverageEndIndex,
Judgement = "完成",
SampleCount = _currentRunSamples.Count
};
@@ -1307,6 +1317,15 @@ public sealed class MainViewModel : ObservableObject, IDisposable
return values.Length == 0 ? 0 : values.Average();
}
+ private static double? AverageFiniteNullableValues(IEnumerable values)
+ {
+ var finiteValues = values
+ .Where(value => value is { } number && IsFinite(number))
+ .Select(value => value!.Value)
+ .ToArray();
+ return finiteValues.Length == 0 ? null : finiteValues.Average();
+ }
+
private static IEnumerable BuildStandardDeviationFallback(IEnumerable values)
{
var samples = values