refactor(project): 迁移项目命名并移除WPF客户端

This commit is contained in:
Scottxjw
2026-08-13 13:29:56 +08:00
parent 5189c665c3
commit 645f46abcd
70 changed files with 84 additions and 1913 deletions

View File

@@ -3,11 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCTDataMiner.Core", "src\WCTDataMiner.Core\WCTDataMiner.Core.csproj", "{A1B2C3D4-1234-5678-9ABC-DEF012345601}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gpulse.WCT.DataAnalyzer.Core", "src\Gpulse.WCT.DataAnalyzer.Core\Gpulse.WCT.DataAnalyzer.Core.csproj", "{A1B2C3D4-1234-5678-9ABC-DEF012345601}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCTDataMiner.Wpf", "src\WCTDataMiner.Wpf\WCTDataMiner.Wpf.csproj", "{A1B2C3D4-1234-5678-9ABC-DEF012345602}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WCTDataMiner", "src\WCTDataMiner\WCTDataMiner.csproj", "{A1B2C3D4-1234-5678-9ABC-DEF012345603}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gpulse.WCT.DataAnalyzer", "src\Gpulse.WCT.DataAnalyzer\Gpulse.WCT.DataAnalyzer.csproj", "{A1B2C3D4-1234-5678-9ABC-DEF012345603}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -19,10 +17,6 @@ Global
{A1B2C3D4-1234-5678-9ABC-DEF012345601}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345601}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345601}.Release|Any CPU.Build.0 = Release|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345602}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345602}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345602}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345602}.Release|Any CPU.Build.0 = Release|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345603}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345603}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-1234-5678-9ABC-DEF012345603}.Release|Any CPU.ActiveCfg = Release|Any CPU

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Configuration;
namespace Gpulse.WCT.DataAnalyzer.Core.Configuration;
/// <summary>
/// 应用配置

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class PlossRecordConfiguration : IEntityTypeConfiguration<PlossRecord>
{

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class QfodRecordConfiguration : IEntityTypeConfiguration<QfodRecord>
{

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class RxTypeConfiguration : IEntityTypeConfiguration<RxType>
{

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class TestScenarioConfiguration : IEntityTypeConfiguration<TestScenario>
{

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class TxHardwareConfiguration : IEntityTypeConfiguration<TxHardware>
{

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class TxPanelConfiguration : IEntityTypeConfiguration<TxPanel>
{

View File

@@ -1,8 +1,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data.Configurations;
namespace Gpulse.WCT.DataAnalyzer.Core.Data.Configurations;
public class TxSoftwareConfiguration : IEntityTypeConfiguration<TxSoftware>
{

View File

@@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
namespace WCTDataMiner.Core.Data;
namespace Gpulse.WCT.DataAnalyzer.Core.Data;
/// <summary>
/// 数据库上下文工厂实现 - 支持SQLite和PostgreSQL切换

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Data;
namespace Gpulse.WCT.DataAnalyzer.Core.Data;
/// <summary>
/// 数据库上下文工厂接口

View File

@@ -1,6 +1,6 @@
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data;
namespace Gpulse.WCT.DataAnalyzer.Core.Data;
/// <summary>
/// 初始数据种子

View File

@@ -1,7 +1,7 @@
using Microsoft.EntityFrameworkCore;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Data;
namespace Gpulse.WCT.DataAnalyzer.Core.Data;
/// <summary>
/// WCT数据采集数据库上下文

View File

@@ -1,10 +1,10 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Parsers;
using WCTDataMiner.Core.Services;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Parsers;
using Gpulse.WCT.DataAnalyzer.Core.Services;
namespace WCTDataMiner.Core.Extensions;
namespace Gpulse.WCT.DataAnalyzer.Core.Extensions;
/// <summary>
/// 服务注册扩展

View File

@@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Gpulse.WCT.DataAnalyzer.Core</AssemblyName>
<RootNamespace>Gpulse.WCT.DataAnalyzer.Core</RootNamespace>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// Ploss记录实体 - 存储Ploss FOD格式解析数据

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// Qfod记录实体 - 存储Qfod格式解析数据

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// RX类型维度表

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// 测试场景实体 - 通过外键关联各维度表

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// TX硬件版本维度表

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// TX面板类型维度表

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Models;
namespace Gpulse.WCT.DataAnalyzer.Core.Models;
/// <summary>
/// TX软件版本维度表

View File

@@ -1,6 +1,6 @@
using System.Globalization;
namespace WCTDataMiner.Core.Parsers;
namespace Gpulse.WCT.DataAnalyzer.Core.Parsers;
/// <summary>
/// 文件名解析器 - 从文件名提取场景信息

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Parsers;
namespace Gpulse.WCT.DataAnalyzer.Core.Parsers;
/// <summary>
/// 通用解析器接口

View File

@@ -1,7 +1,7 @@
using System.Text.RegularExpressions;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Parsers;
namespace Gpulse.WCT.DataAnalyzer.Core.Parsers;
/// <summary>
/// Ploss FOD日志解析器 - 支持两行格式

View File

@@ -1,7 +1,7 @@
using System.Text.RegularExpressions;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Parsers;
namespace Gpulse.WCT.DataAnalyzer.Core.Parsers;
/// <summary>
/// Qfod日志解析器

View File

@@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
namespace WCTDataMiner.Core.Security;
namespace Gpulse.WCT.DataAnalyzer.Core.Security;
/// <summary>
/// 路径验证结果

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Security;
namespace Gpulse.WCT.DataAnalyzer.Core.Security;
/// <summary>
/// 安全相关常量定义

View File

@@ -1,9 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 数据清理服务 - 支持软删除

View File

@@ -1,9 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 维度表管理服务 - 负责维度表的CRUD操作并发安全版本

View File

@@ -3,11 +3,11 @@ using System.Text;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Models;
using WCTDataMiner.Core.Security;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Security;
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 数据导出服务 - 支持安全的路径验证和 RFC 4180 标准 CSV 转义

View File

@@ -1,11 +1,11 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Models;
using WCTDataMiner.Core.Parsers;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Parsers;
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 解析流程协调服务 - 协调文件名解析、行解析、批量写入

View File

@@ -1,10 +1,10 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Models;
using WCTDataMiner.Core.Parsers;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Parsers;
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 篮选条件常量

View File

@@ -1,9 +1,9 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Models;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Models;
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 统计查询服务 - 使用数据库端聚合优化内存效率

View File

@@ -1,4 +1,4 @@
namespace WCTDataMiner.Core.Services;
namespace Gpulse.WCT.DataAnalyzer.Core.Services;
/// <summary>
/// 阈值计算器 - 根据接收功率查表获取动态阈值

View File

@@ -1,7 +1,7 @@
using System.CommandLine;
using WCTDataMiner.Core.Services;
using Gpulse.WCT.DataAnalyzer.Core.Services;
namespace WCTDataMiner.Commands;
namespace Gpulse.WCT.DataAnalyzer.Commands;
/// <summary>
/// 数据清理命令

View File

@@ -1,7 +1,7 @@
using System.CommandLine;
using WCTDataMiner.Core.Services;
using Gpulse.WCT.DataAnalyzer.Core.Services;
namespace WCTDataMiner.Commands;
namespace Gpulse.WCT.DataAnalyzer.Commands;
/// <summary>
/// 数据导出命令

View File

@@ -1,7 +1,7 @@
using System.CommandLine;
using WCTDataMiner.Core.Services;
using Gpulse.WCT.DataAnalyzer.Core.Services;
namespace WCTDataMiner.Commands;
namespace Gpulse.WCT.DataAnalyzer.Commands;
/// <summary>
/// 解析日志文件命令

View File

@@ -1,7 +1,7 @@
using System.CommandLine;
using WCTDataMiner.Core.Services;
using Gpulse.WCT.DataAnalyzer.Core.Services;
namespace WCTDataMiner.Commands;
namespace Gpulse.WCT.DataAnalyzer.Commands;
/// <summary>
/// 统计查询命令

View File

@@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyName>Gpulse.WCT.DataAnalyzer</AssemblyName>
<RootNamespace>Gpulse.WCT.DataAnalyzer</RootNamespace>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
@@ -30,7 +32,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WCTDataMiner.Core\WCTDataMiner.Core.csproj" />
<ProjectReference Include="..\Gpulse.WCT.DataAnalyzer.Core\Gpulse.WCT.DataAnalyzer.Core.csproj" />
</ItemGroup>
<ItemGroup>

View File

@@ -3,15 +3,15 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using WCTDataMiner.Commands;
using WCTDataMiner.Core.Data;
using WCTDataMiner.Core.Extensions;
using WCTDataMiner.Core.Services;
using Gpulse.WCT.DataAnalyzer.Commands;
using Gpulse.WCT.DataAnalyzer.Core.Data;
using Gpulse.WCT.DataAnalyzer.Core.Extensions;
using Gpulse.WCT.DataAnalyzer.Core.Services;
namespace WCTDataMiner;
namespace Gpulse.WCT.DataAnalyzer;
/// <summary>
/// WCTDataMiner - 无线充电FOD日志数据采集系统
/// Gpulse.WCT.DataAnalyzer - 无线充电FOD日志数据采集系统
/// </summary>
public class Program
{
@@ -67,7 +67,7 @@ public class Program
using var commandScope = app.Services.CreateScope();
var provider = commandScope.ServiceProvider;
var rootCommand = new RootCommand("WCTDataMiner - FOD Log Data Parser");
var rootCommand = new RootCommand("Gpulse.WCT.DataAnalyzer - FOD Log Data Parser");
rootCommand.AddCommand(new ParseCommand(provider.GetRequiredService<ParseService>()));
rootCommand.AddCommand(new StatsCommand(provider.GetRequiredService<StatsService>()));

View File

@@ -1,10 +0,0 @@
<Application x:Class="WCTDataMiner.Wpf.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WCTDataMiner.Wpf"
xmlns:converters="clr-namespace:WCTDataMiner.Wpf.Converters">
<Application.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</Application.Resources>
</Application>

View File

@@ -1,123 +0,0 @@
using System.IO;
using System.Windows;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using WCTDataMiner.Core.Extensions;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Configuration;
using WCTDataMiner.Wpf.ViewModels;
using WCTDataMiner.Wpf.Views;
namespace WCTDataMiner.Wpf;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private IHost? _host;
private ILogger? _logger;
protected override void OnStartup(StartupEventArgs e)
{
// 使用 ConfigurationLoader 加载配置,支持文件缺失
var configuration = ConfigurationLoader.LoadConfiguration(
AppDomain.CurrentDomain.BaseDirectory,
out var configFileExists);
// 配置 Serilog - 从配置文件读取
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.Enrich.FromLogContext()
.CreateLogger();
_logger = Log.ForContext<App>();
if (!configFileExists)
{
_logger.Information("配置文件 appsettings.json 不存在,使用默认配置启动");
}
_logger.Information("WCTDataMiner WPF 应用启动");
_host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((context, configBuilder) =>
{
configBuilder.Sources.Clear();
configBuilder.SetBasePath(AppDomain.CurrentDomain.BaseDirectory);
if (configFileExists)
{
configBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
}
else
{
configBuilder.AddInMemoryCollection(DefaultAppSettings.GetDefaultConfiguration());
}
configBuilder.AddEnvironmentVariables("WCTDM_");
})
.ConfigureServices((context, services) =>
{
// 添加 Serilog
services.AddSerilog();
// 注册 Core 层服务
services.AddDatabaseServices(context.Configuration);
services.AddApplicationServices();
// 注册 ViewModels使用 Singleton 确保消息订阅正确工作)
services.AddSingleton<MainViewModel>();
services.AddSingleton<ScenarioListViewModel>();
// Chart ViewModels 使用 Transient每次导航创建新实例
services.AddTransient<PlossChartViewModel>();
services.AddTransient<QfodChartViewModel>();
// Table ViewModels 使用 Transient每次导航创建新实例
services.AddTransient<PlossTableViewModel>();
services.AddTransient<QfodTableViewModel>();
services.AddSingleton<ImportViewModel>();
// 注册 Views
services.AddTransient<MainWindow>();
services.AddTransient<ScenarioListView>();
services.AddTransient<PlossChartView>();
services.AddTransient<QfodChartView>();
services.AddTransient<PlossTableView>();
services.AddTransient<QfodTableView>();
services.AddTransient<ImportView>();
})
.Build();
var mainWindow = _host.Services.GetRequiredService<MainWindow>();
// 初始化加载场景数据
var mainViewModel = _host.Services.GetRequiredService<MainViewModel>();
mainWindow.Loaded += async (s, e) =>
{
try
{
await mainViewModel.InitializeAsync();
}
catch (Exception ex)
{
_logger?.Error(ex, "主窗口初始化失败");
MessageBox.Show($"应用初始化失败: {ex.Message}", "错误",
MessageBoxButton.OK, MessageBoxImage.Error);
}
};
mainWindow.Show();
base.OnStartup(e);
}
protected override void OnExit(ExitEventArgs e)
{
_logger?.Information("WCTDataMiner WPF 应用退出");
Log.CloseAndFlush();
_host?.Dispose();
base.OnExit(e);
}
}

View File

@@ -1,10 +0,0 @@
using System.Windows;
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@@ -1,37 +0,0 @@
using System.IO;
using Microsoft.Extensions.Configuration;
namespace WCTDataMiner.Wpf.Configuration;
/// <summary>
/// 配置加载器 - 支持配置文件缺失时的默认配置
/// </summary>
public static class ConfigurationLoader
{
/// <summary>
/// 加载配置,如果文件不存在则使用默认配置
/// </summary>
/// <param name="basePath">配置文件基础路径</param>
/// <param name="configFileExists">输出参数:指示配置文件是否存在</param>
/// <returns>配置对象</returns>
public static IConfiguration LoadConfiguration(string basePath, out bool configFileExists)
{
var settingsPath = Path.Combine(basePath, "appsettings.json");
configFileExists = File.Exists(settingsPath);
var builder = new ConfigurationBuilder().SetBasePath(basePath);
if (configFileExists)
{
builder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
}
else
{
// 使用默认配置
builder.AddInMemoryCollection(DefaultAppSettings.GetDefaultConfiguration());
}
builder.AddEnvironmentVariables("WCTDM_");
return builder.Build();
}
}

View File

@@ -1,30 +0,0 @@
namespace WCTDataMiner.Wpf.Configuration;
/// <summary>
/// 默认应用配置 - 当配置文件缺失时使用
/// </summary>
public static class DefaultAppSettings
{
/// <summary>
/// 获取默认配置字典
/// </summary>
public static Dictionary<string, string?> GetDefaultConfiguration()
{
return new Dictionary<string, string?>
{
// Serilog 配置
["Serilog:MinimumLevel:Default"] = "Information",
["Serilog:MinimumLevel:Override:WCTDataMiner"] = "Debug",
["Serilog:MinimumLevel:Override:Microsoft"] = "Warning",
["Serilog:WriteTo:0:Name"] = "Console",
["Serilog:WriteTo:1:Name"] = "File",
["Serilog:WriteTo:1:Args:path"] = "./data/logs/wctdataminer-.log",
["Serilog:WriteTo:1:Args:rollingInterval"] = "Day",
["Serilog:WriteTo:1:Args:outputTemplate"] = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
// 数据库配置
["Database:Type"] = "sqlite",
["Database:Path"] = "./data/database/wctminer.db"
};
}
}

View File

@@ -1,53 +0,0 @@
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace WCTDataMiner.Wpf.Converters;
/// <summary>
/// Boolean to Visibility 转换器
/// </summary>
public class BooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Visibility visibility)
{
return visibility == Visibility.Visible;
}
return false;
}
}
/// <summary>
/// 反转 Boolean 转换器
/// </summary>
public class InverseBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return !boolValue;
}
return false;
}
}

View File

@@ -1,74 +0,0 @@
<Window x:Class="WCTDataMiner.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:WCTDataMiner.Wpf.Views"
xmlns:viewmodels="clr-namespace:WCTDataMiner.Wpf.ViewModels"
mc:Ignorable="d"
Title="WCTDataMiner - FOD 数据分析工具" Height="600" Width="900"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 左侧导航栏 -->
<Border Grid.Column="0" Background="#F5F5F5" BorderBrush="#DDD" BorderThickness="0,0,1,0">
<StackPanel Margin="10">
<TextBlock Text="导航" FontSize="16" FontWeight="Bold" Margin="0,0,0,15"/>
<Button Content="数据导入" Command="{Binding NavigateToImportCommand}"
Margin="0,5" Padding="10,5" HorizontalAlignment="Stretch"
Background="#4CAF50" Foreground="White" FontWeight="Bold"/>
<Button Content="场景列表" Command="{Binding NavigateToScenariosCommand}"
Margin="0,5" Padding="10,5" HorizontalAlignment="Stretch"/>
<TextBlock Text="筛选条件" FontSize="14" FontWeight="Bold" Margin="0,20,0,10"/>
<TextBlock Text="TX 面板:" Margin="0,5"/>
<ComboBox ItemsSource="{Binding TxPanels}" SelectedItem="{Binding SelectedTxPanel}"
Margin="0,0,0,10"/>
<TextBlock Text="TX 硬件:" Margin="0,5"/>
<ComboBox ItemsSource="{Binding TxHardwares}" SelectedItem="{Binding SelectedTxHardware}"
Margin="0,0,0,10"/>
<TextBlock Text="TX 软件:" Margin="0,5"/>
<ComboBox ItemsSource="{Binding TxSoftwares}" SelectedItem="{Binding SelectedTxSoftware}"
Margin="0,0,0,10"/>
<TextBlock Text="RX 类型:" Margin="0,5"/>
<ComboBox ItemsSource="{Binding RxTypes}" SelectedItem="{Binding SelectedRxType}"
Margin="0,0,0,10"/>
</StackPanel>
</Border>
<!-- 右侧内容区域 -->
<ContentControl Grid.Column="1" Content="{Binding CurrentView}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:ImportViewModel}">
<views:ImportView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:ScenarioListViewModel}">
<views:ScenarioListView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:PlossChartViewModel}">
<views:PlossChartView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:QfodChartViewModel}">
<views:QfodChartView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:PlossTableViewModel}">
<views:PlossTableView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:QfodTableViewModel}">
<views:QfodTableView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>

