更新最新版20260604
This commit is contained in:
@@ -21,9 +21,11 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
private const string DataSheetName = "实时数据";
|
||||
private const int StandardTrialCount = 3;
|
||||
|
||||
public string Export(SlipReportExport report)
|
||||
public string Export(SlipReportExport report, string? exportDirectory = null)
|
||||
{
|
||||
var directory = GetExportDirectory();
|
||||
var directory = string.IsNullOrWhiteSpace(exportDirectory)
|
||||
? GetDefaultExportDirectory()
|
||||
: exportDirectory;
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
var safeNumber = MakeSafeFileName(string.IsNullOrWhiteSpace(report.TestNumber)
|
||||
@@ -52,7 +54,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Services
|
||||
return path;
|
||||
}
|
||||
|
||||
private static string GetExportDirectory()
|
||||
public static string GetDefaultExportDirectory()
|
||||
{
|
||||
var legacyDirectory = Path.Combine(@"D:\文件保存", DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture));
|
||||
if (Directory.Exists(@"D:\"))
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Platform.Storage;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -32,6 +35,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private const string DefaultAdcPortName = "COM4";
|
||||
private const string LegacyPlcPortName = "COM7";
|
||||
private const string LegacyAdcPortName = "COM8";
|
||||
private static readonly TimeSpan ResetButtonPendingTimeout = TimeSpan.FromMilliseconds(800);
|
||||
|
||||
private readonly SlipResistanceDeviceService deviceService = new();
|
||||
private readonly SlipExcelExportService excelExportService = new();
|
||||
@@ -45,6 +49,9 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
|
||||
private bool isLoadingDeviceSettings;
|
||||
private bool wasRunning;
|
||||
private bool isResetButtonPending;
|
||||
private bool hasObservedResetDeviceBusy;
|
||||
private DateTime resetButtonPendingStartedAt = DateTime.MinValue;
|
||||
private string activePlcPortName = string.Empty;
|
||||
private string activeAdcPortName = string.Empty;
|
||||
private int activeBaudRate;
|
||||
@@ -258,7 +265,20 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
private async Task Clear()
|
||||
{
|
||||
ApplyConnectionSettings();
|
||||
await RunDeviceCommand(deviceService.PulseResetAsync(), "已按老代码逻辑发送复位指令 M90");
|
||||
BeginResetButtonFeedback();
|
||||
|
||||
try
|
||||
{
|
||||
await deviceService.PulseResetAsync();
|
||||
CurrentStatus = "已按老代码逻辑发送复位指令 M90";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "设备指令失败:已按老代码逻辑发送复位指令 M90");
|
||||
CurrentStatus = $"设备指令失败:{ex.Message}";
|
||||
ClearResetButtonFeedback();
|
||||
UpdateResetButtonText(deviceService.CurrentSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -293,7 +313,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ExportReport()
|
||||
private async Task ExportReport()
|
||||
{
|
||||
var points = currentRun.Count > 0 ? currentRun.ToList() : lastCompletedRun;
|
||||
if (points.Count == 0)
|
||||
@@ -305,6 +325,15 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
|
||||
try
|
||||
{
|
||||
CurrentStatus = "请选择 Excel 报告保存目录";
|
||||
var exportDirectory = await SelectExportDirectoryAsync();
|
||||
if (exportDirectory is null)
|
||||
{
|
||||
Log.Information("用户取消 Excel 导出目录选择:TestNumber={TestNumber}, PointCount={PointCount}", TestNumber, points.Count);
|
||||
CurrentStatus = "已取消 Excel 导出";
|
||||
return;
|
||||
}
|
||||
|
||||
var report = new SlipReportExport(
|
||||
TestNumber,
|
||||
OperatorName,
|
||||
@@ -322,7 +351,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
Samples.ToList(),
|
||||
points);
|
||||
|
||||
var path = excelExportService.Export(report);
|
||||
var path = excelExportService.Export(report, exportDirectory);
|
||||
UploadProgress = 100;
|
||||
CurrentStatus = $"Excel 已导出:{path}";
|
||||
}
|
||||
@@ -333,6 +362,79 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string?> SelectExportDirectoryAsync()
|
||||
{
|
||||
var storageProvider = GetStorageProvider();
|
||||
if (storageProvider is null || !storageProvider.CanPickFolder)
|
||||
{
|
||||
var defaultDirectory = SlipExcelExportService.GetDefaultExportDirectory();
|
||||
Log.Warning("当前平台无法打开 Excel 导出目录选择器,将使用默认目录:Path={Path}", defaultDirectory);
|
||||
return defaultDirectory;
|
||||
}
|
||||
|
||||
var folders = await storageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
||||
{
|
||||
Title = "选择报告保存目录",
|
||||
AllowMultiple = false,
|
||||
SuggestedStartLocation = await TryGetSuggestedExportFolderAsync(storageProvider)
|
||||
});
|
||||
|
||||
var selectedFolder = folders.FirstOrDefault();
|
||||
if (selectedFolder is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var localPath = selectedFolder.TryGetLocalPath();
|
||||
if (string.IsNullOrWhiteSpace(localPath) && selectedFolder.Path.IsFile)
|
||||
{
|
||||
localPath = selectedFolder.Path.LocalPath;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(localPath))
|
||||
{
|
||||
throw new InvalidOperationException("选择的目录不是本地文件夹,无法保存 Excel");
|
||||
}
|
||||
|
||||
return localPath;
|
||||
}
|
||||
|
||||
private static async Task<IStorageFolder?> TryGetSuggestedExportFolderAsync(IStorageProvider storageProvider)
|
||||
{
|
||||
try
|
||||
{
|
||||
var defaultDirectory = SlipExcelExportService.GetDefaultExportDirectory();
|
||||
if (Directory.Exists(defaultDirectory))
|
||||
{
|
||||
return await storageProvider.TryGetFolderFromPathAsync(defaultDirectory);
|
||||
}
|
||||
|
||||
var parentDirectory = Path.GetDirectoryName(defaultDirectory);
|
||||
if (!string.IsNullOrWhiteSpace(parentDirectory) && Directory.Exists(parentDirectory))
|
||||
{
|
||||
return await storageProvider.TryGetFolderFromPathAsync(parentDirectory);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning(ex, "准备 Excel 默认导出目录失败,将不设置目录选择初始位置");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IStorageProvider? GetStorageProvider()
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop
|
||||
&& desktop.MainWindow is not null)
|
||||
{
|
||||
return desktop.MainWindow.StorageProvider;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private Task LiftMotion() => RunDeviceCommand(deviceService.ApplyOldLiftAsync(), "提升按老代码逻辑执行:M4=0, M5=1");
|
||||
|
||||
@@ -478,7 +580,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
{
|
||||
DeviceStatus = device.IsTestRunning ? "联机 / 测试中" : device.IsResetting ? "联机 / 复位中" : "联机 / 待机";
|
||||
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
||||
ResetButtonText = device.IsResetting ? "复位中" : "复位";
|
||||
UpdateResetButtonText(device);
|
||||
VerticalPressure = device.VerticalLoadN.ToString("F1", CultureInfo.InvariantCulture);
|
||||
HorizontalForce = device.HorizontalFrictionN.ToString("F1", CultureInfo.InvariantCulture);
|
||||
FrictionCoefficient = device.FrictionCoefficient.ToString("F3", CultureInfo.InvariantCulture);
|
||||
@@ -489,7 +591,7 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
{
|
||||
DeviceStatus = IsAdcCalibrationError(device.LastError) ? "数据无效" : "离线";
|
||||
TestButtonText = device.IsTestRunning ? "停止" : "测试";
|
||||
ResetButtonText = device.IsResetting ? "复位中" : "复位";
|
||||
UpdateResetButtonText(device);
|
||||
VerticalPressure = "--";
|
||||
HorizontalForce = "--";
|
||||
FrictionCoefficient = "--";
|
||||
@@ -530,6 +632,37 @@ namespace Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.ViewModel
|
||||
wasRunning = isRecording;
|
||||
}
|
||||
|
||||
private void BeginResetButtonFeedback()
|
||||
{
|
||||
isResetButtonPending = true;
|
||||
hasObservedResetDeviceBusy = false;
|
||||
resetButtonPendingStartedAt = DateTime.UtcNow;
|
||||
ResetButtonText = "复位中";
|
||||
}
|
||||
|
||||
private void ClearResetButtonFeedback()
|
||||
{
|
||||
isResetButtonPending = false;
|
||||
hasObservedResetDeviceBusy = false;
|
||||
resetButtonPendingStartedAt = DateTime.MinValue;
|
||||
}
|
||||
|
||||
private void UpdateResetButtonText(SlipDeviceSnapshot device)
|
||||
{
|
||||
if (device.IsResetting)
|
||||
{
|
||||
hasObservedResetDeviceBusy = true;
|
||||
}
|
||||
else if (!device.IsConnected
|
||||
|| hasObservedResetDeviceBusy
|
||||
|| (isResetButtonPending && DateTime.UtcNow - resetButtonPendingStartedAt >= ResetButtonPendingTimeout))
|
||||
{
|
||||
ClearResetButtonFeedback();
|
||||
}
|
||||
|
||||
ResetButtonText = device.IsResetting || isResetButtonPending ? "复位中" : "复位";
|
||||
}
|
||||
|
||||
private void BeginRun()
|
||||
{
|
||||
currentRun.Clear();
|
||||
|
||||
@@ -7,16 +7,14 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
d:DesignWidth="1440"
|
||||
d:DesignHeight="900"
|
||||
d:DesignWidth="1024"
|
||||
d:DesignHeight="768"
|
||||
x:Class="Footwear_Test_methodsfor_wholeshoe_Slipresistanceperformance.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
WindowState="Maximized"
|
||||
Width="1280"
|
||||
Height="800"
|
||||
MinWidth="1024"
|
||||
MinHeight="720"
|
||||
Width="1024"
|
||||
Height="768"
|
||||
Title="鞋类 整鞋试验方法 防滑性能测定-GB/T 3903.6-2024"
|
||||
Background="#F4F7FB"
|
||||
BackgroundAnimationEnabled="False"
|
||||
@@ -195,11 +193,11 @@
|
||||
</suki:SukiWindow.Styles>
|
||||
|
||||
<Grid RowDefinitions="*"
|
||||
Width="1264"
|
||||
HorizontalAlignment="Left"
|
||||
MaxWidth="1264"
|
||||
HorizontalAlignment="Stretch"
|
||||
Margin="8"
|
||||
ClipToBounds="True">
|
||||
<Grid Grid.Row="0" RowDefinitions="Auto,Auto,*" RowSpacing="8">
|
||||
<Grid Grid.Row="0" RowDefinitions="Auto,*" RowSpacing="8">
|
||||
<Border Classes="panel">
|
||||
<Grid ColumnDefinitions="1.2*,1.15*" ColumnSpacing="14">
|
||||
<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,*,Auto,*" RowSpacing="6" ColumnSpacing="8">
|
||||
@@ -249,57 +247,7 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="1"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource LineBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8"
|
||||
Padding="8,4"
|
||||
ClipToBounds="True">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Disabled"
|
||||
ClipToBounds="True">
|
||||
<StackPanel Orientation="Horizontal" Spacing="16">
|
||||
<TextBlock Text="标准数据" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="设备状态" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding DeviceStatus}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="正压力(N)" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding VerticalPressure}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="摩擦力(N)" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding HorizontalForce}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="位移(mm)" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Distance}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="实时系数" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding FrictionCoefficient}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="静摩擦系数" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding StaticCoefficient}" FontWeight="SemiBold" Foreground="#5B21B6" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="动摩擦系数" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding DynamicCoefficient}" FontWeight="SemiBold" Foreground="#047857" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="5">
|
||||
<TextBlock Text="结果" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding ResultSummary}" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding StandardReference}" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding CurrentStatus}" Foreground="{StaticResource TextSecondaryBrush}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
|
||||
<DockPanel Grid.Row="2" LastChildFill="True" MinHeight="0" ClipToBounds="True">
|
||||
<DockPanel Grid.Row="1" LastChildFill="True" MinHeight="0" ClipToBounds="True">
|
||||
<Border DockPanel.Dock="Left" Width="280" Classes="panel" Margin="0,0,10,0">
|
||||
<Grid RowDefinitions="Auto,Auto,*" RowSpacing="8">
|
||||
<TextBlock Text="实时数据"
|
||||
|
||||
Reference in New Issue
Block a user