diff --git a/XP.Scan/App.config b/XP.Scan/App.config
new file mode 100644
index 0000000..0359d51
--- /dev/null
+++ b/XP.Scan/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/XP.Scan/App.xaml b/XP.Scan/App.xaml
new file mode 100644
index 0000000..c910703
--- /dev/null
+++ b/XP.Scan/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/XP.Scan/App.xaml.cs b/XP.Scan/App.xaml.cs
new file mode 100644
index 0000000..f4b0bec
--- /dev/null
+++ b/XP.Scan/App.xaml.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Windows;
+using Prism.Container.DryIoc;
+using Prism.Ioc;
+using XP.Common.Configs;
+using XP.Common.Dump.Configs;
+using XP.Common.Dump.Implementations;
+using XP.Common.Dump.Interfaces;
+using XP.Common.Helpers;
+using XP.Common.Localization.Configs;
+using XP.Common.Localization.Extensions;
+using XP.Common.Localization;
+using XP.Common.Localization.Implementations;
+using XP.Common.Localization.Interfaces;
+using XP.Common.Logging;
+using XP.Common.Logging.Implementations;
+using XP.Common.Logging.Interfaces;
+using System.Resources;
+using XP.Scan.Views;
+using Prism.Navigation.Regions;
+
+namespace XP.Scan
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App
+ {
+ protected override Window CreateShell()
+ {
+ // 在创建窗口前初始化 LocalizationExtension,确保 XAML 标记扩展能获取到翻译
+ var localizationService = Container.Resolve();
+
+ // 强制设置 UI 语言为中文(确保 ResourceManager 返回中文资源)
+ var zhCN = new System.Globalization.CultureInfo("zh-CN");
+ System.Globalization.CultureInfo.CurrentUICulture = zhCN;
+ System.Globalization.CultureInfo.CurrentCulture = zhCN;
+
+ LocalizationExtension.Initialize(localizationService);
+ LocalizationHelper.Initialize(localizationService);
+
+ // 注册 XP.Scan 模块的资源源 | Register XP.Scan module resource source
+ var scanResourceManager = new ResourceManager("XP.Scan.Resources.Resources", typeof(App).Assembly);
+ localizationService.RegisterResourceSource("XP.Scan", scanResourceManager);
+
+ return Container.Resolve();
+ }
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ // 启动时导航到 XPScanView
+ var regionManager = Container.Resolve();
+ regionManager.RequestNavigate("ContentRegion", nameof(XPScanView));
+ }
+
+ protected override void RegisterTypes(IContainerRegistry containerRegistry)
+ {
+ // 初始化 Serilog
+ SerilogInitializer.Initialize(new SerilogConfig
+ {
+ LogPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Files", "Logs")
+ });
+
+ // 日志服务
+ containerRegistry.RegisterSingleton();
+
+ // 本地化服务
+ containerRegistry.RegisterSingleton();
+ containerRegistry.RegisterSingleton();
+
+ // Dump 服务
+ containerRegistry.RegisterSingleton(() => ConfigLoader.LoadDumpConfig());
+ containerRegistry.RegisterSingleton();
+
+ // 注册 XPScanView 用于区域导航
+ containerRegistry.RegisterForNavigation();
+ }
+
+ protected override IContainerExtension CreateContainerExtension()
+ {
+ return new DryIocContainerExtension();
+ }
+ }
+}
diff --git a/XP.Scan/Properties/Licenses.licx b/XP.Scan/Properties/Licenses.licx
new file mode 100644
index 0000000..3fe9014
--- /dev/null
+++ b/XP.Scan/Properties/Licenses.licx
@@ -0,0 +1 @@
+Telerik.Windows.Controls.RadNumericUpDown, Telerik.Windows.Controls.Input, Version=2024.1.408.310, Culture=neutral, PublicKeyToken=5803cfa389c90ce7
diff --git a/XP.Scan/Resources/Resources.en-US.resx b/XP.Scan/Resources/Resources.en-US.resx
new file mode 100644
index 0000000..af6d4fe
--- /dev/null
+++ b/XP.Scan/Resources/Resources.en-US.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Scan Mode:
+ Scan - Scan mode label
+
+
+ Frame Merge:
+ Scan - Frame merge label
+
+
+ Acquisition Count:
+ Scan - Acquisition count label
+
+
+ Rotation Angle:
+ Scan - Rotation angle label
+
+
+ Progress:
+ Scan - Acquisition progress label
+
+
+ Start
+ Scan - Start acquisition button
+
+
+ Stop
+ Scan - Stop acquisition button
+
+
diff --git a/XP.Scan/Resources/Resources.resx b/XP.Scan/Resources/Resources.resx
new file mode 100644
index 0000000..668ba28
--- /dev/null
+++ b/XP.Scan/Resources/Resources.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 采集模式:
+ Scan - 采集模式标签 | Scan mode label
+
+
+ 帧合并:
+ Scan - 帧合并标签 | Frame merge label
+
+
+ 采集张数:
+ Scan - 采集张数标签 | Acquisition count label
+
+
+ 旋转角度:
+ Scan - 旋转角度标签 | Rotation angle label
+
+
+ 采集进度:
+ Scan - 采集进度标签 | Acquisition progress label
+
+
+ 开始采集
+ Scan - 开始采集按钮 | Start acquisition button
+
+
+ 停止采集
+ Scan - 停止采集按钮 | Stop acquisition button
+
+
diff --git a/XP.Scan/Resources/Resources.zh-CN.resx b/XP.Scan/Resources/Resources.zh-CN.resx
new file mode 100644
index 0000000..668ba28
--- /dev/null
+++ b/XP.Scan/Resources/Resources.zh-CN.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 采集模式:
+ Scan - 采集模式标签 | Scan mode label
+
+
+ 帧合并:
+ Scan - 帧合并标签 | Frame merge label
+
+
+ 采集张数:
+ Scan - 采集张数标签 | Acquisition count label
+
+
+ 旋转角度:
+ Scan - 旋转角度标签 | Rotation angle label
+
+
+ 采集进度:
+ Scan - 采集进度标签 | Acquisition progress label
+
+
+ 开始采集
+ Scan - 开始采集按钮 | Start acquisition button
+
+
+ 停止采集
+ Scan - 停止采集按钮 | Stop acquisition button
+
+
diff --git a/XP.Scan/Resources/Resources.zh-TW.resx b/XP.Scan/Resources/Resources.zh-TW.resx
new file mode 100644
index 0000000..f64b9e3
--- /dev/null
+++ b/XP.Scan/Resources/Resources.zh-TW.resx
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 採集模式:
+ Scan - 採集模式標籤 | Scan mode label
+
+
+ 幀合併:
+ Scan - 幀合併標籤 | Frame merge label
+
+
+ 採集張數:
+ Scan - 採集張數標籤 | Acquisition count label
+
+
+ 旋轉角度:
+ Scan - 旋轉角度標籤 | Rotation angle label
+
+
+ 採集進度:
+ Scan - 採集進度標籤 | Acquisition progress label
+
+
+ 開始採集
+ Scan - 開始採集按鈕 | Start acquisition button
+
+
+ 停止採集
+ Scan - 停止採集按鈕 | Stop acquisition button
+
+
\ No newline at end of file
diff --git a/XP.Scan/ViewModels/MainWindowViewModel.cs b/XP.Scan/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..753253a
--- /dev/null
+++ b/XP.Scan/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,23 @@
+using Prism.Mvvm;
+
+namespace XP.Scan.ViewModels
+{
+ public class MainWindowViewModel : BindableBase
+ {
+ private string _title = "XplorePlane";
+
+ public string Title
+ {
+ get => _title;
+ set => SetProperty(ref _title, value);
+ }
+ }
+
+ public class AcquisitionMode
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+
+ public override string ToString() => Name;
+ }
+}
diff --git a/XP.Scan/ViewModels/XPScanViewModel.cs b/XP.Scan/ViewModels/XPScanViewModel.cs
new file mode 100644
index 0000000..2e4e2f3
--- /dev/null
+++ b/XP.Scan/ViewModels/XPScanViewModel.cs
@@ -0,0 +1,109 @@
+using Prism.Commands;
+using Prism.Mvvm;
+using System.Collections.ObjectModel;
+using System.Windows.Input;
+using XP.Common.Localization.Interfaces;
+using XP.Common.Logging.Interfaces;
+
+namespace XP.Scan.ViewModels
+{
+ public class XPScanViewModel : BindableBase
+ {
+ private readonly ILoggerService _logger;
+ private readonly ILocalizationService _localizationService;
+ private AcquisitionMode _selectedAcquisitionMode;
+ private int _mergeLevel = 1;
+ private int _acquisitionCount = 10;
+ private double _rotationAngle = 0;
+ private double _acquisitionProgress = 0;
+ private bool _canStartAcquisition = true;
+ private bool _canStopAcquisition = false;
+
+ // 本地化文本属性
+ public string LabelScanMode => _localizationService.GetString("Scan_Text_ScanMode");
+ public string LabelFrameMerge => _localizationService.GetString("Scan_Text_FrameMerge");
+ public string LabelNums => _localizationService.GetString("Scan_Text_Nums");
+ public string LabelAngles => _localizationService.GetString("Scan_Text_Angles");
+ public string LabelProgress => _localizationService.GetString("Scan_Text_Progress");
+ public string LabelStart => _localizationService.GetString("Scan_Button_Start");
+ public string LabelStop => _localizationService.GetString("Scan_Button_Stop");
+
+ public ObservableCollection AcquisitionModes { get; set; }
+
+ public AcquisitionMode SelectedAcquisitionMode
+ {
+ get => _selectedAcquisitionMode;
+ set => SetProperty(ref _selectedAcquisitionMode, value);
+ }
+
+ public int MergeLevel
+ {
+ get => _mergeLevel;
+ set => SetProperty(ref _mergeLevel, value);
+ }
+
+ public int AcquisitionCount
+ {
+ get => _acquisitionCount;
+ set => SetProperty(ref _acquisitionCount, value);
+ }
+
+ public double RotationAngle
+ {
+ get => _rotationAngle;
+ set => SetProperty(ref _rotationAngle, value);
+ }
+
+ public double AcquisitionProgress
+ {
+ get => _acquisitionProgress;
+ set => SetProperty(ref _acquisitionProgress, value);
+ }
+
+ public bool CanStartAcquisition
+ {
+ get => _canStartAcquisition;
+ set => SetProperty(ref _canStartAcquisition, value);
+ }
+
+ public bool CanStopAcquisition
+ {
+ get => _canStopAcquisition;
+ set => SetProperty(ref _canStopAcquisition, value);
+ }
+
+ public ICommand StartAcquisitionCommand { get; }
+ public ICommand StopAcquisitionCommand { get; }
+
+ private void StartAcquisition()
+ {
+ _logger.Info("开始采集 | Start acquisition");
+ CanStartAcquisition = false;
+ CanStopAcquisition = true;
+ }
+
+ private void StopAcquisition()
+ {
+ _logger.Info("停止采集 | Stop acquisition");
+ CanStartAcquisition = true;
+ CanStopAcquisition = false;
+ }
+
+ public XPScanViewModel(ILoggerService loggerService, ILocalizationService localizationService)
+ {
+ _logger = loggerService.ForModule();
+ _localizationService = localizationService;
+
+ AcquisitionModes = new ObservableCollection
+ {
+ new AcquisitionMode { Id = 1, Name = "QucikScan" },
+ new AcquisitionMode { Id = 2, Name = "QualityScan" }
+ };
+
+ SelectedAcquisitionMode = AcquisitionModes[0];
+
+ StartAcquisitionCommand = new DelegateCommand(StartAcquisition);
+ StopAcquisitionCommand = new DelegateCommand(StopAcquisition);
+ }
+ }
+}
diff --git a/XP.Scan/Views/MainWindow.xaml b/XP.Scan/Views/MainWindow.xaml
new file mode 100644
index 0000000..1b84ab0
--- /dev/null
+++ b/XP.Scan/Views/MainWindow.xaml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/XP.Scan/Views/MainWindow.xaml.cs b/XP.Scan/Views/MainWindow.xaml.cs
new file mode 100644
index 0000000..0f4461e
--- /dev/null
+++ b/XP.Scan/Views/MainWindow.xaml.cs
@@ -0,0 +1,15 @@
+using System.Windows;
+
+namespace XP.Scan.Views
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/XP.Scan/Views/XPScanView.xaml b/XP.Scan/Views/XPScanView.xaml
new file mode 100644
index 0000000..1b9befa
--- /dev/null
+++ b/XP.Scan/Views/XPScanView.xaml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/XP.Scan/Views/XPScanView.xaml.cs b/XP.Scan/Views/XPScanView.xaml.cs
new file mode 100644
index 0000000..7b3dd76
--- /dev/null
+++ b/XP.Scan/Views/XPScanView.xaml.cs
@@ -0,0 +1,12 @@
+using System.Windows.Controls;
+
+namespace XP.Scan.Views
+{
+ public partial class XPScanView : UserControl
+ {
+ public XPScanView()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/XP.Scan/XP.Scan.csproj b/XP.Scan/XP.Scan.csproj
new file mode 100644
index 0000000..9b2218a
--- /dev/null
+++ b/XP.Scan/XP.Scan.csproj
@@ -0,0 +1,36 @@
+
+
+ WinExe
+ net8.0-windows10.0.26100.0
+ true
+
+
+
+
+
+
+
+
+
+
+ PublicResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+ Resources.resx
+
+
+ Resources.resx
+
+
+ Resources.resx
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
\ No newline at end of file
diff --git a/XplorePlane.sln b/XplorePlane.sln
index 0a25d71..e525d5e 100644
--- a/XplorePlane.sln
+++ b/XplorePlane.sln
@@ -60,6 +60,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XP.Hardware", "XP.Hardware"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XP.ImageProcessing", "XP.ImageProcessing", "{C24535A4-6717-4149-AB81-1EF09A15F90F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XP.Scan", "XP.Scan\XP.Scan.csproj", "{F40C71DC-7639-CD57-6183-2EAA78980EC5}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "XP.ScanMode", "XP.ScanMode", "{E208A5EA-7E3B-46B4-B045-A703F6274218}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -250,6 +254,18 @@ Global
{6170AF9F-A792-6BDC-4E25-072EA87FAA15}.Release|x64.Build.0 = Release|Any CPU
{6170AF9F-A792-6BDC-4E25-072EA87FAA15}.Release|x86.ActiveCfg = Release|Any CPU
{6170AF9F-A792-6BDC-4E25-072EA87FAA15}.Release|x86.Build.0 = Release|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Debug|x64.Build.0 = Debug|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Debug|x86.Build.0 = Debug|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Release|x64.ActiveCfg = Release|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Release|x64.Build.0 = Release|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Release|x86.ActiveCfg = Release|Any CPU
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -267,6 +283,7 @@ Global
{67D180E8-AB8F-FF62-ED46-270803B8F713} = {29E2D405-341A-4445-B788-3E77A677C2BA}
{B8F5E3A1-7C2D-4E9F-A1B3-6D8E4F2C9A01} = {29E2D405-341A-4445-B788-3E77A677C2BA}
{6170AF9F-A792-6BDC-4E25-072EA87FAA15} = {29E2D405-341A-4445-B788-3E77A677C2BA}
+ {F40C71DC-7639-CD57-6183-2EAA78980EC5} = {E208A5EA-7E3B-46B4-B045-A703F6274218}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DB6D69BA-49FD-432F-8069-2A8F64933CDE}