View File

@@ -1,16 +0,0 @@
using System.Windows;
using WCTDataMiner.Wpf.ViewModels;
namespace WCTDataMiner.Wpf;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow(MainViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}

View File

@@ -1,31 +0,0 @@
namespace WCTDataMiner.Wpf.Messages;
/// <summary>
/// 数据导入完成消息
/// </summary>
/// <param name="ImportedCount">导入的文件数量</param>
public record DataImportedMessage(int ImportedCount);
/// <summary>
/// 导航到 Ploss 图表消息
/// </summary>
/// <param name="ScenarioId">场景 ID</param>
public record NavigateToPlossChartMessage(Guid ScenarioId);
/// <summary>
/// 导航到 Qfod 图表消息
/// </summary>
/// <param name="ScenarioId">场景 ID</param>
public record NavigateToQfodChartMessage(Guid ScenarioId);
/// <summary>
/// 导航到 Ploss 表格消息
/// </summary>
/// <param name="ScenarioId">场景 ID</param>
public record NavigateToPlossTableMessage(Guid ScenarioId);
/// <summary>
/// 导航到 Qfod 表格消息
/// </summary>
/// <param name="ScenarioId">场景 ID</param>
public record NavigateToQfodTableMessage(Guid ScenarioId);

View File

@@ -1,170 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
using System.Collections.ObjectModel;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// 数据导入 ViewModel
/// </summary>
public partial class ImportViewModel : ObservableObject
{
private readonly ParseService _parseService;
private readonly ILogger<ImportViewModel> _logger;
[ObservableProperty]
private string _selectedPath = "";
[ObservableProperty]
private bool _isRecursive;
[ObservableProperty]
private bool _isImporting;
[ObservableProperty]
private int _progressValue;
[ObservableProperty]
private string _statusMessage = "请选择日志文件或目录";
[ObservableProperty]
private ObservableCollection<ImportResultItem> _importResults = new();
public ImportViewModel(ParseService parseService, ILogger<ImportViewModel> logger)
{
_parseService = parseService;
_logger = logger;
}
[RelayCommand]
private void SelectFile()
{
var dialog = new OpenFileDialog
{
Title = "选择日志文件",
Filter = "日志文件 (*.log)|*.log|所有文件 (*.*)|*.*",
Multiselect = false
};
if (dialog.ShowDialog() == true)
{
SelectedPath = dialog.FileName;
StatusMessage = $"已选择文件: {System.IO.Path.GetFileName(SelectedPath)}";
_logger.LogDebug("用户选择文件: {FilePath}", SelectedPath);
}
}
[RelayCommand]
private void SelectFolder()
{
var dialog = new OpenFolderDialog
{
Title = "选择日志目录",
Multiselect = false
};
if (dialog.ShowDialog() == true)
{
SelectedPath = dialog.FolderName;
StatusMessage = $"已选择目录: {System.IO.Path.GetFileName(SelectedPath)}";
_logger.LogDebug("用户选择目录: {FolderPath}, 递归={IsRecursive}", SelectedPath, IsRecursive);
}
}
[RelayCommand]
private async Task ImportAsync()
{
if (string.IsNullOrEmpty(SelectedPath))
{
StatusMessage = "请先选择文件或目录";
return;
}
IsImporting = true;
ImportResults.Clear();
StatusMessage = "正在导入...";
_logger.LogInformation("开始导入: {Path}, 递归={IsRecursive}", SelectedPath, IsRecursive);
try
{
if (System.IO.File.Exists(SelectedPath))
{
// 导入单个文件
var report = await _parseService.ParseFileAsync(SelectedPath);
ImportResults.Add(new ImportResultItem(report));
StatusMessage = $"导入完成: Qfod={report.QfodCount}, Ploss={report.PlossCount}, 错误={report.ErrorCount}";
_logger.LogInformation("单文件导入完成: {FileName}, Qfod={Qfod}, Ploss={Ploss}, 错误={Errors}",
report.FileName, report.QfodCount, report.PlossCount, report.ErrorCount);
// 发送导入完成消息,通知其他 ViewModel 刷新数据
WeakReferenceMessenger.Default.Send(new DataImportedMessage(1));
}
else if (System.IO.Directory.Exists(SelectedPath))
{
// 导入目录
var reports = await _parseService.ParseDirectoryAsync(SelectedPath, IsRecursive);
int totalQfod = 0, totalPloss = 0, totalErrors = 0;
foreach (var report in reports)
{
ImportResults.Add(new ImportResultItem(report));
totalQfod += report.QfodCount;
totalPloss += report.PlossCount;
totalErrors += report.ErrorCount;
ProgressValue = (int)((double)ImportResults.Count / reports.Count * 100);
}
StatusMessage = $"导入完成: {reports.Count} 个文件, Qfod={totalQfod}, Ploss={totalPloss}, 错误={totalErrors}";
_logger.LogInformation("目录导入完成: {FileCount} 个文件, Qfod={Qfod}, Ploss={Ploss}, 错误={Errors}",
reports.Count, totalQfod, totalPloss, totalErrors);
// 发送导入完成消息,通知其他 ViewModel 刷新数据
WeakReferenceMessenger.Default.Send(new DataImportedMessage(reports.Count));
}
else
{
StatusMessage = "路径不存在";
_logger.LogWarning("导入路径不存在: {Path}", SelectedPath);
}
}
catch (Exception ex)
{
StatusMessage = $"导入失败: {ex.Message}";
_logger.LogError(ex, "导入失败: {Path}", SelectedPath);
}
finally
{
IsImporting = false;
ProgressValue = 100;
}
}
}
/// <summary>
/// 导入结果项
/// </summary>
public class ImportResultItem
{
public string FileName { get; }
public int QfodCount { get; }
public int PlossCount { get; }
public int ErrorCount { get; }
public string Status { get; }
public bool IsSuccess { get; }
public ImportResultItem(ParseReport report)
{
FileName = report.FileName;
QfodCount = report.QfodCount;
PlossCount = report.PlossCount;
ErrorCount = report.ErrorCount;
IsSuccess = report.IsSuccess;
Status = report.IsSuccess ? "成功" : report.ErrorMessage ?? "失败";
}
}

