更新最新版20260604

This commit is contained in:
GukSang.Jin
2026-06-04 09:22:18 +08:00
parent fff560db5d
commit 38eb461c36
3 changed files with 151 additions and 68 deletions

View File

@@ -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();