测试报告导出

This commit is contained in:
hsc
2026-07-23 10:37:31 +08:00
parent 092087f670
commit c592bcd272
14 changed files with 297 additions and 4 deletions

View File

@@ -47,6 +47,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "加密狗", "加密狗\加
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CurveModule", "CurveModule\CurveModule.csproj", "{84006890-9288-44C4-9D17-FA88F3FDECFA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExportModule", "ExportModule\ExportModule.csproj", "{8923122F-F7D3-4AE7-9E86-9E78F1296A11}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -137,6 +139,10 @@ Global
{84006890-9288-44C4-9D17-FA88F3FDECFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{84006890-9288-44C4-9D17-FA88F3FDECFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{84006890-9288-44C4-9D17-FA88F3FDECFA}.Release|Any CPU.Build.0 = Release|Any CPU
{8923122F-F7D3-4AE7-9E86-9E78F1296A11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8923122F-F7D3-4AE7-9E86-9E78F1296A11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8923122F-F7D3-4AE7-9E86-9E78F1296A11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8923122F-F7D3-4AE7-9E86-9E78F1296A11}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -152,5 +158,6 @@ Global
{31F84D0E-3634-4C70-A830-21AF412E3BBF} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
{AF2533DD-599D-49D7-942B-B0C26534D15F} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
{84006890-9288-44C4-9D17-FA88F3FDECFA} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
{8923122F-F7D3-4AE7-9E86-9E78F1296A11} = {1E92D601-68FC-45F6-8251-BD0F39226E5B}
EndGlobalSection
EndGlobal

View File

@@ -22,8 +22,10 @@
<ProjectReference Include="..\BenchMovementModule\BenchMovementModule.csproj" />
<ProjectReference Include="..\CANModule\CANModule.csproj" />
<ProjectReference Include="..\Command\Command.csproj" />
<ProjectReference Include="..\CurveModule\CurveModule.csproj" />
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
<ProjectReference Include="..\DeviceEditModule\DeviceEditModule.csproj" />
<ProjectReference Include="..\ExportModule\ExportModule.csproj" />
<ProjectReference Include="..\LoginModule\LoginModule.csproj" />
<ProjectReference Include="..\MainModule\MainModule.csproj" />
<ProjectReference Include="..\MonitorModule\MonitorModule.csproj" />

View File

@@ -12,12 +12,12 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading; // 💡 新增:引入定时器命名空间
using System.Windows.Threading;
using UIShare;
using UIShare.GlobalVariable;
using UIShare.PubEvent;
using UIShare.UIViewModel;
using ZLGUSBCANFD;
using SqlSugar;
namespace ADP.ViewModels
{
@@ -845,6 +845,10 @@ namespace ADP.ViewModels
_regionManager.RequestNavigate("ShellViewManager", "BenchMovementView");
break;
case "曲线回读界面":
if (_globalInfo.CurrentScope != "default") return;
_regionManager.RequestNavigate("ShellViewManager", "CurveRecallView");
break;
case "测试报告导出界面":
if (_globalInfo.CurrentScope != "default") return;
_regionManager.RequestNavigate("ShellViewManager", "CurveRecallView");
break;

View File

@@ -71,6 +71,11 @@
CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"
Style="{StaticResource MaterialDesignFlatButton}"
Margin="8" />
<Button Content="测试报告导出界面"
Command="{Binding NavigateCommand}"
CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"
Style="{StaticResource MaterialDesignFlatButton}"
Margin="8" />
</StackPanel>
</materialDesign:DrawerHost.LeftDrawerContent>
<Grid>
@@ -189,6 +194,7 @@ Command="{Binding SelectCANMessageCommand}">
Foreground="Black" />
</MenuItem.Icon>
</MenuItem>
<!-- 获得文件路径字符串 -->
<MenuItem Header="获得文件路径字符串"
Foreground="Black"

View File

@@ -0,0 +1,20 @@
using ExportModule.Views;
using System.Reflection;
namespace ExportModule
{
public class ExportModule: IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
IRegionManager regionManager = containerProvider.Resolve<IRegionManager>();
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ExportView>("ExportView");
}
}
}

View File

@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ORM\ORM.csproj" />
<ProjectReference Include="..\Service\Service.csproj" />
<ProjectReference Include="..\UIShare\UIShare.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,68 @@
using Logger;
using MahApps.Metro.Controls;
using Model.Entity;
using Model.Models;
using NLog.Targets;
using OxyPlot;
using Service.Interface;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Threading;
using UIShare.GlobalVariable;
using UIShare.UIViewModel;
using UIShare.ViewModelBase;
namespace ExportModule.ViewModels
{
public class ExportViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable
{
#region
public bool KeepAlive => true;
#endregion
#region
#endregion
#region
#endregion
public ExportViewModel(IContainerProvider containerProvider) : base(containerProvider)
{
}
#region
#endregion
#region
#endregion
#region
public override void OnNavigatedTo(NavigationContext navigationContext)
{
}
public void Dispose()
{
}
#endregion
}
}

View File

@@ -0,0 +1,14 @@
<UserControl x:Class="ExportModule.Views.ExportView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="1080" d:DesignWidth="1920">
<Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ExportModule.Views
{
/// <summary>
/// ExportView.xaml 的交互逻辑
/// </summary>
public partial class ExportView : UserControl
{
public ExportView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,64 @@
using SqlSugar;
using System;
namespace Model.Entity
{
/// <summary>
/// 测试报告记录:每次 ExecuteSteps / ExecuteErrorSteps 中每个步骤的执行结果。
/// 同一次运行的所有步骤共享同一个 TestRoundId。
/// </summary>
public class TestReportEntity : BaseEntity
{
/// <summary>同一次运行的统一标识StepRunning.TestRoundID</summary>
[SugarColumn(ColumnName = "TestRoundId", ColumnDescription = "运行轮次标识")]
public Guid TestRoundId { get; set; }
/// <summary>作用域/台架名称</summary>
[SugarColumn(ColumnName = "Scope", ColumnDescription = "台架名称")]
public string Scope { get; set; } = string.Empty;
/// <summary>步骤序号</summary>
[SugarColumn(ColumnName = "StepIndex")]
public int StepIndex { get; set; }
/// <summary>步骤名称</summary>
[SugarColumn(ColumnName = "StepName", Length = 200)]
public string StepName { get; set; } = string.Empty;
/// <summary>步骤类型(普通步骤/循环开始/循环结束)</summary>
[SugarColumn(ColumnName = "StepType", Length = 50)]
public string StepType { get; set; } = string.Empty;
/// <summary>执行的方法名称</summary>
[SugarColumn(ColumnName = "MethodName", Length = 200, IsNullable = true)]
public string? MethodName { get; set; }
/// <summary>方法的完整类型名(实际方法所属类)</summary>
[SugarColumn(ColumnName = "MethodFullName", Length = 500, IsNullable = true)]
public string? MethodFullName { get; set; }
/// <summary>执行结果(成功/失败/未执行/跳过)</summary>
[SugarColumn(ColumnName = "Result", Length = 20)]
public string Result { get; set; } = string.Empty;
/// <summary>执行耗时(毫秒)</summary>
[SugarColumn(ColumnName = "RunTimeMs", IsNullable = true)]
public int? RunTimeMs { get; set; }
/// <summary>输出值(如果有)</summary>
[SugarColumn(ColumnName = "OutputValue", Length = 500, IsNullable = true)]
public string? OutputValue { get; set; }
/// <summary>子程序嵌套深度0=主程序)</summary>
[SugarColumn(ColumnName = "Depth")]
public int Depth { get; set; }
/// <summary>是否异常流程步骤</summary>
[SugarColumn(ColumnName = "IsErrorStep")]
public bool IsErrorStep { get; set; }
/// <summary>循环剩余次数(仅循环步骤有值)</summary>
[SugarColumn(ColumnName = "LoopRemaining", IsNullable = true)]
public int? LoopRemaining { get; set; }
}
}

View File

@@ -101,6 +101,7 @@ namespace TestingModule.ViewModels
_ScopedContext = scopedContext;
_systemConfig = systemConfig;
_globalInfo = globalInfo;
Title = _systemConfig.Title;
EditStepCommand = new DelegateCommand(EditStep);
CopyStepCommand = new DelegateCommand(CopyStep);
PasteStepCommand = new DelegateCommand(PasteStep);

View File

@@ -12,7 +12,7 @@
d:DesignWidth="800"
mc:Ignorable="d">
<Grid>
<GroupBox Header="程序编辑区域">
<GroupBox Header="{Binding Title}">
<TabControl SelectedIndex="{Binding SelectedTabIndex, Mode=TwoWay}">
<i:Interaction.Behaviors>
<behaviors:TabControlSelectionChangedBehavior Command="{Binding TabSelectionChangedCommand}"

View File

@@ -3,6 +3,8 @@ using UIShare.PubEvent;
using Common.Tools;
using Logger;
using MaterialDesignThemes.Wpf;
using Model.Entity;
using ORM;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -42,7 +44,7 @@ namespace UIShare
/// <summary>标记是否已被注销,执行方法据此安全退出</summary>
private volatile bool _disposed = false;
private Guid TestRoundID;
public Guid TestRoundID;
public StepRunning(ScopedContext ScopedContext, SystemConfig systemConfig,IEventAggregator eventAggregator, DeviceManager deviceManager)
{
_scopedContext = ScopedContext;
@@ -114,6 +116,7 @@ namespace UIShare
step.CurrentLoopCount = context.LoopCount;
LoggerHelper.InfoWithNotify(_systemConfig.Title, $"循环开始,共{context.LoopCount}次", depth);
index++;
SaveStepRecord(step, depth, true);
}
// 处理循环结束
@@ -124,6 +127,7 @@ namespace UIShare
LoggerHelper.ErrorWithNotify(_systemConfig.Title, "未匹配的循环结束指令", depth: depth);
step.Result = 2;
index++;
SaveStepRecord(step, depth, true);
continue;
}
@@ -138,6 +142,7 @@ namespace UIShare
// 继续循环:跳转到循环开始后的第一条指令
index = context.StartIndex + 1;
LoggerHelper.InfoWithNotify(_systemConfig.Title, $"循环第{context.CurrentLoop}次结束,跳回开始,剩余{context.LoopCount - context.CurrentLoop}次", depth);
SaveStepRecord(step, depth, true);
}
else
{
@@ -154,6 +159,7 @@ namespace UIShare
program.ErrorStepCollection.First(x => x.ID == step.LoopStartStepId).Result = 1;
loopStopwatchStack.Pop();
}
SaveStepRecord(step, depth, true);
}
}
@@ -212,6 +218,7 @@ namespace UIShare
stepStopwatch.Stop();
step.RunTime = (int)stepStopwatch.ElapsedMilliseconds;
}
SaveStepRecord(step, depth, true);
}
}
@@ -284,6 +291,7 @@ namespace UIShare
step.CurrentLoopCount = context.LoopCount;
LoggerHelper.InfoWithNotify(_systemConfig.Title, $"循环开始({step.Name}),共{context.LoopCount}次", depth);
index++;
SaveStepRecord(step, depth, false);
}
// 处理循环结束
@@ -294,6 +302,7 @@ namespace UIShare
LoggerHelper.ErrorWithNotify(_systemConfig.Title, "未匹配的循环结束指令", depth:depth);
step.Result = 2;
index++;
SaveStepRecord(step, depth, false);
continue;
}
@@ -308,6 +317,7 @@ namespace UIShare
// 继续循环:跳转到循环开始后的第一条指令
index = context.StartIndex + 1;
LoggerHelper.InfoWithNotify(_systemConfig.Title, $"循环第{context.CurrentLoop}次结束,跳回开始,剩余{context.LoopCount - context.CurrentLoop}次", depth);
SaveStepRecord(step, depth, false);
}
else
{
@@ -324,6 +334,7 @@ namespace UIShare
program.StepCollection.First(x => x.ID == step.LoopStartStepId).Result = 1;
loopStopwatchStack.Pop();
}
SaveStepRecord(step, depth, false);
}
}
@@ -389,6 +400,7 @@ namespace UIShare
_scopedContext.SingleStep = false;
_eventAggregator.GetEvent<RunSingalCompletedEvent>().Publish("Play");
}
SaveStepRecord(step, depth, false);
}
}
@@ -669,6 +681,56 @@ namespace UIShare
}
}
/// <summary>
/// 将单个步骤的执行结果保存到数据库(测试报告)。
/// 同一次运行的所有步骤共享 TestRoundID导出时按此 Guid 查询。
/// </summary>
private void SaveStepRecord(StepVM step, int depth, bool isErrorStep)
{
try
{
// 获取输出参数值
string? outputValue = null;
if (step.Method != null)
{
var outputParam = step.Method.Parameters.FirstOrDefault(p => p.Category == ParameterCategory.Output);
if (outputParam?.Value != null)
outputValue = outputParam.Value.ToString();
}
var entity = new TestReportEntity
{
TestRoundId = TestRoundID,
Scope = _systemConfig.Title,
StepIndex = step.Index,
StepName = step.Name ?? "",
StepType = step.StepType ?? "普通步骤",
MethodName = step.Method?.Name,
MethodFullName = step.Method?.FullName,
Result = step.Result switch
{
-1 => "未执行",
0 => "执行中",
1 => "成功",
2 => "失败",
_ => "未知"
},
RunTimeMs = step.RunTime,
OutputValue = outputValue,
Depth = depth,
IsErrorStep = isErrorStep,
LoopRemaining = step.CurrentLoopCount,
CreateTime = DateTime.Now
};
SqlSugarContext.DbContext.Insertable(entity).ExecuteCommand();
}
catch (Exception ex)
{
LoggerHelper.Error($"保存步骤记录失败 [{step.Index}]: {ex.Message}");
}
}
public void ResetAllStepStatus(ObservableCollection<StepVM> StepCollection)
{
foreach (var step in StepCollection)

View File

@@ -21,6 +21,7 @@
<ProjectReference Include="..\Common\Common.csproj" />
<ProjectReference Include="..\DeviceCommand\DeviceCommand.csproj" />
<ProjectReference Include="..\Logger\Logger.csproj" />
<ProjectReference Include="..\ORM\ORM.csproj" />
<ProjectReference Include="..\ZLGUSBCANFD\ZLGUSBCANFD.csproj" />
</ItemGroup>
</Project>