This commit is contained in:
GukSang.Jin
2026-05-11 15:12:09 +08:00
parent eb4ee774d6
commit 9f205264e7
3 changed files with 103 additions and 94 deletions

View File

@@ -328,7 +328,7 @@
<Grid Background="{StaticResource ShellGradientBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="SidebarColumn" Width="118" />
<ColumnDefinition Width="360" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
@@ -337,7 +337,7 @@
Background="{StaticResource SidebarBrush}"
BorderBrush="#B9C7D1"
BorderThickness="0,0,1,0"
Padding="12">
Padding="18,16,14,16">
<Grid>
<ScrollViewer x:Name="SidebarDetails"
VerticalScrollBarVisibility="Auto"
@@ -346,21 +346,10 @@
<Border Style="{StaticResource PanelBorderStyle}"
Background="{StaticResource SurfaceRaisedBrush}"
Margin="0,0,0,14">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Margin="0,0,12,0">
<TextBlock Style="{StaticResource SectionTitleStyle}" Text="试验参数" />
<TextBlock Style="{StaticResource SectionHintStyle}" Text="参数编辑保留在侧栏,主界面聚焦控制、曲线和结果。" />
</StackPanel>
<Button Grid.Column="1"
Width="88"
Style="{StaticResource GhostButtonStyle}"
Click="ToggleSidebar_Click"
Content="收起" />
</Grid>
<StackPanel>
<TextBlock Style="{StaticResource SectionTitleStyle}" Text="试验参数" />
<TextBlock Style="{StaticResource SectionHintStyle}" Text="参数编辑保留在侧栏,主界面聚焦控制、曲线和结果。" />
</StackPanel>
</Border>
<Border Style="{StaticResource PanelBorderStyle}"
@@ -505,48 +494,6 @@
</StackPanel>
</ScrollViewer>
<Grid x:Name="SidebarCollapsedRail" Visibility="Collapsed">
<Border Style="{StaticResource PanelBorderStyle}"
Background="{StaticResource SurfaceRaisedBrush}"
Padding="10">
<StackPanel VerticalAlignment="Center">
<Button Width="52"
Height="42"
Padding="0"
HorizontalAlignment="Center"
Margin="0,0,0,12"
Style="{StaticResource GhostButtonStyle}"
Click="ToggleSidebar_Click"
Content=">" />
<TextBlock HorizontalAlignment="Center"
FontSize="15"
FontWeight="SemiBold"
Text="参数" />
<Border Height="4"
Margin="8,14,8,14"
Background="{StaticResource AccentStripBrush}"
CornerRadius="2" />
<Border Style="{StaticResource InsetCardStyle}" Padding="10">
<StackPanel>
<TextBlock HorizontalAlignment="Center"
Style="{StaticResource CaptionStyle}"
Text="状态" />
<TextBlock HorizontalAlignment="Center"
FontWeight="SemiBold"
Text="{Binding MachineState, Mode=OneWay}" />
<TextBlock Margin="0,10,0,0"
HorizontalAlignment="Center"
Style="{StaticResource CaptionStyle}"
Text="{Binding Recipe.BatchNumber, Mode=TwoWay}" />
</StackPanel>
</Border>
<TextBlock Margin="0,14,0,0"
HorizontalAlignment="Center"
Style="{StaticResource SectionHintStyle}"
Text="展开编辑" />
</StackPanel>
</Border>
</Grid>
</Grid>
</Border>
@@ -767,8 +714,11 @@
<Border Grid.Column="0"
Style="{StaticResource PanelBorderStyle}"
Margin="0,0,16,0">
<Grid>
Margin="0,0,16,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
@@ -784,13 +734,20 @@
BorderBrush="#C2D0D9"
BorderThickness="1"
CornerRadius="14"
Padding="8">
Padding="8"
MinHeight="360"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Border Background="{StaticResource ChartSurfaceBrush}"
BorderBrush="#98A9B7"
BorderThickness="1"
CornerRadius="10"
Padding="6">
Padding="8,8,8,18"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<lvc:CartesianChart x:Name="RealtimeForceChart"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Series="{Binding ForceSeries}"
XAxes="{Binding ForceXAxes}"
YAxes="{Binding ForceYAxes}"

