#0005 增加日志
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
|||||||
|
.vs/ProjectEvaluation/xploreplane.metadata.v10.bin
|
||||||
|
.vs/ProjectEvaluation/xploreplane.projects.v10.bin
|
||||||
|
.vs/ProjectEvaluation/xploreplane.strings.v10.bin
|
||||||
|
.vs/XplorePlane/DesignTimeBuild/.dtbcache.v2
|
||||||
|
.vs/XplorePlane/FileContentIndex/241be4f9-f3c1-44c3-a625-51f3a7efa276.vsidx
|
||||||
|
.vs/XplorePlane/FileContentIndex/a28e3b89-b000-44c7-aab5-785c933af59b.vsidx
|
||||||
|
.vs/XplorePlane/FileContentIndex/a475b41e-8352-4745-8040-08886d83ddf4.vsidx
|
||||||
|
.vs/XplorePlane/FileContentIndex/bdb864e9-e54b-49df-bf87-9b121265e567.vsidx
|
||||||
|
.vs/XplorePlane/v18/.futdcache.v2
|
||||||
|
.vs/XplorePlane/v18/.suo
|
||||||
|
.vs/XplorePlane/v18/DocumentLayout.backup.json
|
||||||
|
.vs/XplorePlane/v18/DocumentLayout.json
|
||||||
|
XplorePlane/obj/project.assets.json
|
||||||
|
XplorePlane/obj/*
|
||||||
|
|
||||||
|
# 排除 Libs 目录中的 DLL 和 PDB 文件(但保留目录结构)
|
||||||
|
XplorePlane/Libs/Hardware/*.dll
|
||||||
|
XplorePlane/Libs/Hardware/*.pdb
|
||||||
|
XplorePlane/Libs/Native/*.dll
|
||||||
|
XplorePlane/Libs/Native/*.pdb
|
||||||
|
|
||||||
|
# 保留 .gitkeep 文件以维持目录结构
|
||||||
|
!XplorePlane/Libs/**/.gitkeep
|
||||||
|
|
||||||
|
# 排除构建输出
|
||||||
|
XplorePlane/bin/
|
||||||
|
XplorePlane/obj/
|
||||||
|
|
||||||
|
# 排除日志文件
|
||||||
|
logs/
|
||||||
|
*.log
|
||||||
@@ -1,20 +1,109 @@
|
|||||||
#简介
|
## XplorePlane 平面CT软件
|
||||||
TODO: 简要介绍你的项目。通过此节说明此项目的目标或动机。
|
|
||||||
|
|
||||||
#入门
|
### 系统目标
|
||||||
TODO: 指导用户在自己的系统上设置和运行代码。在本节中,可讨论:
|
|
||||||
1. 安装过程
|
|
||||||
2. 软件依赖项
|
|
||||||
3. 最新发布
|
|
||||||
4. API 参考
|
|
||||||
|
|
||||||
#生成与测试
|
XplorePlane 系统用于控制平面 CT 设备的各个子系统(射线源、探测器、运动控制)并完成采集图像的处理与分析,为研发与调试提供统一的软件平台。
|
||||||
TODO: 说明并展示如何生成代码和运行测试。
|
|
||||||
|
|
||||||
#参与
|
**总体架构风格**
|
||||||
TODO: 说明其他用户和开发人员可如何帮助改善代码。
|
|
||||||
|
|
||||||
如需深入了解如何创建优秀的自述文件,请参阅以下[指南](https://docs.microsoft.com/en-us/azure/devops/repos/git/create-a-readme?view=azure-devops)。还可从以下自述文件中寻求灵感:
|
- 客户端框架: WPF + Prism MVVM(项目 XplorePlane,目标框架 net8.0-windows)
|
||||||
- [ASP.NET Core](https://github.com/aspnet/Home)
|
- 图像处理内核: 独立类库 ImageProcessing.Core(算子基类与参数模型)和 ImageProcessing.Processors(具体算子实现),基于 EmguCV
|
||||||
- [Visual Studio Code](https://github.com/Microsoft/vscode)
|
- 基础设施: 日志使用 Serilog,序列化使用 Newtonsoft.Json,资源统一通过 WPF 资源系统管理
|
||||||
- [Chakra Core](https://github.com/Microsoft/ChakraCore)
|
|
||||||
|
**开发目标**
|
||||||
|
|
||||||
|
在现有图像处理与 UI 基础上,引入并集成:
|
||||||
|
|
||||||
|
- 射线源子系统(X-Ray Source)
|
||||||
|
- 探测器子系统(Detector)
|
||||||
|
- 运动控制子系统(Motion Control)
|
||||||
|
- 通过统一的 CT 扫描工作流,在 UI 中实现一键式扫描、实时状态监控与图像后处理
|
||||||
|
|
||||||
|
### 项目框架
|
||||||
|
|
||||||
|
```css
|
||||||
|
XplorePlane/
|
||||||
|
├── XplorePlane.csproj # .NET 8 WPF project file
|
||||||
|
│
|
||||||
|
├── App.xaml # Application + global ResourceDictionary
|
||||||
|
├── App.xaml.cs
|
||||||
|
│
|
||||||
|
├── Views/
|
||||||
|
│ └── MainWindow.xaml # Main window (Grid + StackPanel layout)
|
||||||
|
│ └── MainWindow.xaml.cs # Code-behind (minimal – only TreeView event)
|
||||||
|
│
|
||||||
|
├── ViewModels/
|
||||||
|
│ └── MainViewModel.cs # Root VM: navigation, callouts, props, commands
|
||||||
|
│ └── NavGroupNode.cs # Tree group node VM
|
||||||
|
│ └── NavLeafNode.cs # Tree leaf node VM
|
||||||
|
│ └── InspectionCalloutVM.cs # Overlay callout card VM
|
||||||
|
│ └── CalloutRowVM.cs # Single callout data row VM
|
||||||
|
│ └── RelayCommand.cs # ICommand implementation
|
||||||
|
│
|
||||||
|
├── Models/
|
||||||
|
│ └── FeatureProperties.cs # Bindable domain model for right panel
|
||||||
|
│
|
||||||
|
└── Assets/
|
||||||
|
└── Icons/ # 28×28 toolbar icon PNGs
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### XplorePlane.Hardware(硬件库)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### XplorePlane.ImageProcessing (图像库)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 日志系统
|
||||||
|
|
||||||
|
项目已集成 Serilog 日志框架,提供统一的日志服务:
|
||||||
|
|
||||||
|
- **日志框架**: Serilog 4.3.1
|
||||||
|
- **日志输出**: 控制台、文件(按天滚动)、调试输出
|
||||||
|
- **日志路径**: `logs/xploreplane-YYYYMMDD.log`
|
||||||
|
- **配置文件**: `App.config`
|
||||||
|
- **服务接口**: `ILoggerService`
|
||||||
|
|
||||||
|
**使用示例**:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class MyService
|
||||||
|
{
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
public MyService(ILoggerService logger)
|
||||||
|
{
|
||||||
|
// 使用泛型自动推断模块名
|
||||||
|
_logger = logger?.ForModule<MyService>() ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DoSomething()
|
||||||
|
{
|
||||||
|
_logger.Info("执行操作");
|
||||||
|
_logger.Debug("调试信息:参数={Value}", someValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
详细使用指南请参考:`Doc/Logging.README.md`
|
||||||
|
|
||||||
|
### TO-DO List
|
||||||
|
|
||||||
|
- [x] 软件基于 WPF + Prism 基础的框架
|
||||||
|
- [x] 日志库的引用
|
||||||
|
- [ ] 界面的布局
|
||||||
|
- [ ] 打通与硬件层的调用流程
|
||||||
|
- [ ] 打通与图像层的调用流程
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<appSettings>
|
||||||
|
<!-- Serilog 日志配置 -->
|
||||||
|
<add key="Serilog:LogPath" value="logs" />
|
||||||
|
<add key="Serilog:MinimumLevel" value="Information" />
|
||||||
|
<add key="Serilog:EnableConsole" value="true" />
|
||||||
|
<add key="Serilog:RollingInterval" value="Day" />
|
||||||
|
<add key="Serilog:FileSizeLimitMB" value="100" />
|
||||||
|
<add key="Serilog:RetainedFileCountLimit" value="30" />
|
||||||
|
|
||||||
|
<!-- 射线源配置 -->
|
||||||
|
<add key="RaySource.Type" value="Comet225"/>
|
||||||
|
<add key="RaySource.StationName" value="XRAY_STATION"/>
|
||||||
|
<add key="RaySource.ConnectionTimeout" value="5000"/>
|
||||||
|
<add key="RaySource.VoltageMin" value="20"/>
|
||||||
|
<add key="RaySource.VoltageMax" value="225"/>
|
||||||
|
<add key="RaySource.CurrentMin" value="10"/>
|
||||||
|
<add key="RaySource.CurrentMax" value="1000"/>
|
||||||
|
|
||||||
|
<!-- 探测器配置 -->
|
||||||
|
<add key="Detector.Type" value="Varex4343"/>
|
||||||
|
<add key="Detector.IPAddress" value="192.168.1.101"/>
|
||||||
|
<add key="Detector.Port" value="8080"/>
|
||||||
|
|
||||||
|
<!-- PLC 配置 -->
|
||||||
|
<add key="PLC.Type" value="B&R"/>
|
||||||
|
<add key="PLC.StationName" value="PLC_STATION"/>
|
||||||
|
<add key="PLC.ConnectionTimeout" value="5000"/>
|
||||||
|
</appSettings>
|
||||||
|
</configuration>
|
||||||
+56
-1
@@ -1,8 +1,11 @@
|
|||||||
using System.Windows;
|
using System;
|
||||||
|
using System.Windows;
|
||||||
using XplorePlane.Views;
|
using XplorePlane.Views;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
|
using XplorePlane.Services;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
using Prism.DryIoc;
|
using Prism.DryIoc;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace XplorePlane
|
namespace XplorePlane
|
||||||
{
|
{
|
||||||
@@ -13,12 +16,58 @@ namespace XplorePlane
|
|||||||
{
|
{
|
||||||
protected override void OnStartup(StartupEventArgs e)
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
{
|
{
|
||||||
|
// 配置 Serilog 日志系统
|
||||||
|
SerilogConfig.Configure();
|
||||||
|
|
||||||
|
// 捕获未处理的异常
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
|
||||||
|
DispatcherUnhandledException += OnDispatcherUnhandledException;
|
||||||
|
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
|
|
||||||
// Initialize Prism with DryIoc
|
// Initialize Prism with DryIoc
|
||||||
var bootstrapper = new AppBootstrapper();
|
var bootstrapper = new AppBootstrapper();
|
||||||
bootstrapper.Run();
|
bootstrapper.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnExit(ExitEventArgs e)
|
||||||
|
{
|
||||||
|
// 关闭并刷新日志
|
||||||
|
SerilogConfig.CloseAndFlush();
|
||||||
|
base.OnExit(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理未捕获的异常
|
||||||
|
/// </summary>
|
||||||
|
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
var exception = e.ExceptionObject as Exception;
|
||||||
|
Log.Fatal(exception, "应用程序发生未处理的异常");
|
||||||
|
|
||||||
|
MessageBox.Show(
|
||||||
|
$"应用程序发生严重错误:\n\n{exception?.Message}\n\n请查看日志文件获取详细信息。",
|
||||||
|
"严重错误",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理 UI 线程未捕获的异常
|
||||||
|
/// </summary>
|
||||||
|
private void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
|
||||||
|
{
|
||||||
|
Log.Error(e.Exception, "UI 线程发生未处理的异常");
|
||||||
|
|
||||||
|
MessageBox.Show(
|
||||||
|
$"应用程序发生错误:\n\n{e.Exception.Message}\n\n请查看日志文件获取详细信息。",
|
||||||
|
"错误",
|
||||||
|
MessageBoxButton.OK,
|
||||||
|
MessageBoxImage.Error);
|
||||||
|
|
||||||
|
// 标记为已处理,防止应用程序崩溃
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AppBootstrapper : PrismBootstrapper
|
public class AppBootstrapper : PrismBootstrapper
|
||||||
@@ -30,8 +79,14 @@ namespace XplorePlane
|
|||||||
|
|
||||||
protected override void RegisterTypes(IContainerRegistry containerRegistry)
|
protected override void RegisterTypes(IContainerRegistry containerRegistry)
|
||||||
{
|
{
|
||||||
|
// 注册日志服务
|
||||||
|
containerRegistry.RegisterSingleton<ILoggerService, LoggerService>();
|
||||||
|
|
||||||
|
// 注册视图和视图模型
|
||||||
containerRegistry.RegisterForNavigation<MainWindow>();
|
containerRegistry.RegisterForNavigation<MainWindow>();
|
||||||
containerRegistry.Register<MainViewModel>();
|
containerRegistry.Register<MainViewModel>();
|
||||||
|
|
||||||
|
Log.Information("依赖注入容器配置完成");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
<telerik:RadRibbonWindow x:Class="TelerikWpfApp2.MainWindow"
|
<telerik:RadRibbonWindow x:Class="XplorePlane.MainWindow"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
mc:Ignorable="d" d:DesignWidth="1280"
|
mc:Ignorable="d" d:DesignWidth="1280"
|
||||||
Icon="Images\TelerikExcel.ico"
|
|
||||||
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
|
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
|
||||||
xmlns:spreadsheetControls="clr-namespace:Telerik.Windows.Controls.Spreadsheet.Controls;assembly=Telerik.Windows.Controls.Spreadsheet"
|
xmlns:spreadsheetControls="clr-namespace:Telerik.Windows.Controls.Spreadsheet.Controls;assembly=Telerik.Windows.Controls.Spreadsheet"
|
||||||
xmlns:spreadsheet="clr-namespace:Telerik.Windows.Controls.Spreadsheet;assembly=Telerik.Windows.Controls.Spreadsheet"
|
xmlns:spreadsheet="clr-namespace:Telerik.Windows.Controls.Spreadsheet;assembly=Telerik.Windows.Controls.Spreadsheet"
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ using Telerik.Windows.Controls;
|
|||||||
using Telerik.Windows.Documents.Spreadsheet.FormatProviders;
|
using Telerik.Windows.Documents.Spreadsheet.FormatProviders;
|
||||||
using Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.Xlsx;
|
using Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.Xlsx;
|
||||||
using Telerik.Windows.Documents.Spreadsheet.FormatProviders.Pdf;
|
using Telerik.Windows.Documents.Spreadsheet.FormatProviders.Pdf;
|
||||||
using TelerikWpfApp2.ViewModel;
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
namespace TelerikWpfApp2
|
namespace XplorePlane
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for MainWindow.xaml
|
/// Interaction logic for MainWindow.xaml
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
## XplorePlane 平面CT软件
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### 系统目标
|
|
||||||
XplorePlane 系统用于控制平面 CT 设备的各个子系统(射线源、探测器、运动控制)并完成采集图像的处理与分析,为研发与调试提供统一的软件平台。
|
|
||||||
总体架构风格
|
|
||||||
- 客户端框架: WPF + Prism MVVM(项目 XplorePlane,目标框架 net8.0-windows)。
|
|
||||||
- 图像处理内核: 独立类库 ImageProcessing.Core(算子基类与参数模型)和 ImageProcessing.Processors(具体算子实现),基于 EmguCV。
|
|
||||||
- 基础设施: 日志使用 Serilog,序列化使用 Newtonsoft.Json,资源统一通过 WPF 资源系统管理。
|
|
||||||
|
|
||||||
开发目标
|
|
||||||
- 在现有图像处理与 UI 基础上,引入并集成:
|
|
||||||
- 射线源子系统(X-Ray Source)
|
|
||||||
- 探测器子系统(Detector)
|
|
||||||
- 运动控制子系统(Motion Control)
|
|
||||||
- 通过统一的 CT 扫描工作流,在 UI 中实现一键式扫描、实时状态监控与图像后处理。
|
|
||||||
|
|
||||||
### 项目框架
|
|
||||||
|
|
||||||
|
|
||||||
```css
|
|
||||||
XplorePlane/
|
|
||||||
├── XplorePlane.csproj # .NET 8 WPF project file
|
|
||||||
│
|
|
||||||
├── App.xaml # Application + global ResourceDictionary
|
|
||||||
├── App.xaml.cs
|
|
||||||
│
|
|
||||||
├── Views/
|
|
||||||
│ └── MainWindow.xaml # Main window (Grid + StackPanel layout)
|
|
||||||
│ └── MainWindow.xaml.cs # Code-behind (minimal – only TreeView event)
|
|
||||||
│
|
|
||||||
├── ViewModels/
|
|
||||||
│ └── MainViewModel.cs # Root VM: navigation, callouts, props, commands
|
|
||||||
│ └── NavGroupNode.cs # Tree group node VM
|
|
||||||
│ └── NavLeafNode.cs # Tree leaf node VM
|
|
||||||
│ └── InspectionCalloutVM.cs # Overlay callout card VM
|
|
||||||
│ └── CalloutRowVM.cs # Single callout data row VM
|
|
||||||
│ └── RelayCommand.cs # ICommand implementation
|
|
||||||
│
|
|
||||||
├── Models/
|
|
||||||
│ └── FeatureProperties.cs # Bindable domain model for right panel
|
|
||||||
│
|
|
||||||
└── Assets/
|
|
||||||
└── Icons/ # 28×28 toolbar icon PNGs
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### XplorePlane.Hardware(硬件库)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### XplorePlane.ImageProcessing (图像库)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### TO-DO List
|
|
||||||
|
|
||||||
[] 软件基于WPF + Prism 基础的框架, 主页面设计
|
|
||||||
[] 打通与硬件 和图像的调用流程
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志服务接口
|
||||||
|
/// </summary>
|
||||||
|
public interface ILoggerService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 为指定模块创建日志器(使用泛型自动推断类型名)
|
||||||
|
/// </summary>
|
||||||
|
ILoggerService ForModule<T>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为指定模块创建日志器(手动指定模块名)
|
||||||
|
/// </summary>
|
||||||
|
ILoggerService ForModule(string moduleName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录调试信息
|
||||||
|
/// </summary>
|
||||||
|
void Debug(string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录调试信息(带参数)
|
||||||
|
/// </summary>
|
||||||
|
void Debug(string messageTemplate, params object[] propertyValues);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录一般信息
|
||||||
|
/// </summary>
|
||||||
|
void Info(string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录一般信息(带参数)
|
||||||
|
/// </summary>
|
||||||
|
void Info(string messageTemplate, params object[] propertyValues);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录警告信息
|
||||||
|
/// </summary>
|
||||||
|
void Warn(string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录警告信息(带参数)
|
||||||
|
/// </summary>
|
||||||
|
void Warn(string messageTemplate, params object[] propertyValues);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录错误信息
|
||||||
|
/// </summary>
|
||||||
|
void Error(string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录错误信息(带参数)
|
||||||
|
/// </summary>
|
||||||
|
void Error(string messageTemplate, params object[] propertyValues);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录错误信息(带异常)
|
||||||
|
/// </summary>
|
||||||
|
void Error(Exception exception, string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录错误信息(带异常和参数)
|
||||||
|
/// </summary>
|
||||||
|
void Error(Exception exception, string messageTemplate, params object[] propertyValues);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录致命错误
|
||||||
|
/// </summary>
|
||||||
|
void Fatal(string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录致命错误(带参数)
|
||||||
|
/// </summary>
|
||||||
|
void Fatal(string messageTemplate, params object[] propertyValues);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录致命错误(带异常)
|
||||||
|
/// </summary>
|
||||||
|
void Fatal(Exception exception, string message);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录致命错误(带异常和参数)
|
||||||
|
/// </summary>
|
||||||
|
void Fatal(Exception exception, string messageTemplate, params object[] propertyValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
using System;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Core;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志服务实现
|
||||||
|
/// </summary>
|
||||||
|
public class LoggerService : ILoggerService
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly string _moduleName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数
|
||||||
|
/// </summary>
|
||||||
|
public LoggerService() : this(Log.Logger, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数(指定 Serilog Logger)
|
||||||
|
/// </summary>
|
||||||
|
public LoggerService(ILogger logger) : this(logger, null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 私有构造函数(用于创建带模块名的实例)
|
||||||
|
/// </summary>
|
||||||
|
private LoggerService(ILogger logger, string moduleName)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_moduleName = moduleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为指定模块创建日志器(使用泛型自动推断类型名)
|
||||||
|
/// </summary>
|
||||||
|
public ILoggerService ForModule<T>()
|
||||||
|
{
|
||||||
|
var typeName = typeof(T).FullName ?? typeof(T).Name;
|
||||||
|
return new LoggerService(_logger, typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为指定模块创建日志器(手动指定模块名)
|
||||||
|
/// </summary>
|
||||||
|
public ILoggerService ForModule(string moduleName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(moduleName))
|
||||||
|
throw new ArgumentException("模块名不能为空", nameof(moduleName));
|
||||||
|
|
||||||
|
return new LoggerService(_logger, moduleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取带模块名的日志器
|
||||||
|
/// </summary>
|
||||||
|
private ILogger GetLogger()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_moduleName))
|
||||||
|
return _logger;
|
||||||
|
|
||||||
|
return _logger.ForContext("Module", _moduleName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Debug(string message)
|
||||||
|
{
|
||||||
|
GetLogger().Debug(FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Debug(string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Debug(FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Info(string message)
|
||||||
|
{
|
||||||
|
GetLogger().Information(FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Info(string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Information(FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warn(string message)
|
||||||
|
{
|
||||||
|
GetLogger().Warning(FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warn(string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Warning(FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(string message)
|
||||||
|
{
|
||||||
|
GetLogger().Error(FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Error(FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(Exception exception, string message)
|
||||||
|
{
|
||||||
|
GetLogger().Error(exception, FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error(Exception exception, string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Error(exception, FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fatal(string message)
|
||||||
|
{
|
||||||
|
GetLogger().Fatal(FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fatal(string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Fatal(FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fatal(Exception exception, string message)
|
||||||
|
{
|
||||||
|
GetLogger().Fatal(exception, FormatMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues)
|
||||||
|
{
|
||||||
|
GetLogger().Fatal(exception, FormatMessage(messageTemplate), propertyValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 格式化消息(添加模块名前缀)
|
||||||
|
/// </summary>
|
||||||
|
private string FormatMessage(string message)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_moduleName))
|
||||||
|
return message;
|
||||||
|
|
||||||
|
return $"[{_moduleName}] {message}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志服务使用示例
|
||||||
|
/// </summary>
|
||||||
|
public class LoggingExample
|
||||||
|
{
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
public LoggingExample(ILoggerService logger)
|
||||||
|
{
|
||||||
|
// 推荐方式:使用泛型自动推断类型名
|
||||||
|
_logger = logger?.ForModule<LoggingExample>() ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 基本日志示例
|
||||||
|
/// </summary>
|
||||||
|
public void BasicLoggingExample()
|
||||||
|
{
|
||||||
|
// 调试信息
|
||||||
|
_logger.Debug("这是调试信息");
|
||||||
|
|
||||||
|
// 一般信息
|
||||||
|
_logger.Info("应用程序启动成功");
|
||||||
|
|
||||||
|
// 警告信息
|
||||||
|
_logger.Warn("连接不稳定,正在重试...");
|
||||||
|
|
||||||
|
// 错误信息
|
||||||
|
_logger.Error("操作失败");
|
||||||
|
|
||||||
|
// 致命错误
|
||||||
|
_logger.Fatal("系统崩溃");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结构化日志示例
|
||||||
|
/// </summary>
|
||||||
|
public void StructuredLoggingExample()
|
||||||
|
{
|
||||||
|
var userId = 12345;
|
||||||
|
var action = "登录";
|
||||||
|
var voltage = 150.5f;
|
||||||
|
var current = 500;
|
||||||
|
|
||||||
|
// 使用占位符(推荐)
|
||||||
|
_logger.Info("用户 {UserId} 执行了操作 {Action}", userId, action);
|
||||||
|
_logger.Info("设置电压为 {Voltage} kV,电流为 {Current} μA", voltage, current);
|
||||||
|
|
||||||
|
// 不推荐:字符串拼接
|
||||||
|
// _logger.Info($"用户 {userId} 执行了操作 {action}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异常日志示例
|
||||||
|
/// </summary>
|
||||||
|
public async Task ExceptionLoggingExample()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 模拟操作
|
||||||
|
await Task.Delay(100);
|
||||||
|
throw new InvalidOperationException("模拟异常");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// 记录异常(带上下文信息)
|
||||||
|
_logger.Error(ex, "操作失败:参数={Parameter}", "test");
|
||||||
|
|
||||||
|
// 或者简单记录
|
||||||
|
_logger.Error(ex, "操作失败");
|
||||||
|
|
||||||
|
// 致命错误
|
||||||
|
_logger.Fatal(ex, "系统发生致命错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不同模块名示例
|
||||||
|
/// </summary>
|
||||||
|
public void DifferentModuleNameExample(ILoggerService logger)
|
||||||
|
{
|
||||||
|
// 方式 1:使用泛型(推荐)
|
||||||
|
var logger1 = logger.ForModule<LoggingExample>();
|
||||||
|
logger1.Info("使用泛型推断的模块名");
|
||||||
|
// 输出: [XplorePlane.Services.LoggingExample] 使用泛型推断的模块名
|
||||||
|
|
||||||
|
// 方式 2:手动指定模块名
|
||||||
|
var logger2 = logger.ForModule("CustomModule");
|
||||||
|
logger2.Info("使用自定义模块名");
|
||||||
|
// 输出: [CustomModule] 使用自定义模块名
|
||||||
|
|
||||||
|
// 方式 3:不指定模块名
|
||||||
|
logger.Info("没有模块名");
|
||||||
|
// 输出: 没有模块名
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实际业务场景示例
|
||||||
|
/// </summary>
|
||||||
|
public async Task<bool> BusinessScenarioExample()
|
||||||
|
{
|
||||||
|
_logger.Info("开始初始化硬件...");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 步骤 1
|
||||||
|
_logger.Debug("步骤 1: 检查硬件连接");
|
||||||
|
await Task.Delay(100);
|
||||||
|
|
||||||
|
// 步骤 2
|
||||||
|
_logger.Debug("步骤 2: 加载配置");
|
||||||
|
var config = LoadConfig();
|
||||||
|
_logger.Info("配置加载成功:{ConfigName}", config);
|
||||||
|
|
||||||
|
// 步骤 3
|
||||||
|
_logger.Debug("步骤 3: 建立连接");
|
||||||
|
await Task.Delay(100);
|
||||||
|
|
||||||
|
_logger.Info("硬件初始化成功");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "硬件初始化失败");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string LoadConfig()
|
||||||
|
{
|
||||||
|
return "DefaultConfig";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 日志系统验证类 - 用于测试日志功能是否正常工作
|
||||||
|
/// </summary>
|
||||||
|
public class LoggingVerification
|
||||||
|
{
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
|
||||||
|
public LoggingVerification(ILoggerService logger)
|
||||||
|
{
|
||||||
|
_logger = logger?.ForModule<LoggingVerification>() ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 运行完整的日志验证测试
|
||||||
|
/// </summary>
|
||||||
|
public async Task RunVerificationAsync()
|
||||||
|
{
|
||||||
|
_logger.Info("========================================");
|
||||||
|
_logger.Info("开始日志系统验证测试");
|
||||||
|
_logger.Info("========================================");
|
||||||
|
|
||||||
|
// 测试 1: 基本日志级别
|
||||||
|
TestBasicLogLevels();
|
||||||
|
|
||||||
|
// 测试 2: 结构化日志
|
||||||
|
TestStructuredLogging();
|
||||||
|
|
||||||
|
// 测试 3: 异常日志
|
||||||
|
await TestExceptionLoggingAsync();
|
||||||
|
|
||||||
|
// 测试 4: 模块名
|
||||||
|
TestModuleNames();
|
||||||
|
|
||||||
|
_logger.Info("========================================");
|
||||||
|
_logger.Info("日志系统验证测试完成");
|
||||||
|
_logger.Info("========================================");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestBasicLogLevels()
|
||||||
|
{
|
||||||
|
_logger.Info("测试 1: 基本日志级别");
|
||||||
|
_logger.Debug("这是 Debug 级别日志");
|
||||||
|
_logger.Info("这是 Info 级别日志");
|
||||||
|
_logger.Warn("这是 Warn 级别日志");
|
||||||
|
_logger.Error("这是 Error 级别日志");
|
||||||
|
_logger.Info("✓ 基本日志级别测试完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestStructuredLogging()
|
||||||
|
{
|
||||||
|
_logger.Info("测试 2: 结构化日志");
|
||||||
|
|
||||||
|
var userId = 12345;
|
||||||
|
var userName = "张三";
|
||||||
|
var voltage = 150.5f;
|
||||||
|
var current = 500;
|
||||||
|
var timestamp = DateTime.Now;
|
||||||
|
|
||||||
|
_logger.Info("用户登录: UserId={UserId}, UserName={UserName}", userId, userName);
|
||||||
|
_logger.Info("设置参数: Voltage={Voltage}kV, Current={Current}μA", voltage, current);
|
||||||
|
_logger.Info("操作时间: {Timestamp:yyyy-MM-dd HH:mm:ss}", timestamp);
|
||||||
|
|
||||||
|
_logger.Info("✓ 结构化日志测试完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TestExceptionLoggingAsync()
|
||||||
|
{
|
||||||
|
_logger.Info("测试 3: 异常日志");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(10);
|
||||||
|
throw new InvalidOperationException("这是一个测试异常");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "捕获到异常: {Message}", ex.Message);
|
||||||
|
_logger.Info("✓ 异常日志测试完成");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TestModuleNames()
|
||||||
|
{
|
||||||
|
_logger.Info("测试 4: 模块名功能");
|
||||||
|
_logger.Info("当前模块名应该是: XplorePlane.Services.LoggingVerification");
|
||||||
|
_logger.Info("✓ 模块名测试完成");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# Services 目录说明
|
||||||
|
|
||||||
|
此目录用于存放应用程序的服务层代码,负责封装硬件库的调用和业务逻辑。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
Services/
|
||||||
|
├── IHardwareService.cs # 硬件服务接口
|
||||||
|
├── HardwareService.cs # 硬件服务实现
|
||||||
|
├── IConfigurationService.cs # 配置服务接口
|
||||||
|
└── ConfigurationService.cs # 配置服务实现
|
||||||
|
```
|
||||||
|
|
||||||
|
## 服务说明
|
||||||
|
|
||||||
|
### 1. HardwareService
|
||||||
|
|
||||||
|
统一管理所有硬件模块的服务适配器。
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 初始化和关闭所有硬件
|
||||||
|
- 射线源控制(开关、参数设置)
|
||||||
|
- 探测器控制(图像采集、配置)
|
||||||
|
- PLC 通讯(变量读写)
|
||||||
|
|
||||||
|
**使用示例**:
|
||||||
|
```csharp
|
||||||
|
// 在 ViewModel 中注入
|
||||||
|
public class MainViewModel
|
||||||
|
{
|
||||||
|
private readonly IHardwareService _hardwareService;
|
||||||
|
|
||||||
|
public MainViewModel(IHardwareService hardwareService)
|
||||||
|
{
|
||||||
|
_hardwareService = hardwareService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeAsync()
|
||||||
|
{
|
||||||
|
await _hardwareService.InitializeAllAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. ConfigurationService
|
||||||
|
|
||||||
|
管理应用程序配置的服务。
|
||||||
|
|
||||||
|
**功能**:
|
||||||
|
- 读取 App.config 配置
|
||||||
|
- 类型安全的配置访问
|
||||||
|
- 默认值支持
|
||||||
|
|
||||||
|
## 依赖注入配置
|
||||||
|
|
||||||
|
在 `App.xaml.cs` 中注册服务:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
protected override void RegisterTypes(IContainerRegistry containerRegistry)
|
||||||
|
{
|
||||||
|
// 注册硬件服务
|
||||||
|
containerRegistry.RegisterSingleton<IHardwareService, HardwareService>();
|
||||||
|
|
||||||
|
// 注册配置服务
|
||||||
|
containerRegistry.RegisterSingleton<IConfigurationService, ConfigurationService>();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **服务生命周期**:硬件服务应注册为 Singleton,确保全局唯一
|
||||||
|
2. **异步操作**:所有硬件操作都应使用异步方法
|
||||||
|
3. **错误处理**:服务层应捕获并记录所有异常
|
||||||
|
4. **资源释放**:实现 IDisposable 接口,确保资源正确释放
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
using System;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.IO;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Serilog 配置类
|
||||||
|
/// </summary>
|
||||||
|
public static class SerilogConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 Serilog
|
||||||
|
/// </summary>
|
||||||
|
public static void Configure()
|
||||||
|
{
|
||||||
|
// 读取配置
|
||||||
|
var logPath = GetConfigValue("Serilog:LogPath", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"));
|
||||||
|
var minimumLevel = GetLogLevel(GetConfigValue("Serilog:MinimumLevel", "Information"));
|
||||||
|
var enableConsole = GetConfigValue("Serilog:EnableConsole", "true").ToLower() == "true";
|
||||||
|
var rollingInterval = GetRollingInterval(GetConfigValue("Serilog:RollingInterval", "Day"));
|
||||||
|
var fileSizeLimitMB = int.Parse(GetConfigValue("Serilog:FileSizeLimitMB", "100"));
|
||||||
|
var retainedFileCountLimit = int.Parse(GetConfigValue("Serilog:RetainedFileCountLimit", "30"));
|
||||||
|
|
||||||
|
// 确保日志目录存在
|
||||||
|
if (!Directory.Exists(logPath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(logPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置 Serilog
|
||||||
|
var loggerConfig = new LoggerConfiguration()
|
||||||
|
.MinimumLevel.Is(minimumLevel)
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
.Enrich.WithProperty("Application", "XplorePlane")
|
||||||
|
.Enrich.WithProperty("MachineName", Environment.MachineName)
|
||||||
|
.WriteTo.File(
|
||||||
|
path: Path.Combine(logPath, "xploreplane-.log"),
|
||||||
|
rollingInterval: rollingInterval,
|
||||||
|
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
|
||||||
|
fileSizeLimitBytes: fileSizeLimitMB * 1024 * 1024,
|
||||||
|
retainedFileCountLimit: retainedFileCountLimit,
|
||||||
|
shared: true
|
||||||
|
);
|
||||||
|
|
||||||
|
// 添加控制台输出
|
||||||
|
if (enableConsole)
|
||||||
|
{
|
||||||
|
loggerConfig.WriteTo.Console(
|
||||||
|
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加调试输出(仅在 Debug 模式)
|
||||||
|
#if DEBUG
|
||||||
|
loggerConfig.WriteTo.Debug(
|
||||||
|
outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 创建全局 Logger
|
||||||
|
Log.Logger = loggerConfig.CreateLogger();
|
||||||
|
|
||||||
|
Log.Information("========================================");
|
||||||
|
Log.Information("XplorePlane 应用程序启动");
|
||||||
|
Log.Information("日志路径: {LogPath}", logPath);
|
||||||
|
Log.Information("日志级别: {MinimumLevel}", minimumLevel);
|
||||||
|
Log.Information("========================================");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关闭并刷新日志
|
||||||
|
/// </summary>
|
||||||
|
public static void CloseAndFlush()
|
||||||
|
{
|
||||||
|
Log.Information("========================================");
|
||||||
|
Log.Information("XplorePlane 应用程序退出");
|
||||||
|
Log.Information("========================================");
|
||||||
|
Log.CloseAndFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置值
|
||||||
|
/// </summary>
|
||||||
|
private static string GetConfigValue(string key, string defaultValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return ConfigurationManager.AppSettings[key] ?? defaultValue;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取日志级别
|
||||||
|
/// </summary>
|
||||||
|
private static LogEventLevel GetLogLevel(string level)
|
||||||
|
{
|
||||||
|
return level.ToLower() switch
|
||||||
|
{
|
||||||
|
"verbose" => LogEventLevel.Verbose,
|
||||||
|
"debug" => LogEventLevel.Debug,
|
||||||
|
"information" => LogEventLevel.Information,
|
||||||
|
"warning" => LogEventLevel.Warning,
|
||||||
|
"error" => LogEventLevel.Error,
|
||||||
|
"fatal" => LogEventLevel.Fatal,
|
||||||
|
_ => LogEventLevel.Information
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取滚动间隔
|
||||||
|
/// </summary>
|
||||||
|
private static RollingInterval GetRollingInterval(string interval)
|
||||||
|
{
|
||||||
|
return interval.ToLower() switch
|
||||||
|
{
|
||||||
|
"infinite" => RollingInterval.Infinite,
|
||||||
|
"year" => RollingInterval.Year,
|
||||||
|
"month" => RollingInterval.Month,
|
||||||
|
"day" => RollingInterval.Day,
|
||||||
|
"hour" => RollingInterval.Hour,
|
||||||
|
"minute" => RollingInterval.Minute,
|
||||||
|
_ => RollingInterval.Day
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
using Prism.Commands;
|
using Prism.Commands;
|
||||||
using Prism.Mvvm;
|
using Prism.Mvvm;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using XplorePlane.Services;
|
||||||
|
|
||||||
namespace XplorePlane.ViewModels
|
namespace XplorePlane.ViewModels
|
||||||
{
|
{
|
||||||
public class MainViewModel : BindableBase
|
public class MainViewModel : BindableBase
|
||||||
{
|
{
|
||||||
private string _licenseInfo = "µ±Ç°Ê±¼ä";
|
private readonly ILoggerService _logger;
|
||||||
|
private string _licenseInfo = "当前时间";
|
||||||
|
|
||||||
|
public string LicenseInfo
|
||||||
|
{
|
||||||
|
get => _licenseInfo;
|
||||||
|
set => SetProperty(ref _licenseInfo, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<object> NavigationTree { get; set; }
|
public ObservableCollection<object> NavigationTree { get; set; }
|
||||||
|
|
||||||
@@ -18,16 +25,56 @@ namespace XplorePlane.ViewModels
|
|||||||
public DelegateCommand ClearCommand { get; set; }
|
public DelegateCommand ClearCommand { get; set; }
|
||||||
public DelegateCommand EditPropertiesCommand { get; set; }
|
public DelegateCommand EditPropertiesCommand { get; set; }
|
||||||
|
|
||||||
public MainViewModel()
|
public MainViewModel(ILoggerService logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger?.ForModule<MainViewModel>() ?? throw new System.ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
NavigationTree = new ObservableCollection<object>();
|
NavigationTree = new ObservableCollection<object>();
|
||||||
|
|
||||||
NavigateHomeCommand = new DelegateCommand(() => { });
|
NavigateHomeCommand = new DelegateCommand(OnNavigateHome);
|
||||||
NavigateInspectCommand = new DelegateCommand(() => { });
|
NavigateInspectCommand = new DelegateCommand(OnNavigateInspect);
|
||||||
OpenFileCommand = new DelegateCommand(() => { });
|
OpenFileCommand = new DelegateCommand(OnOpenFile);
|
||||||
ExportCommand = new DelegateCommand(() => { });
|
ExportCommand = new DelegateCommand(OnExport);
|
||||||
ClearCommand = new DelegateCommand(() => { });
|
ClearCommand = new DelegateCommand(OnClear);
|
||||||
EditPropertiesCommand = new DelegateCommand(() => { });
|
EditPropertiesCommand = new DelegateCommand(OnEditProperties);
|
||||||
|
|
||||||
|
_logger.Info("MainViewModel 已初始化");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNavigateHome()
|
||||||
|
{
|
||||||
|
_logger.Info("导航到主页");
|
||||||
|
LicenseInfo = "主页";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnNavigateInspect()
|
||||||
|
{
|
||||||
|
_logger.Info("导航到检测页面");
|
||||||
|
LicenseInfo = "检测页面";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOpenFile()
|
||||||
|
{
|
||||||
|
_logger.Info("打开文件");
|
||||||
|
LicenseInfo = "打开文件";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExport()
|
||||||
|
{
|
||||||
|
_logger.Info("导出数据");
|
||||||
|
LicenseInfo = "导出数据";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClear()
|
||||||
|
{
|
||||||
|
_logger.Info("清除数据");
|
||||||
|
LicenseInfo = "清除数据";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEditProperties()
|
||||||
|
{
|
||||||
|
_logger.Info("编辑属性");
|
||||||
|
LicenseInfo = "编辑属性";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,10 @@
|
|||||||
Background="#F5F5F5"
|
Background="#F5F5F5"
|
||||||
WindowStartupLocation="CenterScreen">
|
WindowStartupLocation="CenterScreen">
|
||||||
|
|
||||||
<Window.DataContext>
|
|
||||||
<vm:MainViewModel />
|
|
||||||
</Window.DataContext>
|
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="169*"/>
|
||||||
|
<ColumnDefinition Width="47*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
@@ -26,7 +23,7 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<!-- Row 0: Fluent Ribbon -->
|
<!-- Row 0: Fluent Ribbon -->
|
||||||
<fluent:Ribbon Grid.Row="0" x:Name="Ribbon">
|
<fluent:Ribbon Grid.Row="0" x:Name="Ribbon" Grid.ColumnSpan="2" Grid.RowSpan="2">
|
||||||
<fluent:RibbonTabItem Header="主页">
|
<fluent:RibbonTabItem Header="主页">
|
||||||
<fluent:RibbonGroupBox Header="导航">
|
<fluent:RibbonGroupBox Header="导航">
|
||||||
<fluent:Button Header="主页" Command="{Binding NavigateHomeCommand}" LargeIcon="pack://application:,,,/Assets/Icons/home.png" Size="Large"/>
|
<fluent:Button Header="主页" Command="{Binding NavigateHomeCommand}" LargeIcon="pack://application:,,,/Assets/Icons/home.png" Size="Large"/>
|
||||||
@@ -46,7 +43,7 @@
|
|||||||
</fluent:Ribbon>
|
</fluent:Ribbon>
|
||||||
|
|
||||||
<!-- Row 1: Main Content -->
|
<!-- Row 1: Main Content -->
|
||||||
<Grid Grid.Row="1">
|
<Grid Grid.Row="1" Grid.ColumnSpan="2" Margin="0,0,0,24" Grid.RowSpan="2">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="300
|
<ColumnDefinition Width="300
|
||||||
" MinWidth="200"/>
|
" MinWidth="200"/>
|
||||||
@@ -76,7 +73,7 @@
|
|||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
<Border Grid.Row="0" Background="#FFFFFF" BorderBrush="#DDDDDD" BorderThickness="1" Margin="0,0,0,3" Grid.RowSpan="2">
|
<Border Grid.Row="0" Background="#FFFFFF" BorderBrush="#DDDDDD" BorderThickness="1" Margin="0,0,0,3" Grid.RowSpan="2">
|
||||||
<TextBlock Text="3D Viewport" Foreground="#666666" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16"/>
|
<TextBlock Text="2D Viewport" Foreground="#666666" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16"/>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
@@ -120,7 +117,7 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- Row 2: Status Bar -->
|
<!-- Row 2: Status Bar -->
|
||||||
<Border Grid.Row="2" Background="#F0F0F0" BorderBrush="#DDDDDD" BorderThickness="0,1,0,0">
|
<Border Grid.Row="2" Background="#F0F0F0" BorderBrush="#DDDDDD" BorderThickness="0,1,0,0" Grid.ColumnSpan="2">
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
namespace XplorePlane.Views
|
namespace XplorePlane.Views
|
||||||
{
|
{
|
||||||
@@ -7,9 +8,10 @@ namespace XplorePlane.Views
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
public MainWindow()
|
public MainWindow(MainViewModel viewModel)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,33 @@
|
|||||||
<RootNamespace>XplorePlane</RootNamespace>
|
<RootNamespace>XplorePlane</RootNamespace>
|
||||||
<AssemblyName>XplorePlane</AssemblyName>
|
<AssemblyName>XplorePlane</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="MainWindow.xaml.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove="XplorePlane.csproj.Backup.tmp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Page Remove="MainWindow.xaml" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Prism.DryIoc" Version="9.0.537" />
|
<PackageReference Include="Prism.DryIoc" Version="9.0.537" />
|
||||||
<PackageReference Include="Fluent.Ribbon" Version="9.0.0" />
|
<PackageReference Include="Fluent.Ribbon" Version="9.0.0" />
|
||||||
|
|
||||||
|
<!-- Serilog 日志框架 -->
|
||||||
|
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Debug" Version="3.0.0" />
|
||||||
|
|
||||||
|
<!-- 配置管理 -->
|
||||||
|
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- 确保 App.config 复制到输出目录 -->
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="App.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
Reference in New Issue
Block a user