View File

@@ -1,174 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// 主窗口 ViewModel - 导航控制
/// </summary>
public partial class MainViewModel : ObservableObject,
IRecipient<NavigateToPlossChartMessage>,
IRecipient<NavigateToQfodChartMessage>,
IRecipient<NavigateToPlossTableMessage>,
IRecipient<NavigateToQfodTableMessage>
{
private readonly ImportViewModel _importViewModel;
private readonly ScenarioListViewModel _scenarioListViewModel;
private readonly IServiceProvider _serviceProvider;
[ObservableProperty]
private ObservableObject _currentView = null!;
[ObservableProperty]
private string? _selectedTxPanel;
[ObservableProperty]
private string? _selectedTxHardware;
[ObservableProperty]
private string? _selectedTxSoftware;
[ObservableProperty]
private string? _selectedRxType;
// 筛选选项列表(动态加载)
[ObservableProperty]
private List<string> _txPanels = new() { FilterConstants.AllOption };
[ObservableProperty]
private List<string> _txHardwares = new() { FilterConstants.AllOption };
[ObservableProperty]
private List<string> _txSoftwares = new() { FilterConstants.AllOption };
[ObservableProperty]
private List<string> _rxTypes = new() { FilterConstants.AllOption };
public MainViewModel(
ImportViewModel importViewModel,
ScenarioListViewModel scenarioListViewModel,
IServiceProvider serviceProvider)
{
_importViewModel = importViewModel;
_scenarioListViewModel = scenarioListViewModel;
_serviceProvider = serviceProvider;
_currentView = _scenarioListViewModel;
// 注册导航消息订阅
WeakReferenceMessenger.Default.Register<NavigateToPlossChartMessage>(this);
WeakReferenceMessenger.Default.Register<NavigateToQfodChartMessage>(this);
WeakReferenceMessenger.Default.Register<NavigateToPlossTableMessage>(this);
WeakReferenceMessenger.Default.Register<NavigateToQfodTableMessage>(this);
}
/// <summary>
/// 初始化加载场景数据和筛选选项
/// </summary>
public async Task InitializeAsync()
{
// 加载筛选选项
var filterOptions = await _scenarioListViewModel.GetFilterOptionsAsync();
TxPanels = filterOptions.TxPanels;
TxHardwares = filterOptions.TxHardwares;
TxSoftwares = filterOptions.TxSoftwares;
RxTypes = filterOptions.RxTypes;
// 加载场景列表
await _scenarioListViewModel.LoadScenariosAsync();
}
[RelayCommand]
private void NavigateToImport()
{
CurrentView = _importViewModel;
}
[RelayCommand]
private async Task NavigateToScenarios()
{
CurrentView = _scenarioListViewModel;
await _scenarioListViewModel.LoadScenariosAsync();
}
[RelayCommand]
private void NavigateToPlossChart(Guid scenarioId)
{
var vm = _serviceProvider.GetRequiredService<PlossChartViewModel>();
vm.ScenarioId = scenarioId;
CurrentView = vm;
}
[RelayCommand]
private void NavigateToQfodChart(Guid scenarioId)
{
var vm = _serviceProvider.GetRequiredService<QfodChartViewModel>();
vm.ScenarioId = scenarioId;
CurrentView = vm;
}
/// <summary>
/// 应用筛选条件并刷新场景列表
/// </summary>
public async Task ApplyFilterAsync()
{
await _scenarioListViewModel.LoadScenariosByFilterAsync(
SelectedTxPanel, SelectedTxHardware, SelectedTxSoftware, SelectedRxType);
}
/// <summary>
/// 接收导航到 Ploss 图表消息
/// </summary>
public void Receive(NavigateToPlossChartMessage message)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
var vm = _serviceProvider.GetRequiredService<PlossChartViewModel>();
vm.ScenarioId = message.ScenarioId;
CurrentView = vm;
});
}
/// <summary>
/// 接收导航到 Qfod 图表消息
/// </summary>
public void Receive(NavigateToQfodChartMessage message)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
var vm = _serviceProvider.GetRequiredService<QfodChartViewModel>();
vm.ScenarioId = message.ScenarioId;
CurrentView = vm;
});
}
/// <summary>
/// 接收导航到 Ploss 表格消息
/// </summary>
public void Receive(NavigateToPlossTableMessage message)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
var vm = _serviceProvider.GetRequiredService<PlossTableViewModel>();
vm.ScenarioId = message.ScenarioId;
CurrentView = vm;
});
}
/// <summary>
/// 接收导航到 Qfod 表格消息
/// </summary>
public void Receive(NavigateToQfodTableMessage message)
{
Application.Current.Dispatcher.InvokeAsync(() =>
{
var vm = _serviceProvider.GetRequiredService<QfodTableViewModel>();
vm.ScenarioId = message.ScenarioId;
CurrentView = vm;
});
}
}