View File

@@ -4,22 +4,20 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using COFTester.ViewModels;
using LiveChartsCore.Kernel;
namespace COFTester;
public partial class MainWindow : Window
{
private const double ExpandedSidebarWidth = 360;
private const double CollapsedSidebarWidth = 118;
private readonly MainViewModel _viewModel = new();
private bool _isSidebarCollapsed;
private bool _isRealtimeChartLayoutRefreshQueued;
public MainWindow()
{
InitializeComponent();
DataContext = _viewModel;
SetSidebarCollapsed(true);
QueueRealtimeChartLayoutRefresh();
}
protected override void OnClosed(EventArgs e)
@@ -28,9 +26,16 @@ public partial class MainWindow : Window
base.OnClosed(e);
}
private void ToggleSidebar_Click(object sender, RoutedEventArgs e)
protected override void OnContentRendered(EventArgs e)
{
SetSidebarCollapsed(!_isSidebarCollapsed);
base.OnContentRendered(e);
QueueRealtimeChartLayoutRefresh();
}
protected override void OnStateChanged(EventArgs e)
{
base.OnStateChanged(e);
QueueRealtimeChartLayoutRefresh();
}
private void ShowRealtimeTab_Click(object sender, RoutedEventArgs e)
@@ -96,16 +101,6 @@ public partial class MainWindow : Window
}
}
private void SetSidebarCollapsed(bool collapsed)
{
_isSidebarCollapsed = collapsed;
SidebarColumn.Width = new GridLength(collapsed ? CollapsedSidebarWidth : ExpandedSidebarWidth);
SidebarDetails.Visibility = collapsed ? Visibility.Collapsed : Visibility.Visible;
SidebarCollapsedRail.Visibility = collapsed ? Visibility.Visible : Visibility.Collapsed;
SidebarHost.Padding = collapsed ? new Thickness(12) : new Thickness(18, 16, 14, 16);
QueueRealtimeChartLayoutRefresh();
}
private void QueueRealtimeChartLayoutRefresh()
{
if (_isRealtimeChartLayoutRefreshQueued)
@@ -114,13 +109,25 @@ public partial class MainWindow : Window
}
_isRealtimeChartLayoutRefreshQueued = true;
Dispatcher.BeginInvoke(RefreshRealtimeChartLayout, DispatcherPriority.ContextIdle);
Dispatcher.BeginInvoke(RunRealtimeChartLayoutRefreshSequence, DispatcherPriority.Loaded);
}
private void RunRealtimeChartLayoutRefreshSequence()
{
RefreshRealtimeChartLayout();
Dispatcher.BeginInvoke(() =>
{
RefreshRealtimeChartLayout();
Dispatcher.BeginInvoke(() =>
{
RefreshRealtimeChartLayout();
_isRealtimeChartLayoutRefreshQueued = false;
}, DispatcherPriority.ApplicationIdle);
}, DispatcherPriority.Render);
}
private void RefreshRealtimeChartLayout()
{
_isRealtimeChartLayoutRefreshQueued = false;
if (RealtimeForceChart.ActualWidth <= 0 || RealtimeForceChart.ActualHeight <= 0)
{
return;
@@ -129,5 +136,10 @@ public partial class MainWindow : Window
RealtimeForceChart.InvalidateMeasure();
RealtimeForceChart.InvalidateArrange();
RealtimeForceChart.UpdateLayout();
RealtimeForceChart.CoreChart.Update(new ChartUpdateParams
{
IsAutomaticUpdate = false,
Throttling = false
});
}
}

View File

