更新
This commit is contained in:
@@ -394,14 +394,14 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平位移" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平位移" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding Recipe.TravelMm, Mode=TwoWay, StringFormat=F0 mm}" />
|
Text="{Binding Recipe.TravelMm, Mode=TwoWay, StringFormat={}{0:F0} mm}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}">
|
<Border Style="{StaticResource InsetCardStyle}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平速度" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平速度" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding Recipe.SpeedMmPerMin, Mode=TwoWay, StringFormat=F0 mm/min}" />
|
Text="{Binding Recipe.SpeedMmPerMin, Mode=TwoWay, StringFormat={}{0:F0} mm/min}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
@@ -612,17 +612,6 @@
|
|||||||
<TextBlock VerticalAlignment="Center" FontWeight="SemiBold" Text="暂停" />
|
<TextBlock VerticalAlignment="Center" FontWeight="SemiBold" Text="暂停" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,10,10">
|
|
||||||
<DockPanel>
|
|
||||||
<Ellipse Width="12"
|
|
||||||
Height="12"
|
|
||||||
Fill="{Binding InterlockIndicatorBrush, Mode=OneWay}"
|
|
||||||
Stroke="#8DA0AE"
|
|
||||||
StrokeThickness="1"
|
|
||||||
Margin="0,2,8,0" />
|
|
||||||
<TextBlock VerticalAlignment="Center" FontWeight="SemiBold" Text="联锁" />
|
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,0,10">
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,0,10">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<Ellipse Width="12"
|
<Ellipse Width="12"
|
||||||
@@ -672,10 +661,6 @@
|
|||||||
Style="{StaticResource PrimaryButtonStyle}"
|
Style="{StaticResource PrimaryButtonStyle}"
|
||||||
Command="{Binding PlcStartCommand}"
|
Command="{Binding PlcStartCommand}"
|
||||||
Content="{Binding StartButtonText, Mode=OneWay}" />
|
Content="{Binding StartButtonText, Mode=OneWay}" />
|
||||||
<Button Width="132"
|
|
||||||
Style="{StaticResource SecondaryButtonStyle}"
|
|
||||||
Command="{Binding PauseCommand}"
|
|
||||||
Content="暂停" />
|
|
||||||
<Button Width="132"
|
<Button Width="132"
|
||||||
Style="{StaticResource DangerButtonStyle}"
|
Style="{StaticResource DangerButtonStyle}"
|
||||||
Command="{Binding PlcStopCommand}"
|
Command="{Binding PlcStopCommand}"
|
||||||
@@ -696,19 +681,31 @@
|
|||||||
<WrapPanel>
|
<WrapPanel>
|
||||||
<Button Width="108"
|
<Button Width="108"
|
||||||
Style="{StaticResource CompactButtonStyle}"
|
Style="{StaticResource CompactButtonStyle}"
|
||||||
Command="{Binding RiseCommand}"
|
Tag="Up"
|
||||||
|
PreviewMouseLeftButtonDown="TableMotionButton_PreviewMouseLeftButtonDown"
|
||||||
|
PreviewMouseLeftButtonUp="TableMotionButton_PreviewMouseLeftButtonUp"
|
||||||
|
LostMouseCapture="TableMotionButton_LostMouseCapture"
|
||||||
Content="上升" />
|
Content="上升" />
|
||||||
<Button Width="108"
|
<Button Width="108"
|
||||||
Style="{StaticResource CompactButtonStyle}"
|
Style="{StaticResource CompactButtonStyle}"
|
||||||
Command="{Binding LowerCommand}"
|
Tag="Down"
|
||||||
|
PreviewMouseLeftButtonDown="TableMotionButton_PreviewMouseLeftButtonDown"
|
||||||
|
PreviewMouseLeftButtonUp="TableMotionButton_PreviewMouseLeftButtonUp"
|
||||||
|
LostMouseCapture="TableMotionButton_LostMouseCapture"
|
||||||
Content="下降" />
|
Content="下降" />
|
||||||
<Button Width="108"
|
<Button Width="108"
|
||||||
Style="{StaticResource CompactButtonStyle}"
|
Style="{StaticResource CompactButtonStyle}"
|
||||||
Command="{Binding BackCommand}"
|
Tag="Left"
|
||||||
|
PreviewMouseLeftButtonDown="TableMotionButton_PreviewMouseLeftButtonDown"
|
||||||
|
PreviewMouseLeftButtonUp="TableMotionButton_PreviewMouseLeftButtonUp"
|
||||||
|
LostMouseCapture="TableMotionButton_LostMouseCapture"
|
||||||
Content="后退" />
|
Content="后退" />
|
||||||
<Button Width="108"
|
<Button Width="108"
|
||||||
Style="{StaticResource CompactButtonStyle}"
|
Style="{StaticResource CompactButtonStyle}"
|
||||||
Command="{Binding ForwardCommand}"
|
Tag="Right"
|
||||||
|
PreviewMouseLeftButtonDown="TableMotionButton_PreviewMouseLeftButtonDown"
|
||||||
|
PreviewMouseLeftButtonUp="TableMotionButton_PreviewMouseLeftButtonUp"
|
||||||
|
LostMouseCapture="TableMotionButton_LostMouseCapture"
|
||||||
Content="前进" />
|
Content="前进" />
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -837,28 +834,28 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="实时静摩擦系数" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="实时静摩擦系数" />
|
||||||
<TextBlock Style="{StaticResource MetricValueStyle}"
|
<TextBlock Style="{StaticResource MetricValueStyle}"
|
||||||
Text="{Binding CurrentStaticCoefficient, Mode=OneWay, StringFormat=F3}" />
|
Text="{Binding CurrentStaticCoefficient, Mode=OneWay, StringFormat={}{0:F3}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource MetricCardStyle}" Margin="0,0,0,10">
|
<Border Style="{StaticResource MetricCardStyle}" Margin="0,0,0,10">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="实时动摩擦系数" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="实时动摩擦系数" />
|
||||||
<TextBlock Style="{StaticResource MetricValueStyle}"
|
<TextBlock Style="{StaticResource MetricValueStyle}"
|
||||||
Text="{Binding CurrentKineticCoefficient, Mode=OneWay, StringFormat=F3}" />
|
Text="{Binding CurrentKineticCoefficient, Mode=OneWay, StringFormat={}{0:F3}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource MetricCardStyle}" Margin="0,0,10,0">
|
<Border Style="{StaticResource MetricCardStyle}" Margin="0,0,10,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="峰值力" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="峰值力" />
|
||||||
<TextBlock Style="{StaticResource MetricValueStyle}"
|
<TextBlock Style="{StaticResource MetricValueStyle}"
|
||||||
Text="{Binding CurrentPeakForceN, Mode=OneWay, StringFormat=F3 N}" />
|
Text="{Binding CurrentPeakForceN, Mode=OneWay, StringFormat={}{0:F3} N}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource MetricCardStyle}">
|
<Border Style="{StaticResource MetricCardStyle}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="稳定段平均力" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="稳定段平均力" />
|
||||||
<TextBlock Style="{StaticResource MetricValueStyle}"
|
<TextBlock Style="{StaticResource MetricValueStyle}"
|
||||||
Text="{Binding CurrentAverageForceN, Mode=OneWay, StringFormat=F3 N}" />
|
Text="{Binding CurrentAverageForceN, Mode=OneWay, StringFormat={}{0:F3} N}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
@@ -873,32 +870,46 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="实时力值" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="实时力值" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding CurrentForceN, Mode=OneWay, StringFormat=F3 N}" />
|
Text="{Binding CurrentForceN, Mode=OneWay, StringFormat={}{0:F3} N}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,0,10">
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,0,10">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平速度" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平速度" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding CurrentSpeedMmPerMin, Mode=OneWay, StringFormat=F1 mm/min}" />
|
Text="{Binding CurrentSpeedMmPerMin, Mode=OneWay, StringFormat={}{0:F1} mm/min}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,10,0">
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,10,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="升降速度" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="升降速度" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding CurrentLiftSpeed, Mode=OneWay, StringFormat=F1 mm/min}" />
|
Text="{Binding CurrentLiftSpeed, Mode=OneWay, StringFormat={}{0:F1} mm/min}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}">
|
<Border Style="{StaticResource InsetCardStyle}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="批次统计" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="升降位置" />
|
||||||
<TextBlock FontWeight="SemiBold"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
TextWrapping="Wrap"
|
Text="{Binding CurrentLiftPosition, Mode=OneWay, StringFormat={}{0:F1} mm}" />
|
||||||
Text="{Binding TraceabilitySummary, Mode=OneWay}" />
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,10,0">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="水平位置" />
|
||||||
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
|
Text="{Binding CurrentHorizontalPosition, Mode=OneWay, StringFormat={}{0:F1} mm}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,14,0,0">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="批次统计" />
|
||||||
|
<TextBlock FontWeight="SemiBold"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
Text="{Binding TraceabilitySummary, Mode=OneWay}" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,14,0,0">
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,14,0,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="导出摘要" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="导出摘要" />
|
||||||
@@ -912,28 +923,28 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="静摩擦系数 1" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="静摩擦系数 1" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding StaticCoefficient1, Mode=OneWay, StringFormat=F3}" />
|
Text="{Binding StaticCoefficient1, Mode=OneWay, StringFormat={}{0:F3}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,0,10">
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,0,10">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="静摩擦系数 2" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="静摩擦系数 2" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding StaticCoefficient2, Mode=OneWay, StringFormat=F3}" />
|
Text="{Binding StaticCoefficient2, Mode=OneWay, StringFormat={}{0:F3}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,10,0">
|
<Border Style="{StaticResource InsetCardStyle}" Margin="0,0,10,0">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="动摩擦系数 1" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="动摩擦系数 1" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding KineticCoefficient1, Mode=OneWay, StringFormat=F3}" />
|
Text="{Binding KineticCoefficient1, Mode=OneWay, StringFormat={}{0:F3}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource InsetCardStyle}">
|
<Border Style="{StaticResource InsetCardStyle}">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="动摩擦系数 2" />
|
<TextBlock Style="{StaticResource MetricLabelStyle}" Text="动摩擦系数 2" />
|
||||||
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
<TextBlock Style="{StaticResource SmallMetricValueStyle}"
|
||||||
Text="{Binding KineticCoefficient2, Mode=OneWay, StringFormat=F3}" />
|
Text="{Binding KineticCoefficient2, Mode=OneWay, StringFormat={}{0:F3}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</UniformGrid>
|
</UniformGrid>
|
||||||
@@ -1070,8 +1081,8 @@
|
|||||||
<DataGridTextColumn Header="轮次" Binding="{Binding RunIndex}" Width="0.8*" />
|
<DataGridTextColumn Header="轮次" Binding="{Binding RunIndex}" Width="0.8*" />
|
||||||
<DataGridTextColumn Header="时间" Binding="{Binding CompletedAtLabel}" Width="1.2*" />
|
<DataGridTextColumn Header="时间" Binding="{Binding CompletedAtLabel}" Width="1.2*" />
|
||||||
<DataGridTextColumn Header="批次" Binding="{Binding BatchNumber}" Width="1.1*" />
|
<DataGridTextColumn Header="批次" Binding="{Binding BatchNumber}" Width="1.1*" />
|
||||||
<DataGridTextColumn Header="μs" Binding="{Binding StaticCoefficient, StringFormat=F3}" Width="0.9*" />
|
<DataGridTextColumn Header="μs" Binding="{Binding StaticCoefficient, StringFormat={}{0:F3}}" Width="0.9*" />
|
||||||
<DataGridTextColumn Header="μk" Binding="{Binding KineticCoefficient, StringFormat=F3}" Width="0.9*" />
|
<DataGridTextColumn Header="μk" Binding="{Binding KineticCoefficient, StringFormat={}{0:F3}}" Width="0.9*" />
|
||||||
<DataGridTextColumn Header="模式" Binding="{Binding TestMode}" Width="1.1*" />
|
<DataGridTextColumn Header="模式" Binding="{Binding TestMode}" Width="1.1*" />
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
using COFTester.ViewModels;
|
using COFTester.ViewModels;
|
||||||
|
|
||||||
namespace COFTester;
|
namespace COFTester;
|
||||||
@@ -39,6 +41,44 @@ public partial class MainWindow : Window
|
|||||||
MainWorkspaceTabs.SelectedIndex = 1;
|
MainWorkspaceTabs.SelectedIndex = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TableMotionButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (sender is not Button button || button.Tag is not string direction)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.CaptureMouse();
|
||||||
|
_viewModel.BeginTableMotion(direction);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TableMotionButton_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
EndTableMotionFromButton(sender);
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TableMotionButton_LostMouseCapture(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
EndTableMotionFromButton(sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndTableMotionFromButton(object sender)
|
||||||
|
{
|
||||||
|
if (sender is not Button button || button.Tag is not string direction)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewModel.EndTableMotion(direction);
|
||||||
|
|
||||||
|
if (Mouse.Captured == button)
|
||||||
|
{
|
||||||
|
button.ReleaseMouseCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetSidebarCollapsed(bool collapsed)
|
private void SetSidebarCollapsed(bool collapsed)
|
||||||
{
|
{
|
||||||
_isSidebarCollapsed = collapsed;
|
_isSidebarCollapsed = collapsed;
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ namespace COFTester.Models;
|
|||||||
|
|
||||||
public sealed class TestRecipe : ObservableObject
|
public sealed class TestRecipe : ObservableObject
|
||||||
{
|
{
|
||||||
private string _productCode = string.Empty;
|
public const string DefaultProductCode = "COF-DEFAULT";
|
||||||
private string _batchNumber = string.Empty;
|
|
||||||
|
private string _productCode = DefaultProductCode;
|
||||||
|
private string _batchNumber = CreateDefaultBatchNumber();
|
||||||
private string _testMode = "膜/膜";
|
private string _testMode = "膜/膜";
|
||||||
private string _counterfaceMaterial = "同材质";
|
private string _counterfaceMaterial = "同材质";
|
||||||
private string _direction = "MD";
|
private string _direction = "MD";
|
||||||
@@ -88,4 +90,22 @@ public sealed class TestRecipe : ObservableObject
|
|||||||
get => _specimenDescription;
|
get => _specimenDescription;
|
||||||
set => SetProperty(ref _specimenDescription, value);
|
set => SetProperty(ref _specimenDescription, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ApplyDefaultIdentifiers()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(ProductCode))
|
||||||
|
{
|
||||||
|
ProductCode = DefaultProductCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(BatchNumber))
|
||||||
|
{
|
||||||
|
BatchNumber = CreateDefaultBatchNumber();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CreateDefaultBatchNumber()
|
||||||
|
{
|
||||||
|
return $"BATCH-{DateTime.Now:yyyyMMdd}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
private bool _isPlcCommandBusy;
|
private bool _isPlcCommandBusy;
|
||||||
private bool _isSyncingRecipeFromPlc;
|
private bool _isSyncingRecipeFromPlc;
|
||||||
private bool _activeRunStartedByPlc;
|
private bool _activeRunStartedByPlc;
|
||||||
|
private ushort? _activeTableMotionCoil;
|
||||||
|
private ushort? _pendingTableMotionStopCoil;
|
||||||
private MachineRuntimeState _machineRuntimeState = MachineRuntimeState.Idle;
|
private MachineRuntimeState _machineRuntimeState = MachineRuntimeState.Idle;
|
||||||
private string _plcCommandSummary = "等待 PLC 控制指令。";
|
private string _plcCommandSummary = "等待 PLC 控制指令。";
|
||||||
|
|
||||||
@@ -319,6 +321,45 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
public RelayCommand ClearHistoryCommand => _clearHistoryCommand;
|
public RelayCommand ClearHistoryCommand => _clearHistoryCommand;
|
||||||
|
|
||||||
|
public void BeginTableMotion(string direction)
|
||||||
|
{
|
||||||
|
if (!TryGetTableMotionCoil(direction, out var actionName, out var coilAddress))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_activeTableMotionCoil is not null || _isPlcCommandBusy)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_activeTableMotionCoil = coilAddress;
|
||||||
|
_pendingTableMotionStopCoil = null;
|
||||||
|
_ = SetTableMotionCoilAsync(actionName, coilAddress, value: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndTableMotion(string direction)
|
||||||
|
{
|
||||||
|
if (!TryGetTableMotionCoil(direction, out var actionName, out var coilAddress))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_activeTableMotionCoil != coilAddress)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_isPlcCommandBusy)
|
||||||
|
{
|
||||||
|
_pendingTableMotionStopCoil = coilAddress;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_activeTableMotionCoil = null;
|
||||||
|
_ = SetTableMotionCoilAsync(actionName, coilAddress, value: false);
|
||||||
|
}
|
||||||
|
|
||||||
public string MachineState
|
public string MachineState
|
||||||
{
|
{
|
||||||
get => _machineState;
|
get => _machineState;
|
||||||
@@ -603,6 +644,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
OnPropertyChanged(nameof(DeviceLastConnectedAtLabel));
|
OnPropertyChanged(nameof(DeviceLastConnectedAtLabel));
|
||||||
AddInfoEvent($"设备通信已连接: {_deviceConnectionService.Endpoint}");
|
AddInfoEvent($"设备通信已连接: {_deviceConnectionService.Endpoint}");
|
||||||
await SyncRecipeFromPlcAsync();
|
await SyncRecipeFromPlcAsync();
|
||||||
|
_deviceDataReader.Initialize(Recipe);
|
||||||
|
EnsureRealtimeMonitorTimerRunning();
|
||||||
RaiseCommandStates();
|
RaiseCommandStates();
|
||||||
|
|
||||||
if (_machineRuntimeState == MachineRuntimeState.Idle)
|
if (_machineRuntimeState == MachineRuntimeState.Idle)
|
||||||
@@ -681,6 +724,8 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
private bool TryValidateStartOrResume()
|
private bool TryValidateStartOrResume()
|
||||||
{
|
{
|
||||||
|
Recipe.ApplyDefaultIdentifiers();
|
||||||
|
|
||||||
var validationErrors = ValidateRecipe().ToArray();
|
var validationErrors = ValidateRecipe().ToArray();
|
||||||
if (validationErrors.Length == 0)
|
if (validationErrors.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -768,7 +813,6 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_timer.Stop();
|
|
||||||
_machineRuntimeState = MachineRuntimeState.Paused;
|
_machineRuntimeState = MachineRuntimeState.Paused;
|
||||||
MachineState = "暂停";
|
MachineState = "暂停";
|
||||||
StateBrush = BrushFromHex("#C49645");
|
StateBrush = BrushFromHex("#C49645");
|
||||||
@@ -779,7 +823,6 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
private void Stop()
|
private void Stop()
|
||||||
{
|
{
|
||||||
_timer.Stop();
|
|
||||||
_isShowingHistoricalRun = false;
|
_isShowingHistoricalRun = false;
|
||||||
_displayedRecipeSnapshot = _activeRecipeSnapshot ?? TestRecipeSnapshot.FromRecipe(Recipe);
|
_displayedRecipeSnapshot = _activeRecipeSnapshot ?? TestRecipeSnapshot.FromRecipe(Recipe);
|
||||||
ResetActiveRunContext();
|
ResetActiveRunContext();
|
||||||
@@ -840,7 +883,6 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_timer.Stop();
|
|
||||||
_isShowingHistoricalRun = false;
|
_isShowingHistoricalRun = false;
|
||||||
_displayedRecipeSnapshot = _activeRecipeSnapshot ?? TestRecipeSnapshot.FromRecipe(Recipe);
|
_displayedRecipeSnapshot = _activeRecipeSnapshot ?? TestRecipeSnapshot.FromRecipe(Recipe);
|
||||||
ResetActiveRunContext();
|
ResetActiveRunContext();
|
||||||
@@ -877,20 +919,17 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var frame = await ReadRealtimeFrameAsync();
|
var frame = await ReadRealtimeFrameAsync();
|
||||||
|
if (!_isShowingHistoricalRun)
|
||||||
|
{
|
||||||
|
UpdateLiveProcessSnapshot(frame);
|
||||||
|
}
|
||||||
|
|
||||||
if (_machineRuntimeState != MachineRuntimeState.Running)
|
if (_machineRuntimeState != MachineRuntimeState.Running)
|
||||||
{
|
{
|
||||||
|
RaiseStatusProperties();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentForceN = Math.Max(frame.ForceN, 0.001);
|
|
||||||
CurrentDisplacementMm = frame.DisplacementMm;
|
|
||||||
CurrentSpeedMmPerMin = frame.SpeedMmPerMin;
|
|
||||||
CurrentLiftSpeed = frame.LiftSpeed;
|
|
||||||
CurrentLiftDisplacement = frame.LiftDisplacement;
|
|
||||||
CurrentLiftPosition = frame.LiftPosition;
|
|
||||||
CurrentHorizontalPosition = frame.HorizontalPosition;
|
|
||||||
|
|
||||||
_forceSamples.Add(new ObservablePoint(frame.DisplacementMm, CurrentForceN));
|
_forceSamples.Add(new ObservablePoint(frame.DisplacementMm, CurrentForceN));
|
||||||
_currentRunSamples.Add(new RawSampleRecord
|
_currentRunSamples.Add(new RawSampleRecord
|
||||||
{
|
{
|
||||||
@@ -931,7 +970,14 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HandleRealtimeSamplingFailure(ex);
|
if (_machineRuntimeState == MachineRuntimeState.Running || HasRecoverableActiveRunContext())
|
||||||
|
{
|
||||||
|
HandleRealtimeSamplingFailure(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
HandleRealtimeMonitorFailure(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -939,10 +985,19 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateLiveProcessSnapshot(ProcessFrame frame)
|
||||||
|
{
|
||||||
|
CurrentForceN = Math.Max(frame.ForceN, 0.001);
|
||||||
|
CurrentDisplacementMm = frame.DisplacementMm;
|
||||||
|
CurrentSpeedMmPerMin = frame.SpeedMmPerMin;
|
||||||
|
CurrentLiftSpeed = frame.LiftSpeed;
|
||||||
|
CurrentLiftDisplacement = frame.LiftDisplacement;
|
||||||
|
CurrentLiftPosition = frame.LiftPosition;
|
||||||
|
CurrentHorizontalPosition = frame.HorizontalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
private void FinalizeRun()
|
private void FinalizeRun()
|
||||||
{
|
{
|
||||||
_timer.Stop();
|
|
||||||
|
|
||||||
var record = new RunRecord
|
var record = new RunRecord
|
||||||
{
|
{
|
||||||
RunId = _activeRunId,
|
RunId = _activeRunId,
|
||||||
@@ -1071,7 +1126,104 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
|
|
||||||
private bool CanExecutePlcCommand()
|
private bool CanExecutePlcCommand()
|
||||||
{
|
{
|
||||||
return !_isPlcCommandBusy;
|
return !_isPlcCommandBusy && _activeTableMotionCoil is null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetTableMotionCoil(string direction, out string actionName, out ushort coilAddress)
|
||||||
|
{
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case "Up":
|
||||||
|
actionName = "上";
|
||||||
|
coilAddress = CoilRise;
|
||||||
|
return true;
|
||||||
|
case "Down":
|
||||||
|
actionName = "下";
|
||||||
|
coilAddress = CoilLower;
|
||||||
|
return true;
|
||||||
|
case "Left":
|
||||||
|
actionName = "左";
|
||||||
|
coilAddress = CoilBack;
|
||||||
|
return true;
|
||||||
|
case "Right":
|
||||||
|
actionName = "右";
|
||||||
|
coilAddress = CoilForward;
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
actionName = string.Empty;
|
||||||
|
coilAddress = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> SetTableMotionCoilAsync(string actionName, ushort coilAddress, bool value)
|
||||||
|
{
|
||||||
|
if (_isPlcCommandBusy)
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
_pendingTableMotionStopCoil = coilAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isPlcCommandBusy = true;
|
||||||
|
RaiseCommandStates();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_deviceConnectionService.IsConnected)
|
||||||
|
{
|
||||||
|
await InitializeDeviceConnectionAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_deviceConnectionService.IsConnected)
|
||||||
|
{
|
||||||
|
PlcCommandSummary = $"PLC 台面{actionName}{(value ? "启动" : "停止")}失败: 设备未连接。";
|
||||||
|
|
||||||
|
if (value && _activeTableMotionCoil == coilAddress)
|
||||||
|
{
|
||||||
|
_activeTableMotionCoil = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _deviceConnectionService.WriteSingleCoilAsync(ModbusSlaveAddress, coilAddress, value);
|
||||||
|
PlcCommandSummary = $"PLC 台面{actionName}{(value ? "启动" : "停止")}已发送: M{coilAddress}={(value ? "ON" : "OFF")}";
|
||||||
|
AddInfoEvent($"PLC 台面点动写入成功: {actionName} M{coilAddress}={(value ? "ON" : "OFF")}");
|
||||||
|
InterlockMessage = $"已发送 PLC 台面{actionName}{(value ? "启动" : "停止")}指令: M{coilAddress}={(value ? "ON" : "OFF")}";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
PlcCommandSummary = $"PLC 台面{actionName}{(value ? "启动" : "停止")}失败: {ex.Message}";
|
||||||
|
AddWarningEvent($"PLC 台面点动写入失败 {actionName} M{coilAddress}={(value ? "ON" : "OFF")}: {ex.Message}");
|
||||||
|
|
||||||
|
if (value && _activeTableMotionCoil == coilAddress)
|
||||||
|
{
|
||||||
|
_activeTableMotionCoil = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isPlcCommandBusy = false;
|
||||||
|
RaiseCommandStates();
|
||||||
|
|
||||||
|
if (value && _pendingTableMotionStopCoil == coilAddress)
|
||||||
|
{
|
||||||
|
_pendingTableMotionStopCoil = null;
|
||||||
|
|
||||||
|
if (_activeTableMotionCoil == coilAddress)
|
||||||
|
{
|
||||||
|
_activeTableMotionCoil = null;
|
||||||
|
await SetTableMotionCoilAsync(actionName, coilAddress, value: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> ExecutePlcPulseCommandAsync(string actionName, ushort coilAddress, bool updateLocalState = true)
|
private async Task<bool> ExecutePlcPulseCommandAsync(string actionName, ushort coilAddress, bool updateLocalState = true)
|
||||||
@@ -1340,6 +1492,14 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
return await _deviceDataReader.ReadFrameAsync();
|
return await _deviceDataReader.ReadFrameAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureRealtimeMonitorTimerRunning()
|
||||||
|
{
|
||||||
|
if (_deviceConnectionService.IsConnected && !_timer.IsEnabled)
|
||||||
|
{
|
||||||
|
_timer.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleRealtimeSamplingFailure(Exception ex)
|
private void HandleRealtimeSamplingFailure(Exception ex)
|
||||||
{
|
{
|
||||||
_timer.Stop();
|
_timer.Stop();
|
||||||
@@ -1369,6 +1529,26 @@ public sealed class MainViewModel : ObservableObject, IDisposable
|
|||||||
RaiseStatusProperties();
|
RaiseStatusProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleRealtimeMonitorFailure(Exception ex)
|
||||||
|
{
|
||||||
|
_timer.Stop();
|
||||||
|
_deviceConnectionService.Disconnect();
|
||||||
|
_deviceReconnectTimer.Start();
|
||||||
|
_deviceConnectionFailureLogged = true;
|
||||||
|
DeviceConnectionStatus = "重连中";
|
||||||
|
DeviceConnectionSummary = "实时监控读取中断,软件自动重连中。";
|
||||||
|
OnPropertyChanged(nameof(DeviceConnectionIndicatorBrush));
|
||||||
|
RaiseCommandStates();
|
||||||
|
|
||||||
|
if (_machineRuntimeState == MachineRuntimeState.Idle)
|
||||||
|
{
|
||||||
|
InterlockMessage = $"实时监控读取失败: {ex.Message}";
|
||||||
|
}
|
||||||
|
|
||||||
|
AddWarningEvent($"实时监控读取失败: {ex.Message}");
|
||||||
|
RaiseStatusProperties();
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnRecipePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
private async void OnRecipePropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_isSyncingRecipeFromPlc || string.IsNullOrWhiteSpace(e.PropertyName))
|
if (_isSyncingRecipeFromPlc || string.IsNullOrWhiteSpace(e.PropertyName))
|
||||||
|
|||||||
Reference in New Issue
Block a user