View File

@@ -1,140 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// Ploss 折线图 ViewModel
/// </summary>
public partial class PlossChartViewModel : ObservableObject
{
private readonly ScenarioService _scenarioService;
private readonly ILogger<PlossChartViewModel> _logger;
[ObservableProperty]
private Guid _scenarioId;
[ObservableProperty]
private ISeries[] _series;
[ObservableProperty]
private Axis[] _xAxes;
[ObservableProperty]
private Axis[] _yAxes;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string _loadingMessage = "";
public PlossChartViewModel(
ScenarioService scenarioService,
ILogger<PlossChartViewModel> logger)
{
_scenarioService = scenarioService;
_logger = logger;
// 初始化图表配置
Series = Array.Empty<ISeries>();
XAxes = new Axis[] { new Axis { Name = "Trigger Count" } };
YAxes = new Axis[] { new Axis { Name = "Power Loss (mW)" } };
}
/// <summary>
/// ScenarioId 属性变更时自动触发数据加载
/// </summary>
partial void OnScenarioIdChanged(Guid value)
{
if (value != Guid.Empty)
{
_ = LoadDataAsync().ContinueWith(t =>
{
if (t.Exception != null)
{
_logger.LogError(t.Exception, "LoadDataAsync 失败: ScenarioId={ScenarioId}", value);
}
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
[RelayCommand]
private void SwitchToTable()
{
WeakReferenceMessenger.Default.Send(new NavigateToPlossTableMessage(ScenarioId));
}
public async Task LoadDataAsync()
{
if (IsLoading) return; // 防并发
IsLoading = true;
LoadingMessage = "正在加载数据...";
try
{
var records = await _scenarioService.GetPlossRecordsByScenarioIdAsync(ScenarioId);
if (records.Count == 0)
{
Series = Array.Empty<ISeries>();
_logger.LogWarning("未找到 Ploss 数据: ScenarioId={ScenarioId}", ScenarioId);
LoadingMessage = "无数据";
return;
}
// 构建 Ploss 曲线 (Field9) 和 Threshold 阈值线 (Field10)
var plossValues = records.Select(r => (double)r.Field9).ToArray();
var thresholdValues = records.Select(r => (double)r.Field10).ToArray();
var triggerLabels = records.Select(r => r.Field11.ToString()).ToArray();
Series = new ISeries[]
{
new LineSeries<double>
{
Name = "Ploss",
Values = plossValues,
Fill = null,
GeometrySize = 5
},
new LineSeries<double>
{
Name = "Threshold",
Values = thresholdValues,
Fill = null,
GeometrySize = 0,
LineSmoothness = 0 // 直线
}
};
// 更新 X 轴标签
XAxes = new Axis[]
{
new Axis
{
Name = "Trigger Count",
Labels = triggerLabels
}
};
_logger.LogInformation("加载 Ploss 图表数据成功: ScenarioId={ScenarioId}, 记录数={Count}", ScenarioId, records.Count);
LoadingMessage = "";
}
catch (Exception ex)
{
_logger.LogError(ex, "加载 Ploss 图表数据失败: ScenarioId={ScenarioId}", ScenarioId);
LoadingMessage = $"加载失败: {ex.Message}";
}
finally
{
IsLoading = false;
}
}
}

View File

@@ -1,87 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using WCTDataMiner.Core.Models;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// Ploss 数据表格 ViewModel
/// </summary>
public partial class PlossTableViewModel : ObservableObject
{
private readonly ScenarioService _scenarioService;
private readonly ILogger<PlossTableViewModel> _logger;
[ObservableProperty]
private Guid _scenarioId;
[ObservableProperty]
private ObservableCollection<PlossRecord> _records = new();
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string _loadingMessage = "";
public PlossTableViewModel(
ScenarioService scenarioService,
ILogger<PlossTableViewModel> logger)
{
_scenarioService = scenarioService;
_logger = logger;
}
/// <summary>
/// ScenarioId 属性变更时自动触发数据加载
/// </summary>
partial void OnScenarioIdChanged(Guid value)
{
if (value != Guid.Empty)
{
_ = LoadDataAsync().ContinueWith(t =>
{
if (t.Exception != null)
{
_logger.LogError(t.Exception, "LoadDataAsync 失败: ScenarioId={ScenarioId}", value);
}
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
public async Task LoadDataAsync()
{
if (IsLoading) return; // 防并发
IsLoading = true;
LoadingMessage = "正在加载数据...";
try
{
var records = await _scenarioService.GetPlossRecordsByScenarioIdAsync(ScenarioId);
Records = new ObservableCollection<PlossRecord>(records);
_logger.LogInformation("加载 Ploss 表格数据成功: ScenarioId={ScenarioId}, 记录数={Count}", ScenarioId, records.Count);
LoadingMessage = "";
}
catch (Exception ex)
{
_logger.LogError(ex, "加载 Ploss 表格数据失败: ScenarioId={ScenarioId}", ScenarioId);
LoadingMessage = $"加载失败: {ex.Message}";
}
finally
{
IsLoading = false;
}
}
[RelayCommand]
private void SwitchToChart()
{
WeakReferenceMessenger.Default.Send(new NavigateToPlossChartMessage(ScenarioId));
}
}

View File

@@ -1,144 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using Microsoft.Extensions.Logging;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// Qfod 折线图 ViewModel
/// </summary>
public partial class QfodChartViewModel : ObservableObject
{
private readonly ScenarioService _scenarioService;
private readonly ILogger<QfodChartViewModel> _logger;
[ObservableProperty]
private Guid _scenarioId;
[ObservableProperty]
private ISeries[] _series;
[ObservableProperty]
private Axis[] _xAxes;
[ObservableProperty]
private Axis[] _yAxes;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string _loadingMessage = "";
public QfodChartViewModel(
ScenarioService scenarioService,
ILogger<QfodChartViewModel> logger)
{
_scenarioService = scenarioService;
_logger = logger;
// 初始化图表配置
Series = Array.Empty<ISeries>();
XAxes = new Axis[] { new Axis { Name = "Coil Index" } };
YAxes = new Axis[] { new Axis { Name = "Q Value" } };
}
/// <summary>
/// ScenarioId 属性变更时自动触发数据加载
/// </summary>
partial void OnScenarioIdChanged(Guid value)
{
if (value != Guid.Empty)
{
_ = LoadDataAsync().ContinueWith(t =>
{
if (t.Exception != null)
{
_logger.LogError(t.Exception, "LoadDataAsync 失败: ScenarioId={ScenarioId}", value);
}
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
[RelayCommand]
private void SwitchToTable()
{
WeakReferenceMessenger.Default.Send(new NavigateToQfodTableMessage(ScenarioId));
}
public async Task LoadDataAsync()
{
if (IsLoading) return; // 防并发
IsLoading = true;
LoadingMessage = "正在加载数据...";
try
{
var records = await _scenarioService.GetQfodRecordsByScenarioIdAsync(ScenarioId);
if (records.Count == 0)
{
Series = Array.Empty<ISeries>();
_logger.LogWarning("未找到 Qfod 数据: ScenarioId={ScenarioId}", ScenarioId);
LoadingMessage = "无数据";
return;
}
// 构建 DeltaQ、CurrentQ、RawQ 三条曲线
var deltaQValues = records.Select(r => (double)r.DeltaQ).ToArray();
var currentQValues = records.Select(r => (double)r.CurrentQ).ToArray();
var rawQValues = records.Select(r => (double)r.RawQ).ToArray();
var coilLabels = records.Select(r => $"{r.ChargerIndex}-{r.CoilIndex}").ToArray();
Series = new ISeries[]
{
new LineSeries<double>
{
Name = "DeltaQ",
Values = deltaQValues,
GeometrySize = 5
},
new LineSeries<double>
{
Name = "CurrentQ",
Values = currentQValues,
GeometrySize = 5
},
new LineSeries<double>
{
Name = "RawQ",
Values = rawQValues,
GeometrySize = 5
}
};
// 更新 X 轴标签
XAxes = new Axis[]
{
new Axis
{
Name = "Charger-Coil Index",
Labels = coilLabels
}
};
_logger.LogInformation("加载 Qfod 图表数据成功: ScenarioId={ScenarioId}, 记录数={Count}", ScenarioId, records.Count);
LoadingMessage = "";
}
catch (Exception ex)
{
_logger.LogError(ex, "加载 Qfod 图表数据失败: ScenarioId={ScenarioId}", ScenarioId);
LoadingMessage = $"加载失败: {ex.Message}";
}
finally
{
IsLoading = false;
}
}
}

View File

@@ -1,87 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using WCTDataMiner.Core.Models;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// Qfod 数据表格 ViewModel
/// </summary>
public partial class QfodTableViewModel : ObservableObject
{
private readonly ScenarioService _scenarioService;
private readonly ILogger<QfodTableViewModel> _logger;
[ObservableProperty]
private Guid _scenarioId;
[ObservableProperty]
private ObservableCollection<QfodRecord> _records = new();
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string _loadingMessage = "";
public QfodTableViewModel(
ScenarioService scenarioService,
ILogger<QfodTableViewModel> logger)
{
_scenarioService = scenarioService;
_logger = logger;
}
/// <summary>
/// ScenarioId 属性变更时自动触发数据加载
/// </summary>
partial void OnScenarioIdChanged(Guid value)
{
if (value != Guid.Empty)
{
_ = LoadDataAsync().ContinueWith(t =>
{
if (t.Exception != null)
{
_logger.LogError(t.Exception, "LoadDataAsync 失败: ScenarioId={ScenarioId}", value);
}
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
public async Task LoadDataAsync()
{
if (IsLoading) return; // 防并发
IsLoading = true;
LoadingMessage = "正在加载数据...";
try
{
var records = await _scenarioService.GetQfodRecordsByScenarioIdAsync(ScenarioId);
Records = new ObservableCollection<QfodRecord>(records);
_logger.LogInformation("加载 Qfod 表格数据成功: ScenarioId={ScenarioId}, 记录数={Count}", ScenarioId, records.Count);
LoadingMessage = "";
}
catch (Exception ex)
{
_logger.LogError(ex, "加载 Qfod 表格数据失败: ScenarioId={ScenarioId}", ScenarioId);
LoadingMessage = $"加载失败: {ex.Message}";
}
finally
{
IsLoading = false;
}
}
[RelayCommand]
private void SwitchToChart()
{
WeakReferenceMessenger.Default.Send(new NavigateToQfodChartMessage(ScenarioId));
}
}

View File

@@ -1,177 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using WCTDataMiner.Core.Services;
using WCTDataMiner.Wpf.Messages;
namespace WCTDataMiner.Wpf.ViewModels;
/// <summary>
/// 场景列表 ViewModel
/// </summary>
public partial class ScenarioListViewModel : ObservableObject, IRecipient<DataImportedMessage>
{
private readonly ScenarioService _scenarioService;
private readonly ILogger<ScenarioListViewModel> _logger;
[ObservableProperty]
private ObservableCollection<ScenarioItem> _scenarios = new();
[ObservableProperty]
private ScenarioItem? _selectedScenario;
[ObservableProperty]
private bool _isLoading;
[ObservableProperty]
private string _loadingMessage = "";
public ScenarioListViewModel(
ScenarioService scenarioService,
ILogger<ScenarioListViewModel> logger)
{
_scenarioService = scenarioService;
_logger = logger;
// 注册消息订阅,接收导入完成通知
WeakReferenceMessenger.Default.Register<DataImportedMessage>(this);
}
/// <summary>
/// 从数据库加载场景列表
/// </summary>
public async Task LoadScenariosAsync()
{
// 避免并发加载
if (IsLoading)
{
_logger.LogWarning("跳过重复加载请求:当前正在加载");
return;
}
IsLoading = true;
LoadingMessage = "正在加载场景列表...";
try
{
var scenarios = await _scenarioService.GetAllScenariosAsync();
// 批量更新,避免逐项 Add 导致频繁 UI 刷新
var items = scenarios.Select(s => new ScenarioItem
{
Id = s.Id,
TxPanel = s.TxPanel?.Name ?? "",
TxHardware = s.TxHardware?.Version ?? "",
TxSoftware = s.TxSoftware?.Version ?? "",
RxType = s.RxType?.Name ?? "",
TestDate = s.TestDate.ToString("yyyy-MM-dd"),
QfodCount = s.QfodCount,
PlossCount = s.PlossCount
}).ToList();
Scenarios = new ObservableCollection<ScenarioItem>(items);
}
catch (Exception ex)
{
_logger.LogError(ex, "加载场景列表失败");
LoadingMessage = $"加载失败: {ex.Message}";
return;
}
finally
{
IsLoading = false;
LoadingMessage = "";
}
}
/// <summary>
/// 根据筛选条件加载场景
/// </summary>
public async Task LoadScenariosByFilterAsync(
string? txPanel = null,
string? txHardware = null,
string? txSoftware = null,
string? rxType = null)
{
// 避免并发加载
if (IsLoading)
{
_logger.LogWarning("跳过重复筛选请求:当前正在加载");
return;
}
IsLoading = true;
LoadingMessage = "正在筛选场景...";
try
{
var scenarios = await _scenarioService.GetScenariosByFilterAsync(
txPanel, txHardware, txSoftware, rxType);
// 批量更新,避免逐项 Add 导致频繁 UI 刷新
var items = scenarios.Select(s => new ScenarioItem
{
Id = s.Id,
TxPanel = s.TxPanel?.Name ?? "",
TxHardware = s.TxHardware?.Version ?? "",
TxSoftware = s.TxSoftware?.Version ?? "",
RxType = s.RxType?.Name ?? "",
TestDate = s.TestDate.ToString("yyyy-MM-dd"),
QfodCount = s.QfodCount,
PlossCount = s.PlossCount
}).ToList();
Scenarios = new ObservableCollection<ScenarioItem>(items);
}
catch (Exception ex)
{
_logger.LogError(ex, "筛选场景失败");
LoadingMessage = $"筛选失败: {ex.Message}";
return;
}
finally
{
IsLoading = false;
LoadingMessage = "";
}
}
/// <summary>
/// 接收数据导入完成消息,自动刷新列表
/// </summary>
public async void Receive(DataImportedMessage message)
{
try
{
await LoadScenariosAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "接收数据导入消息后刷新场景列表失败");
}
}
/// <summary>
/// 获取可用的筛选选项
/// </summary>
public async Task<FilterOptions> GetFilterOptionsAsync()
{
return await _scenarioService.GetFilterOptionsAsync();
}
}
/// <summary>
/// 场景项数据模型
/// </summary>
public class ScenarioItem
{
public Guid Id { get; set; }
public string TxPanel { get; set; } = "";
public string TxHardware { get; set; } = "";
public string TxSoftware { get; set; } = "";
public string RxType { get; set; } = "";
public string TestDate { get; set; } = "";
public int QfodCount { get; set; }
public int PlossCount { get; set; }
}

View File

@@ -1,63 +0,0 @@
<UserControl x:Class="WCTDataMiner.Wpf.Views.ImportView"
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"
xmlns:local="clr-namespace:WCTDataMiner.Wpf.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="600">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 标题 -->
<TextBlock Grid.Row="0" Text="数据导入" FontSize="20" FontWeight="Bold" Margin="0,0,0,20"/>
<!-- 选择路径 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,15">
<Button Content="选择文件" Command="{Binding SelectFileCommand}"
Width="100" Margin="0,0,10,0" Padding="10,5"/>
<Button Content="选择目录" Command="{Binding SelectFolderCommand}"
Width="100" Margin="0,0,10,0" Padding="10,5"/>
<TextBox Text="{Binding SelectedPath, UpdateSourceTrigger=PropertyChanged}"
Width="300" IsReadOnly="True" VerticalContentAlignment="Center"/>
</StackPanel>
<!-- 选项和导入按钮 -->
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,15">
<CheckBox Content="递归搜索子目录" IsChecked="{Binding IsRecursive}"
Margin="0,0,20,0" VerticalContentAlignment="Center"/>
<Button Content="开始导入" Command="{Binding ImportCommand}"
Width="120" Padding="15,8" FontWeight="Bold"
IsEnabled="{Binding IsImporting, Converter={StaticResource InverseBooleanConverter}}"/>
</StackPanel>
<!-- 进度和状态 -->
<StackPanel Grid.Row="3" Margin="0,0,0,15">
<ProgressBar Height="20" Value="{Binding ProgressValue}" Maximum="100"
Visibility="{Binding IsImporting, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBlock Text="{Binding StatusMessage}" FontSize="14" Margin="0,5,0,0"/>
</StackPanel>
<!-- 导入结果列表 -->
<DataGrid Grid.Row="4" ItemsSource="{Binding ImportResults}"
AutoGenerateColumns="False"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
AlternatingRowBackground="#F9F9F9"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="文件名" Binding="{Binding FileName}" Width="*"/>
<DataGridTextColumn Header="Qfod" Binding="{Binding QfodCount}" Width="80"/>
<DataGridTextColumn Header="Ploss" Binding="{Binding PlossCount}" Width="80"/>
<DataGridTextColumn Header="错误" Binding="{Binding ErrorCount}" Width="80"/>
<DataGridTextColumn Header="状态" Binding="{Binding Status}" Width="100"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

View File

@@ -1,12 +0,0 @@
using System.Windows;
using System.Windows.Controls;
namespace WCTDataMiner.Wpf.Views;
public partial class ImportView : UserControl
{
public ImportView()
{
InitializeComponent();
}
}

View File

@@ -1,33 +0,0 @@
<UserControl x:Class="WCTDataMiner.Wpf.Views.PlossChartView"
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"
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
xmlns:local="clr-namespace:WCTDataMiner.Wpf.Views"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="Ploss 功率损耗趋势图" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding ScenarioId, StringFormat=' - 场景 {0}'}" FontSize="14" Foreground="Gray" Margin="10,5,0,0"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="切换到表格" Command="{Binding SwitchToTableCommand}" Padding="10,5"/>
</StackPanel>
<lvc:CartesianChart Grid.Row="2"
Series="{Binding Series}"
XAxes="{Binding XAxes}"
YAxes="{Binding YAxes}"
LegendPosition="Bottom"
Background="White"/>
</Grid>
</UserControl>

View File

@@ -1,18 +0,0 @@
using System.Windows.Controls;
namespace WCTDataMiner.Wpf.Views;
public partial class PlossChartView : UserControl
{
public PlossChartView()
{
InitializeComponent();
Loaded += async (s, e) =>
{
if (DataContext is ViewModels.PlossChartViewModel vm)
{
await vm.LoadDataAsync();
}
};
}
}

View File

@@ -1,79 +0,0 @@
<UserControl x:Class="WCTDataMiner.Wpf.Views.PlossTableView"
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"
d:DesignHeight="400" d:DesignWidth="900">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 标题区域 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="Ploss 数据表格" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding ScenarioId, StringFormat=' - 场景 {0}'}" FontSize="14" Foreground="Gray" Margin="10,5,0,0"/>
</StackPanel>
<!-- 操作按钮 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="切换到图表" Command="{Binding SwitchToChartCommand}" Padding="10,5" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Records.Count, StringFormat='共 {0} 条记录'}" VerticalAlignment="Center" Foreground="Gray"/>
<!-- 加载状态/错误消息显示 -->
<TextBlock Text="{Binding LoadingMessage}" VerticalAlignment="Center" Foreground="Orange" Margin="20,0,0,0"/>
</StackPanel>
<!-- 数据表格 -->
<DataGrid Grid.Row="2"
ItemsSource="{Binding Records}"
AutoGenerateColumns="False"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
AlternatingRowBackground="#F9F9F9"
CanUserAddRows="False"
HeadersVisibility="Column"
HorizontalScrollBarVisibility="Auto"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
EnableRowVirtualization="True"
EnableColumnVirtualization="True">
<DataGrid.Columns>
<DataGridTextColumn Header="PowLoss" Binding="{Binding PowLoss}" Width="80"/>
<DataGridTextColumn Header="DeltaP" Binding="{Binding DeltaP}" Width="80"/>
<DataGridTextColumn Header="RX_TYPE" Binding="{Binding Field1}" Width="80"/>
<DataGridTextColumn Header="Field2" Binding="{Binding Field2}" Width="70"/>
<DataGridTextColumn Header="Field3" Binding="{Binding Field3}" Width="70"/>
<DataGridTextColumn Header="RX power" Binding="{Binding Field4}" Width="80"/>
<DataGridTextColumn Header="TX power" Binding="{Binding Field5}" Width="80"/>
<DataGridTextColumn Header="Field6" Binding="{Binding Field6}" Width="70"/>
<DataGridTextColumn Header="Field7" Binding="{Binding Field7}" Width="80">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Field8" Binding="{Binding Field8}" Width="80">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="ploss" Binding="{Binding Field9}" Width="70"/>
<DataGridTextColumn Header="threshold" Binding="{Binding Field10}" Width="80"/>
<DataGridTextColumn Header="Margin" Binding="{Binding Margin}" Width="80"/>
<DataGridTextColumn Header="触发次数" Binding="{Binding Field11}" Width="80"/>
<DataGridTextColumn Header="PFOD result" Binding="{Binding Field12}" Width="90"/>
<DataGridTextColumn Header="Field13" Binding="{Binding Field13}" Width="70"/>
<DataGridTextColumn Header="Field14" Binding="{Binding Field14}" Width="70"/>
<DataGridTextColumn Header="Field15" Binding="{Binding Field15}" Width="70"/>
<DataGridTextColumn Header="Field16" Binding="{Binding Field16}" Width="70"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

View File

@@ -1,14 +0,0 @@
using System.Windows.Controls;
namespace WCTDataMiner.Wpf.Views;
/// <summary>
/// PlossTableView.xaml 的交互逻辑
/// </summary>
public partial class PlossTableView : UserControl
{
public PlossTableView()
{
InitializeComponent();
}
}

View File

@@ -1,33 +0,0 @@
<UserControl x:Class="WCTDataMiner.Wpf.Views.QfodChartView"
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"
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
xmlns:local="clr-namespace:WCTDataMiner.Wpf.Views"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="Qfod Q值变化图" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding ScenarioId, StringFormat=' - 场景 {0}'}" FontSize="14" Foreground="Gray" Margin="10,5,0,0"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="切换到表格" Command="{Binding SwitchToTableCommand}" Padding="10,5"/>
</StackPanel>
<lvc:CartesianChart Grid.Row="2"
Series="{Binding Series}"
XAxes="{Binding XAxes}"
YAxes="{Binding YAxes}"
LegendPosition="Bottom"
Background="White"/>
</Grid>
</UserControl>

View File

@@ -1,18 +0,0 @@
using System.Windows.Controls;
namespace WCTDataMiner.Wpf.Views;
public partial class QfodChartView : UserControl
{
public QfodChartView()
{
InitializeComponent();
Loaded += async (s, e) =>
{
if (DataContext is ViewModels.QfodChartViewModel vm)
{
await vm.LoadDataAsync();
}
};
}
}

View File

@@ -1,61 +0,0 @@
<UserControl x:Class="WCTDataMiner.Wpf.Views.QfodTableView"
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"
d:DesignHeight="400" d:DesignWidth="600">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 标题区域 -->
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10">
<TextBlock Text="Qfod 数据表格" FontSize="18" FontWeight="Bold"/>
<TextBlock Text="{Binding ScenarioId, StringFormat=' - 场景 {0}'}" FontSize="14" Foreground="Gray" Margin="10,5,0,0"/>
</StackPanel>
<!-- 操作按钮 -->
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10">
<Button Content="切换到图表" Command="{Binding SwitchToChartCommand}" Padding="10,5" Margin="0,0,10,0"/>
<TextBlock Text="{Binding Records.Count, StringFormat='共 {0} 条记录'}" VerticalAlignment="Center" Foreground="Gray"/>
<!-- 加载状态/错误消息显示 -->
<TextBlock Text="{Binding LoadingMessage}" VerticalAlignment="Center" Foreground="Orange" Margin="20,0,0,0"/>
</StackPanel>
<!-- 数据表格 -->
<DataGrid Grid.Row="2"
ItemsSource="{Binding Records}"
AutoGenerateColumns="False"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
AlternatingRowBackground="#F9F9F9"
CanUserAddRows="False"
HeadersVisibility="Column"
HorizontalScrollBarVisibility="Auto"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
EnableRowVirtualization="True"
EnableColumnVirtualization="True">
<DataGrid.Columns>
<DataGridTextColumn Header="ChargerIdx" Binding="{Binding ChargerIndex}" Width="100"/>
<DataGridTextColumn Header="CoilIdx" Binding="{Binding CoilIndex}" Width="80"/>
<DataGridTextColumn Header="DeltaQ" Binding="{Binding DeltaQ}" Width="80">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="CurrentQ" Binding="{Binding CurrentQ}" Width="80"/>
<DataGridTextColumn Header="RawQ" Binding="{Binding RawQ}" Width="80"/>
<DataGridTextColumn Header="FodType" Binding="{Binding FodType}" Width="80"/>
<DataGridTextColumn Header="FieldF" Binding="{Binding FieldF}" Width="80"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

View File

@@ -1,14 +0,0 @@
using System.Windows.Controls;
namespace WCTDataMiner.Wpf.Views;
/// <summary>
/// QfodTableView.xaml 的交互逻辑
/// </summary>
public partial class QfodTableView : UserControl
{
public QfodTableView()
{
InitializeComponent();
}
}

View File

@@ -1,46 +0,0 @@
<UserControl x:Class="WCTDataMiner.Wpf.Views.ScenarioListView"
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"
xmlns:local="clr-namespace:WCTDataMiner.Wpf.Views"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="600">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="测试场景列表" FontSize="18" FontWeight="Bold" Margin="0,0,0,10"/>
<DataGrid Grid.Row="1" ItemsSource="{Binding Scenarios}"
SelectedItem="{Binding SelectedScenario}"
AutoGenerateColumns="False"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
AlternatingRowBackground="#F9F9F9">
<DataGrid.Columns>
<DataGridTextColumn Header="TX 面板" Binding="{Binding TxPanel}" Width="*"/>
<DataGridTextColumn Header="TX 硬件" Binding="{Binding TxHardware}" Width="*"/>
<DataGridTextColumn Header="TX 软件" Binding="{Binding TxSoftware}" Width="*"/>
<DataGridTextColumn Header="RX 类型" Binding="{Binding RxType}" Width="*"/>
<DataGridTextColumn Header="测试日期" Binding="{Binding TestDate}" Width="*"/>
<DataGridTextColumn Header="Qfod 数量" Binding="{Binding QfodCount}" Width="80"/>
<DataGridTextColumn Header="Ploss 数量" Binding="{Binding PlossCount}" Width="80"/>
<DataGridTemplateColumn Header="操作" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button Content="Ploss图" Margin="2" Padding="5,2"
Click="OnViewPlossChart"/>
<Button Content="Qfod图" Margin="2" Padding="5,2"
Click="OnViewQfodChart"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

View File

@@ -1,31 +0,0 @@
using CommunityToolkit.Mvvm.Messaging;
using System.Windows;
using System.Windows.Controls;
using WCTDataMiner.Wpf.Messages;
using WCTDataMiner.Wpf.ViewModels;
namespace WCTDataMiner.Wpf.Views;
public partial class ScenarioListView : UserControl
{
public ScenarioListView()
{
InitializeComponent();
}
private void OnViewPlossChart(object sender, RoutedEventArgs e)
{
if (DataContext is ScenarioListViewModel vm && vm.SelectedScenario != null)
{
WeakReferenceMessenger.Default.Send(new NavigateToPlossChartMessage(vm.SelectedScenario.Id));
}
}
private void OnViewQfodChart(object sender, RoutedEventArgs e)
{
if (DataContext is ScenarioListViewModel vm && vm.SelectedScenario != null)
{
WeakReferenceMessenger.Default.Send(new NavigateToQfodChartMessage(vm.SelectedScenario.Id));
}
}
}

View File

@@ -1,42 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<LangVersion>12.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<!-- LiveCharts2 -->
<PackageReference Include="LiveChartsCore" Version="2.0.0-rc2" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc2" />
<!-- MVVM -->
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<!-- DI -->
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<!-- Logging -->
<PackageReference Include="Serilog" Version="4.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WCTDataMiner.Core\WCTDataMiner.Core.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>