@@ -54,6 +54,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
private static readonly TimeSpan RealtimeChartRefreshInterval = TimeSpan.FromMilliseconds(250);
private const double RealtimeChartDisplacementStepMm = 0.5;
private const double RealtimeChartXAxisPaddingMm = 5;
private const double MinimumEmptyChartXAxisMaxLimit = 1;
private const double EmptyChartYAxisMaxLimit = 0.5;
private const double EmptyChartPointForceN = 0.001;
private readonly DispatcherTimer _timer;
private readonly DispatcherTimer _deviceReconnectTimer;
@@ -231,8 +234,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
}
];
_lastForceXAxisMaxLimit = 1;
_lastForceYAxisMaxLimit = 0.5;
_lastForceXAxisMaxLimit = GetEmptyChartXAxisMaxLimit();
_lastForceYAxisMaxLimit = EmptyChartYAxisMaxLimit;
ForceXAxes = CreateForceXAxes(_lastForceXAxisMaxLimit);
ForceYAxes = CreateForceYAxes(_lastForceYAxisMaxLimit);
@@ -241,13 +244,14 @@ public sealed class MainViewModel : ObservableObject, IDisposable
Xi = Recipe.TravelMm * 0.35,
Xj = Recipe.TravelMm,
Yi = 0,
Yj = 1,
Yj = EmptyChartYAxisMaxLimit,
Fill = new SolidColorPaint(new SKColor(0, 105, 180, 18)),
Stroke = new SolidColorPaint(new SKColor(0, 105, 180, 70), 1)
};
ForceSections = [_kineticBand];
ShowEmptyRealtimeChartFrame();
LoadHistoryFromDatabase();
UpdateDerivedValues();
AddInfoEvent("系统启动,正在初始化 PLC 通信与本地数据存储。");
@@ -1262,7 +1266,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
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)
Padding = new Padding(8, 0, 12, 8)
}
];
}
@@ -1661,10 +1665,43 @@ public sealed class MainViewModel : ObservableObject, IDisposable
}
else
{
_currentPointSample.Clear();
ShowEmptyRealtimeChartFrame();
}
}
private void ShowEmptyRealtimeChartFrame()
{
_forceSamples.Clear();
_peakLineSamples.Clear();
_averageLineSamples.Clear();
_currentPointSample.Clear();
_currentPointSample.Add(new ObservablePoint(0, EmptyChartPointForceN));
SetAxisLimits(GetEmptyChartXAxisMaxLimit(), EmptyChartYAxisMaxLimit);
_kineticBand.Xi = Recipe.TravelMm * 0.35;
_kineticBand.Xj = Recipe.TravelMm;
_kineticBand.Yi = 0;
_kineticBand.Yj = EmptyChartYAxisMaxLimit;
OnPropertyChanged(nameof(ForceSections));
}
private void RefreshEmptyRealtimeChartFrameIfVisible()
{
if (_machineRuntimeState != MachineRuntimeState.Idle ||
_isShowingHistoricalRun ||
_currentRunSamples.Count > 0 ||
_forceSamples.Count > 0)
{
return;
}
ShowEmptyRealtimeChartFrame();
}
private double GetEmptyChartXAxisMaxLimit()
{
return Math.Max(Recipe.TravelMm, MinimumEmptyChartXAxisMaxLimit);
}
private void ResetRealtimePresentation()
{
_forceSamples.Clear();
@@ -1692,13 +1729,7 @@ public sealed class MainViewModel : ObservableObject, IDisposable
CurrentKineticCoefficient = 0;
TrialProgressPercent = 0;
SetAxisLimits(1, 0.5);
_kineticBand.Xi = Recipe.TravelMm * 0.35;
_kineticBand.Xj = Recipe.TravelMm;
_kineticBand.Yi = 0;
_kineticBand.Yj = 0.5;
OnPropertyChanged(nameof(ForceSections));
ShowEmptyRealtimeChartFrame();
}
private void ResetActiveRunContext()
@@ -1799,6 +1830,12 @@ public sealed class MainViewModel : ObservableObject, IDisposable
{
AddWarningEvent($"测试参数写入 PLC 失败 {e.PropertyName}: {ex.Message}");
}
if (e.PropertyName == nameof(TestRecipe.TravelMm))
{
RefreshEmptyRealtimeChartFrameIfVisible();
RaiseStatusProperties();
}
}
private async Task SyncRecipeFromPlcAsync()
@@ -1830,6 +1867,9 @@ public sealed class MainViewModel : ObservableObject, IDisposable
{
_isSyncingRecipeFromPlc = false;
}
RefreshEmptyRealtimeChartFrameIfVisible();
RaiseStatusProperties();
}
private async Task SyncRecipePropertyToPlcAsync(string propertyName)