Compare commits
4 Commits
a483144d29
...
7c0f9dab73
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c0f9dab73 | |||
| 3bee2898c5 | |||
| 9a8831c945 | |||
| db8a37410f |
@@ -6,6 +6,7 @@ using System.Windows.Media.Imaging;
|
|||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services;
|
using XplorePlane.Services;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
using XplorePlane.Tests.Helpers;
|
using XplorePlane.Tests.Helpers;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
@@ -22,6 +23,7 @@ namespace XplorePlane.Tests.Pipeline
|
|||||||
private readonly Mock<IPipelineExecutionService> _mockExecSvc;
|
private readonly Mock<IPipelineExecutionService> _mockExecSvc;
|
||||||
private readonly Mock<IPipelinePersistenceService> _mockPersistSvc;
|
private readonly Mock<IPipelinePersistenceService> _mockPersistSvc;
|
||||||
private readonly Mock<ILoggerService> _mockLogger;
|
private readonly Mock<ILoggerService> _mockLogger;
|
||||||
|
private readonly Mock<IXpDataPathService> _mockDataPathService;
|
||||||
|
|
||||||
public PipelineEditorViewModelTests()
|
public PipelineEditorViewModelTests()
|
||||||
{
|
{
|
||||||
@@ -29,11 +31,19 @@ namespace XplorePlane.Tests.Pipeline
|
|||||||
_mockExecSvc = new Mock<IPipelineExecutionService>();
|
_mockExecSvc = new Mock<IPipelineExecutionService>();
|
||||||
_mockPersistSvc = new Mock<IPipelinePersistenceService>();
|
_mockPersistSvc = new Mock<IPipelinePersistenceService>();
|
||||||
_mockLogger = new Mock<ILoggerService>();
|
_mockLogger = new Mock<ILoggerService>();
|
||||||
|
_mockDataPathService = new Mock<IXpDataPathService>();
|
||||||
_mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(_mockLogger.Object);
|
_mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(_mockLogger.Object);
|
||||||
|
_mockDataPathService.SetupGet(s => s.ToolsPath).Returns(Path.GetTempPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
private PipelineEditorViewModel CreateVm() =>
|
private PipelineEditorViewModel CreateVm() =>
|
||||||
new PipelineEditorViewModel(_mockImageSvc.Object, _mockExecSvc.Object, _mockPersistSvc.Object, new EventAggregator(), _mockLogger.Object);
|
new PipelineEditorViewModel(
|
||||||
|
_mockImageSvc.Object,
|
||||||
|
_mockExecSvc.Object,
|
||||||
|
_mockPersistSvc.Object,
|
||||||
|
new EventAggregator(),
|
||||||
|
_mockLogger.Object,
|
||||||
|
_mockDataPathService.Object);
|
||||||
|
|
||||||
// ── 6.1 AddOperatorCommand ────────────────────────────────────
|
// ── 6.1 AddOperatorCommand ────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Moq;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services;
|
using XplorePlane.Services;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
using XplorePlane.Tests.Helpers;
|
using XplorePlane.Tests.Helpers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@@ -199,5 +201,21 @@ namespace XplorePlane.Tests.Pipeline
|
|||||||
|
|
||||||
Assert.Equal(2, result.Count);
|
Assert.Equal(2, result.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LoadAllAsync_UsesToolsPath_WhenConstructedWithDataPathService()
|
||||||
|
{
|
||||||
|
var mockImageSvc = TestHelpers.CreateMockImageService(new[] { "Blur" });
|
||||||
|
var mockDataPathSvc = new Mock<IXpDataPathService>();
|
||||||
|
mockDataPathSvc.SetupGet(s => s.ToolsPath).Returns(_tempDir);
|
||||||
|
|
||||||
|
var service = new PipelinePersistenceService(mockImageSvc.Object, mockDataPathSvc.Object);
|
||||||
|
await service.SaveAsync(BuildModel("P3", "Blur"), Path.Combine(_tempDir, "p3.xpm"));
|
||||||
|
|
||||||
|
var result = await service.LoadAllAsync(null);
|
||||||
|
|
||||||
|
Assert.Single(result);
|
||||||
|
Assert.Equal("P3", result[0].Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
|
|||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services;
|
using XplorePlane.Services;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
using XplorePlane.Tests.Helpers;
|
using XplorePlane.Tests.Helpers;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
@@ -31,8 +32,16 @@ namespace XplorePlane.Tests.Pipeline
|
|||||||
var mockExecSvc = new Mock<IPipelineExecutionService>();
|
var mockExecSvc = new Mock<IPipelineExecutionService>();
|
||||||
var mockPersistSvc = new Mock<IPipelinePersistenceService>();
|
var mockPersistSvc = new Mock<IPipelinePersistenceService>();
|
||||||
var mockLogger = new Mock<ILoggerService>();
|
var mockLogger = new Mock<ILoggerService>();
|
||||||
|
var mockDataPathService = new Mock<IXpDataPathService>();
|
||||||
mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(mockLogger.Object);
|
mockLogger.Setup(l => l.ForModule<PipelineEditorViewModel>()).Returns(mockLogger.Object);
|
||||||
return new PipelineEditorViewModel(mockImageSvc.Object, mockExecSvc.Object, mockPersistSvc.Object, new EventAggregator(), mockLogger.Object);
|
mockDataPathService.SetupGet(s => s.ToolsPath).Returns(Path.GetTempPath());
|
||||||
|
return new PipelineEditorViewModel(
|
||||||
|
mockImageSvc.Object,
|
||||||
|
mockExecSvc.Object,
|
||||||
|
mockPersistSvc.Object,
|
||||||
|
new EventAggregator(),
|
||||||
|
mockLogger.Object,
|
||||||
|
mockDataPathService.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using XP.Common.Database.Interfaces;
|
|||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services.InspectionResults;
|
using XplorePlane.Services.InspectionResults;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace XplorePlane.Tests.Services
|
namespace XplorePlane.Tests.Services
|
||||||
@@ -294,6 +295,33 @@ namespace XplorePlane.Tests.Services
|
|||||||
Assert.Equal(originalHash, snapshot.PipelineHash);
|
Assert.Equal(originalHash, snapshot.PipelineHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Constructor_WithDataPathService_WritesIntoDataInspectionResultsDirectory()
|
||||||
|
{
|
||||||
|
var mockDataPathService = new Mock<IXpDataPathService>();
|
||||||
|
var dataRoot = Path.Combine(_tempRoot, "xpdata", "Data");
|
||||||
|
mockDataPathService.SetupGet(s => s.DataPath).Returns(dataRoot);
|
||||||
|
|
||||||
|
var store = new InspectionResultStore(_dbContext, _mockLogger.Object, mockDataPathService.Object);
|
||||||
|
var run = new InspectionRunRecord
|
||||||
|
{
|
||||||
|
ProgramName = "Program-By-Service",
|
||||||
|
WorkpieceId = "Part-03",
|
||||||
|
SerialNumber = "SN-DATA"
|
||||||
|
};
|
||||||
|
|
||||||
|
await store.BeginRunAsync(run);
|
||||||
|
await store.CompleteRunAsync(run.RunId);
|
||||||
|
|
||||||
|
var manifestPath = Path.Combine(
|
||||||
|
dataRoot,
|
||||||
|
"InspectionResults",
|
||||||
|
run.ResultRootPath.Replace('/', Path.DirectorySeparatorChar),
|
||||||
|
"manifest.json");
|
||||||
|
|
||||||
|
Assert.True(File.Exists(manifestPath));
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_dbContext.Dispose();
|
_dbContext.Dispose();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Feature: cnc-run-execution
|
// Feature: cnc-run-execution
|
||||||
// Properties 1, 2, 12: CncEditorViewModel execution control
|
// Properties 1, 2, 12: CncEditorViewModel execution control
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@@ -16,6 +16,8 @@ using XP.Common.Logging.Interfaces;
|
|||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services.AppState;
|
using XplorePlane.Services.AppState;
|
||||||
using XplorePlane.Services.Cnc;
|
using XplorePlane.Services.Cnc;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
|
using XplorePlane.Services;
|
||||||
using XplorePlane.ViewModels.Cnc;
|
using XplorePlane.ViewModels.Cnc;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@@ -23,16 +25,20 @@ namespace XplorePlane.Tests.ViewModels
|
|||||||
{
|
{
|
||||||
public class CncEditorViewModelTests
|
public class CncEditorViewModelTests
|
||||||
{
|
{
|
||||||
// ── Helpers ──────────────────────────────────────────────────────────
|
// 鈹€鈹€ Helpers 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
||||||
|
|
||||||
private static CncEditorViewModel CreateVm(
|
private static CncEditorViewModel CreateVm(
|
||||||
Mock<ICncExecutionService> mockExecSvc = null,
|
Mock<ICncExecutionService> mockExecSvc = null,
|
||||||
CncProgram initialProgram = null)
|
CncProgram initialProgram = null,
|
||||||
|
Mock<IPipelinePersistenceService> mockPipelinePersistenceService = null)
|
||||||
{
|
{
|
||||||
var mockCncProgramSvc = new Mock<ICncProgramService>();
|
var mockCncProgramSvc = new Mock<ICncProgramService>();
|
||||||
var mockAppState = new Mock<IAppStateService>();
|
var mockAppState = new Mock<IAppStateService>();
|
||||||
var mockLogger = new Mock<ILoggerService>();
|
var mockLogger = new Mock<ILoggerService>();
|
||||||
|
var mockDataPathService = new Mock<IXpDataPathService>();
|
||||||
|
mockPipelinePersistenceService ??= new Mock<IPipelinePersistenceService>();
|
||||||
mockLogger.Setup(l => l.ForModule<CncEditorViewModel>()).Returns(mockLogger.Object);
|
mockLogger.Setup(l => l.ForModule<CncEditorViewModel>()).Returns(mockLogger.Object);
|
||||||
|
mockDataPathService.SetupGet(s => s.PlanPath).Returns(System.IO.Path.GetTempPath());
|
||||||
|
|
||||||
mockExecSvc ??= new Mock<ICncExecutionService>();
|
mockExecSvc ??= new Mock<ICncExecutionService>();
|
||||||
|
|
||||||
@@ -44,15 +50,52 @@ namespace XplorePlane.Tests.ViewModels
|
|||||||
DateTime.UtcNow, DateTime.UtcNow,
|
DateTime.UtcNow, DateTime.UtcNow,
|
||||||
new List<CncNode>
|
new List<CncNode>
|
||||||
{
|
{
|
||||||
new ReferencePointNode(Guid.NewGuid(), 0, "参考点_0", 0, 0, 0, 0, 0, 0, false, 0, 0)
|
new ReferencePointNode(Guid.NewGuid(), 0, "鍙傝€冪偣_0", 0, 0, 0, 0, 0, 0, false, 0, 0)
|
||||||
}.AsReadOnly()));
|
}.AsReadOnly()));
|
||||||
|
|
||||||
|
mockCncProgramSvc
|
||||||
|
.Setup(s => s.CreateNode(It.IsAny<CncNodeType>()))
|
||||||
|
.Returns((CncNodeType nodeType) => nodeType switch
|
||||||
|
{
|
||||||
|
CncNodeType.InspectionModule => new InspectionModuleNode(Guid.NewGuid(), 0, "检测模块_0", new PipelineModel()),
|
||||||
|
CncNodeType.ReferencePoint => new ReferencePointNode(Guid.NewGuid(), 0, "参考点_0", 0, 0, 0, 0, 0, 0, false, 0, 0),
|
||||||
|
_ => throw new InvalidOperationException($"Unsupported node type in test: {nodeType}")
|
||||||
|
});
|
||||||
|
|
||||||
|
mockCncProgramSvc
|
||||||
|
.Setup(s => s.InsertNode(It.IsAny<CncProgram>(), It.IsAny<int>(), It.IsAny<CncNode>()))
|
||||||
|
.Returns((CncProgram program, int afterIndex, CncNode node) =>
|
||||||
|
{
|
||||||
|
var nodes = program.Nodes.ToList();
|
||||||
|
var insertIndex = Math.Clamp(afterIndex + 1, 0, nodes.Count);
|
||||||
|
nodes.Insert(insertIndex, node with { Index = insertIndex });
|
||||||
|
|
||||||
|
for (var i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
nodes[i] = nodes[i] with { Index = i };
|
||||||
|
}
|
||||||
|
|
||||||
|
return program with { Nodes = nodes.AsReadOnly(), UpdatedAt = DateTime.UtcNow };
|
||||||
|
});
|
||||||
|
|
||||||
|
mockCncProgramSvc
|
||||||
|
.Setup(s => s.UpdateNode(It.IsAny<CncProgram>(), It.IsAny<int>(), It.IsAny<CncNode>()))
|
||||||
|
.Returns((CncProgram program, int index, CncNode updatedNode) =>
|
||||||
|
{
|
||||||
|
var nodes = program.Nodes.ToList();
|
||||||
|
nodes[index] = updatedNode with { Index = index };
|
||||||
|
return program with { Nodes = nodes.AsReadOnly(), UpdatedAt = DateTime.UtcNow };
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
var vm = new CncEditorViewModel(
|
var vm = new CncEditorViewModel(
|
||||||
mockCncProgramSvc.Object,
|
mockCncProgramSvc.Object,
|
||||||
mockAppState.Object,
|
mockAppState.Object,
|
||||||
new EventAggregator(),
|
new EventAggregator(),
|
||||||
mockLogger.Object,
|
mockLogger.Object,
|
||||||
mockExecSvc.Object);
|
mockExecSvc.Object,
|
||||||
|
mockDataPathService.Object,
|
||||||
|
mockPipelinePersistenceService.Object);
|
||||||
|
|
||||||
if (initialProgram != null)
|
if (initialProgram != null)
|
||||||
{
|
{
|
||||||
@@ -78,9 +121,9 @@ namespace XplorePlane.Tests.ViewModels
|
|||||||
return new CncProgram(Guid.NewGuid(), "TestProgram", DateTime.UtcNow, DateTime.UtcNow, nodes);
|
return new CncProgram(Guid.NewGuid(), "TestProgram", DateTime.UtcNow, DateTime.UtcNow, nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Property 1: 运行/停止按钮状态互斥 ────────────────────────────────
|
// 鈹€鈹€ Property 1: 杩愯/鍋滄鎸夐挳鐘舵€佷簰鏂?鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
||||||
|
|
||||||
// Feature: cnc-run-execution, Property 1: 运行/停止按钮状态互斥
|
// Feature: cnc-run-execution, Property 1: 杩愯/鍋滄鎸夐挳鐘舵€佷簰鏂?
|
||||||
// Validates: Requirements 1.1, 1.3, 1.4
|
// Validates: Requirements 1.1, 1.3, 1.4
|
||||||
[Property(MaxTest = 100)]
|
[Property(MaxTest = 100)]
|
||||||
public Property RunStop_Commands_AreMutuallyExclusive()
|
public Property RunStop_Commands_AreMutuallyExclusive()
|
||||||
@@ -125,9 +168,9 @@ namespace XplorePlane.Tests.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Property 2: 执行完成后状态重置 ───────────────────────────────────
|
// 鈹€鈹€ Property 2: 鎵ц瀹屾垚鍚庣姸鎬侀噸缃?鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
||||||
|
|
||||||
// Feature: cnc-run-execution, Property 2: 执行完成后状态重置
|
// Feature: cnc-run-execution, Property 2: 鎵ц瀹屾垚鍚庣姸鎬侀噸缃?
|
||||||
// Validates: Requirements 1.7, 6.5
|
// Validates: Requirements 1.7, 6.5
|
||||||
[Property(MaxTest = 100)]
|
[Property(MaxTest = 100)]
|
||||||
public Property AfterExecution_IsRunningFalse_AllNodesIdle()
|
public Property AfterExecution_IsRunningFalse_AllNodesIdle()
|
||||||
@@ -176,9 +219,9 @@ namespace XplorePlane.Tests.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Property 12: 执行中编辑命令全部禁用 ──────────────────────────────
|
// 鈹€鈹€ Property 12: 鎵ц涓紪杈戝懡浠ゅ叏閮ㄧ鐢?鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
|
||||||
|
|
||||||
// Feature: cnc-run-execution, Property 12: 执行中编辑命令全部禁用
|
// Feature: cnc-run-execution, Property 12: 鎵ц涓紪杈戝懡浠ゅ叏閮ㄧ鐢?
|
||||||
// Validates: Requirements 6.7
|
// Validates: Requirements 6.7
|
||||||
[Property(MaxTest = 100)]
|
[Property(MaxTest = 100)]
|
||||||
public Property WhileRunning_AllEditCommands_AreDisabled()
|
public Property WhileRunning_AllEditCommands_AreDisabled()
|
||||||
@@ -228,5 +271,50 @@ namespace XplorePlane.Tests.ViewModels
|
|||||||
return allDisabled;
|
return allDisabled;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InsertInspectionModuleFromPipelineFileAsync_LoadsPipelineAndInsertsNode()
|
||||||
|
{
|
||||||
|
var pipelineFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"{Guid.NewGuid():N}.xpm");
|
||||||
|
await System.IO.File.WriteAllTextAsync(pipelineFile, "{}");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var expectedPipeline = new PipelineModel
|
||||||
|
{
|
||||||
|
Name = "BuiltIn/ModuleA"
|
||||||
|
};
|
||||||
|
|
||||||
|
var mockPipelinePersistenceService = new Mock<IPipelinePersistenceService>();
|
||||||
|
mockPipelinePersistenceService
|
||||||
|
.Setup(s => s.LoadAsync(pipelineFile))
|
||||||
|
.ReturnsAsync(expectedPipeline);
|
||||||
|
|
||||||
|
var initialProgram = new CncProgram(
|
||||||
|
Guid.NewGuid(),
|
||||||
|
"TestProgram",
|
||||||
|
DateTime.UtcNow,
|
||||||
|
DateTime.UtcNow,
|
||||||
|
new List<CncNode>
|
||||||
|
{
|
||||||
|
new SavePositionNode(Guid.NewGuid(), 0, "保存位置_0", MotionState.Default)
|
||||||
|
}.AsReadOnly());
|
||||||
|
|
||||||
|
var vm = CreateVm(
|
||||||
|
initialProgram: initialProgram,
|
||||||
|
mockPipelinePersistenceService: mockPipelinePersistenceService);
|
||||||
|
|
||||||
|
await vm.InsertInspectionModuleFromPipelineFileAsync(pipelineFile);
|
||||||
|
|
||||||
|
var insertedNode = Assert.IsType<InspectionModuleNode>(vm.Nodes.Last().Model);
|
||||||
|
Assert.Same(expectedPipeline, insertedNode.Pipeline);
|
||||||
|
Assert.Equal("BuiltIn/ModuleA", insertedNode.Pipeline.Name);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (System.IO.File.Exists(pipelineFile))
|
||||||
|
System.IO.File.Delete(pipelineFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<appSettings>
|
<appSettings>
|
||||||
<!-- 语言配置 可选值: ZhCN, ZhTW, EnUS| Language Configuration -->
|
<!-- 语言配置 可选值: ZhCN, ZhTW, EnUS| Language Configuration -->
|
||||||
<add key="Language" value="ZhCN" />
|
<add key="Language" value="ZhCN" />
|
||||||
|
<add key="XpData:RootPath" value="D:\XPData" />
|
||||||
<add key="UserManual" value="D:\HMQProject\XplorePlane_CT\Code\XplorePlane\XP.App\bin\Debug\net8.0-windows7.0\UserManual.pdf" />
|
<add key="UserManual" value="D:\HMQProject\XplorePlane_CT\Code\XplorePlane\XP.App\bin\Debug\net8.0-windows7.0\UserManual.pdf" />
|
||||||
|
|
||||||
<!-- Serilog日志配置 -->
|
<!-- Serilog日志配置 -->
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ using XplorePlane.Services.MainViewport;
|
|||||||
using XplorePlane.Services.Matrix;
|
using XplorePlane.Services.Matrix;
|
||||||
using XplorePlane.Services.Measurement;
|
using XplorePlane.Services.Measurement;
|
||||||
using XplorePlane.Services.Recipe;
|
using XplorePlane.Services.Recipe;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
using XplorePlane.ViewModels.Cnc;
|
using XplorePlane.ViewModels.Cnc;
|
||||||
using XplorePlane.Views;
|
using XplorePlane.Views;
|
||||||
@@ -377,6 +378,7 @@ namespace XplorePlane
|
|||||||
|
|
||||||
// 注册全局状态服务(单例)
|
// 注册全局状态服务(单例)
|
||||||
containerRegistry.RegisterSingleton<IAppStateService, AppStateService>();
|
containerRegistry.RegisterSingleton<IAppStateService, AppStateService>();
|
||||||
|
containerRegistry.RegisterSingleton<IXpDataPathService, XpDataPathService>();
|
||||||
|
|
||||||
// 注册检测配方服务(单例)
|
// 注册检测配方服务(单例)
|
||||||
containerRegistry.RegisterSingleton<IRecipeService, RecipeService>();
|
containerRegistry.RegisterSingleton<IRecipeService, RecipeService>();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Threading.Tasks;
|
|||||||
using XP.Common.Database.Interfaces;
|
using XP.Common.Database.Interfaces;
|
||||||
using XP.Common.Logging.Interfaces;
|
using XP.Common.Logging.Interfaces;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
|
|
||||||
namespace XplorePlane.Services.InspectionResults
|
namespace XplorePlane.Services.InspectionResults
|
||||||
{
|
{
|
||||||
@@ -160,6 +161,14 @@ WHERE run_id = @run_id";
|
|||||||
"InspectionResults");
|
"InspectionResults");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InspectionResultStore(IDbContext db, ILoggerService logger, IXpDataPathService dataPathService)
|
||||||
|
: this(
|
||||||
|
db,
|
||||||
|
logger,
|
||||||
|
Path.Combine(dataPathService?.DataPath ?? throw new ArgumentNullException(nameof(dataPathService)), "InspectionResults"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public async Task BeginRunAsync(InspectionRunRecord runRecord, InspectionAssetWriteRequest runSourceAsset = null)
|
public async Task BeginRunAsync(InspectionRunRecord runRecord, InspectionAssetWriteRequest runSourceAsset = null)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(runRecord);
|
ArgumentNullException.ThrowIfNull(runRecord);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
|
|
||||||
namespace XplorePlane.Services
|
namespace XplorePlane.Services
|
||||||
{
|
{
|
||||||
@@ -29,6 +30,11 @@ namespace XplorePlane.Services
|
|||||||
"XplorePlane", "Pipelines");
|
"XplorePlane", "Pipelines");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PipelinePersistenceService(IImageProcessingService imageProcessingService, IXpDataPathService dataPathService)
|
||||||
|
: this(imageProcessingService, dataPathService?.ToolsPath)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public async Task SaveAsync(PipelineModel pipeline, string filePath)
|
public async Task SaveAsync(PipelineModel pipeline, string filePath)
|
||||||
{
|
{
|
||||||
if (pipeline == null) throw new ArgumentNullException(nameof(pipeline));
|
if (pipeline == null) throw new ArgumentNullException(nameof(pipeline));
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services.Storage
|
||||||
|
{
|
||||||
|
public interface IXpDataPathService
|
||||||
|
{
|
||||||
|
string DefaultRootPath { get; }
|
||||||
|
|
||||||
|
string RootPath { get; }
|
||||||
|
|
||||||
|
string PlanPath { get; }
|
||||||
|
|
||||||
|
string ToolsPath { get; }
|
||||||
|
|
||||||
|
string DataPath { get; }
|
||||||
|
|
||||||
|
string ReportPath { get; }
|
||||||
|
|
||||||
|
string LegacyInspectionDataPath { get; }
|
||||||
|
|
||||||
|
void SaveRootPath(string rootPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
using System;
|
||||||
|
using System.Configuration;
|
||||||
|
using System.IO;
|
||||||
|
using XP.Common.Logging.Interfaces;
|
||||||
|
|
||||||
|
namespace XplorePlane.Services.Storage
|
||||||
|
{
|
||||||
|
public class XpDataPathService : IXpDataPathService
|
||||||
|
{
|
||||||
|
internal const string RootPathSettingKey = "XpData:RootPath";
|
||||||
|
private const string DefaultRoot = @"D:\XPData";
|
||||||
|
|
||||||
|
private readonly ILoggerService _logger;
|
||||||
|
private readonly object _syncRoot = new();
|
||||||
|
private string _rootPath;
|
||||||
|
|
||||||
|
public XpDataPathService(ILoggerService logger)
|
||||||
|
{
|
||||||
|
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<XpDataPathService>();
|
||||||
|
_rootPath = LoadConfiguredRootPath();
|
||||||
|
EnsureManagedDirectories(_rootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DefaultRootPath => DefaultRoot;
|
||||||
|
|
||||||
|
public string RootPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
return _rootPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PlanPath => EnsureSubdirectory("Plan");
|
||||||
|
|
||||||
|
public string ToolsPath => EnsureSubdirectory("Tools");
|
||||||
|
|
||||||
|
public string DataPath => EnsureSubdirectory("Data");
|
||||||
|
|
||||||
|
public string ReportPath => EnsureSubdirectory("Report");
|
||||||
|
|
||||||
|
public string LegacyInspectionDataPath => Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
|
"XplorePlane",
|
||||||
|
"InspectionResults");
|
||||||
|
|
||||||
|
public void SaveRootPath(string rootPath)
|
||||||
|
{
|
||||||
|
var normalizedRootPath = NormalizeRootPath(rootPath);
|
||||||
|
EnsureManagedDirectories(normalizedRootPath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||||
|
var appSettings = config.AppSettings.Settings;
|
||||||
|
|
||||||
|
if (appSettings[RootPathSettingKey] == null)
|
||||||
|
{
|
||||||
|
appSettings.Add(RootPathSettingKey, normalizedRootPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
appSettings[RootPathSettingKey].Value = normalizedRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.Save(ConfigurationSaveMode.Modified);
|
||||||
|
ConfigurationManager.RefreshSection("appSettings");
|
||||||
|
}
|
||||||
|
catch (ConfigurationErrorsException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "保存 XP 数据根目录失败:配置文件写入异常");
|
||||||
|
throw new InvalidOperationException($"保存数据根目录失败:{ex.Message}", ex);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "保存 XP 数据根目录失败:无权写入配置文件");
|
||||||
|
throw new InvalidOperationException("保存数据根目录失败:没有配置文件写入权限。", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
_rootPath = normalizedRootPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string EnsureSubdirectory(string directoryName)
|
||||||
|
{
|
||||||
|
string rootPath;
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
rootPath = _rootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureManagedDirectories(rootPath);
|
||||||
|
return Path.Combine(rootPath, directoryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string LoadConfiguredRootPath()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return NormalizeRootPath(ConfigurationManager.AppSettings[RootPathSettingKey]);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Warn("读取 XP 数据根目录失败,回退默认目录:{Message}", ex.Message);
|
||||||
|
return NormalizeRootPath(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeRootPath(string rootPath)
|
||||||
|
{
|
||||||
|
var candidate = string.IsNullOrWhiteSpace(rootPath) ? DefaultRoot : rootPath.Trim();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var normalized = Path.GetFullPath(candidate)
|
||||||
|
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||||
|
|
||||||
|
if (!Path.IsPathRooted(normalized))
|
||||||
|
{
|
||||||
|
return DefaultRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return DefaultRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EnsureManagedDirectories(string rootPath)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(rootPath);
|
||||||
|
Directory.CreateDirectory(Path.Combine(rootPath, "Plan"));
|
||||||
|
Directory.CreateDirectory(Path.Combine(rootPath, "Tools"));
|
||||||
|
Directory.CreateDirectory(Path.Combine(rootPath, "Data"));
|
||||||
|
Directory.CreateDirectory(Path.Combine(rootPath, "Report"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,8 @@ using XplorePlane.Events;
|
|||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services.AppState;
|
using XplorePlane.Services.AppState;
|
||||||
using XplorePlane.Services.Cnc;
|
using XplorePlane.Services.Cnc;
|
||||||
|
using XplorePlane.Services;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
|
|
||||||
namespace XplorePlane.ViewModels.Cnc
|
namespace XplorePlane.ViewModels.Cnc
|
||||||
{
|
{
|
||||||
@@ -28,6 +30,8 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
private readonly ICncExecutionService _cncExecutionService;
|
private readonly ICncExecutionService _cncExecutionService;
|
||||||
|
private readonly IXpDataPathService _dataPathService;
|
||||||
|
private readonly IPipelinePersistenceService _pipelinePersistenceService;
|
||||||
|
|
||||||
private CncProgram _currentProgram;
|
private CncProgram _currentProgram;
|
||||||
private ObservableCollection<CncNodeViewModel> _nodes;
|
private ObservableCollection<CncNodeViewModel> _nodes;
|
||||||
@@ -51,13 +55,17 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
IAppStateService appStateService,
|
IAppStateService appStateService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
ILoggerService logger,
|
ILoggerService logger,
|
||||||
ICncExecutionService cncExecutionService)
|
ICncExecutionService cncExecutionService,
|
||||||
|
IXpDataPathService dataPathService,
|
||||||
|
IPipelinePersistenceService pipelinePersistenceService)
|
||||||
{
|
{
|
||||||
_cncProgramService = cncProgramService ?? throw new ArgumentNullException(nameof(cncProgramService));
|
_cncProgramService = cncProgramService ?? throw new ArgumentNullException(nameof(cncProgramService));
|
||||||
ArgumentNullException.ThrowIfNull(appStateService);
|
ArgumentNullException.ThrowIfNull(appStateService);
|
||||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||||
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<CncEditorViewModel>();
|
_logger = (logger ?? throw new ArgumentNullException(nameof(logger))).ForModule<CncEditorViewModel>();
|
||||||
_cncExecutionService = cncExecutionService ?? throw new ArgumentNullException(nameof(cncExecutionService));
|
_cncExecutionService = cncExecutionService ?? throw new ArgumentNullException(nameof(cncExecutionService));
|
||||||
|
_dataPathService = dataPathService ?? throw new ArgumentNullException(nameof(dataPathService));
|
||||||
|
_pipelinePersistenceService = pipelinePersistenceService ?? throw new ArgumentNullException(nameof(pipelinePersistenceService));
|
||||||
|
|
||||||
_nodes = new ObservableCollection<CncNodeViewModel>();
|
_nodes = new ObservableCollection<CncNodeViewModel>();
|
||||||
_treeNodes = new ObservableCollection<CncNodeViewModel>();
|
_treeNodes = new ObservableCollection<CncNodeViewModel>();
|
||||||
@@ -331,7 +339,8 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
Title = "保存 CNC 程序",
|
Title = "保存 CNC 程序",
|
||||||
Filter = "CNC 程序文件 (*.xp)|*.xp|所有文件 (*.*)|*.*",
|
Filter = "CNC 程序文件 (*.xp)|*.xp|所有文件 (*.*)|*.*",
|
||||||
DefaultExt = ".xp",
|
DefaultExt = ".xp",
|
||||||
FileName = _currentProgram.Name
|
FileName = _currentProgram.Name,
|
||||||
|
InitialDirectory = GetPlanDirectory()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dlg.ShowDialog() != true)
|
if (dlg.ShowDialog() != true)
|
||||||
@@ -355,7 +364,8 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
{
|
{
|
||||||
Title = "加载 CNC 程序",
|
Title = "加载 CNC 程序",
|
||||||
Filter = "CNC 程序文件 (*.xp)|*.xp|所有文件 (*.*)|*.*",
|
Filter = "CNC 程序文件 (*.xp)|*.xp|所有文件 (*.*)|*.*",
|
||||||
DefaultExt = ".xp"
|
DefaultExt = ".xp",
|
||||||
|
InitialDirectory = GetPlanDirectory()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dlg.ShowDialog() != true)
|
if (dlg.ShowDialog() != true)
|
||||||
@@ -438,6 +448,64 @@ namespace XplorePlane.ViewModels.Cnc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task InsertInspectionModuleFromPipelineFileAsync(string filePath)
|
||||||
|
{
|
||||||
|
if (IsRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(filePath))
|
||||||
|
throw new ArgumentException("检测模块文件路径不能为空。", nameof(filePath));
|
||||||
|
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
throw new FileNotFoundException("检测模块文件不存在。", filePath);
|
||||||
|
|
||||||
|
if (_currentProgram == null)
|
||||||
|
{
|
||||||
|
ExecuteNewProgram();
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipeline = await _pipelinePersistenceService.LoadAsync(filePath);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var node = _cncProgramService.CreateNode(CncNodeType.InspectionModule);
|
||||||
|
if (node is not InspectionModuleNode inspectionModuleNode)
|
||||||
|
throw new InvalidOperationException("无法创建检测模块节点。");
|
||||||
|
|
||||||
|
var pipelineName = string.IsNullOrWhiteSpace(pipeline.Name)
|
||||||
|
? Path.GetFileNameWithoutExtension(filePath)
|
||||||
|
: pipeline.Name;
|
||||||
|
pipeline.Name = pipelineName;
|
||||||
|
|
||||||
|
var configuredNode = inspectionModuleNode with
|
||||||
|
{
|
||||||
|
Pipeline = pipeline,
|
||||||
|
Name = pipelineName
|
||||||
|
};
|
||||||
|
|
||||||
|
int afterIndex = ResolveInsertAfterIndex(CncNodeType.InspectionModule);
|
||||||
|
_currentProgram = _cncProgramService.InsertNode(_currentProgram, afterIndex, configuredNode);
|
||||||
|
_preferredSelectedNodeId = configuredNode.Id;
|
||||||
|
ClearPendingInsertAnchor();
|
||||||
|
|
||||||
|
OnProgramEdited();
|
||||||
|
StatusMessage = $"已插入检测模块:{pipelineName}";
|
||||||
|
_logger.Info("Inserted built-in inspection module from file: {FilePath}", filePath);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_logger.Warn("Built-in inspection module insertion blocked: {Message}", ex.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetPlanDirectory()
|
||||||
|
{
|
||||||
|
var directory = _dataPathService.PlanPath;
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
return directory;
|
||||||
|
}
|
||||||
|
|
||||||
private static string Esc(string value)
|
private static string Esc(string value)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(value)) return string.Empty;
|
if (string.IsNullOrEmpty(value)) return string.Empty;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using XP.Common.Logging.Interfaces;
|
|||||||
using XplorePlane.Events;
|
using XplorePlane.Events;
|
||||||
using XplorePlane.Models;
|
using XplorePlane.Models;
|
||||||
using XplorePlane.Services;
|
using XplorePlane.Services;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
|
|
||||||
namespace XplorePlane.ViewModels
|
namespace XplorePlane.ViewModels
|
||||||
{
|
{
|
||||||
@@ -28,6 +29,7 @@ namespace XplorePlane.ViewModels
|
|||||||
private readonly IPipelinePersistenceService _persistenceService;
|
private readonly IPipelinePersistenceService _persistenceService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly ILoggerService _logger;
|
private readonly ILoggerService _logger;
|
||||||
|
private readonly IXpDataPathService _dataPathService;
|
||||||
|
|
||||||
private PipelineNodeViewModel _selectedNode;
|
private PipelineNodeViewModel _selectedNode;
|
||||||
private BitmapSource _sourceImage;
|
private BitmapSource _sourceImage;
|
||||||
@@ -49,13 +51,15 @@ namespace XplorePlane.ViewModels
|
|||||||
IPipelineExecutionService executionService,
|
IPipelineExecutionService executionService,
|
||||||
IPipelinePersistenceService persistenceService,
|
IPipelinePersistenceService persistenceService,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
ILoggerService logger)
|
ILoggerService logger,
|
||||||
|
IXpDataPathService dataPathService)
|
||||||
{
|
{
|
||||||
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
|
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
|
||||||
_executionService = executionService ?? throw new ArgumentNullException(nameof(executionService));
|
_executionService = executionService ?? throw new ArgumentNullException(nameof(executionService));
|
||||||
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
|
_persistenceService = persistenceService ?? throw new ArgumentNullException(nameof(persistenceService));
|
||||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||||
_logger = logger?.ForModule<PipelineEditorViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger?.ForModule<PipelineEditorViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
_dataPathService = dataPathService ?? throw new ArgumentNullException(nameof(dataPathService));
|
||||||
|
|
||||||
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
|
PipelineNodes = new ObservableCollection<PipelineNodeViewModel>();
|
||||||
AvailableDevices = new ObservableCollection<string>();
|
AvailableDevices = new ObservableCollection<string>();
|
||||||
@@ -808,11 +812,9 @@ namespace XplorePlane.ViewModels
|
|||||||
return $"已执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)";
|
return $"已执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetPipelineDirectory()
|
private string GetPipelineDirectory()
|
||||||
{
|
{
|
||||||
var dir = Path.Combine(
|
var dir = _dataPathService.ToolsPath;
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
|
||||||
"XplorePlane", "Pipelines");
|
|
||||||
Directory.CreateDirectory(dir);
|
Directory.CreateDirectory(dir);
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Prism.Commands;
|
using Prism.Commands;
|
||||||
using Prism.Events;
|
using Prism.Events;
|
||||||
using Prism.Ioc;
|
using Prism.Ioc;
|
||||||
@@ -7,10 +7,13 @@ using System;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Configuration;
|
using System.Configuration;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using XplorePlane.Events;
|
using XplorePlane.Events;
|
||||||
using XplorePlane.Services.MainViewport;
|
using XplorePlane.Services.MainViewport;
|
||||||
|
using XplorePlane.Services.Storage;
|
||||||
using XplorePlane.ViewModels.Cnc;
|
using XplorePlane.ViewModels.Cnc;
|
||||||
using XplorePlane.Views;
|
using XplorePlane.Views;
|
||||||
using XplorePlane.Views.Cnc;
|
using XplorePlane.Views.Cnc;
|
||||||
@@ -29,6 +32,7 @@ namespace XplorePlane.ViewModels
|
|||||||
private readonly IContainerProvider _containerProvider;
|
private readonly IContainerProvider _containerProvider;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly IMainViewportService _mainViewportService;
|
private readonly IMainViewportService _mainViewportService;
|
||||||
|
private readonly IXpDataPathService _xpDataPathService;
|
||||||
private readonly CncEditorViewModel _cncEditorViewModel;
|
private readonly CncEditorViewModel _cncEditorViewModel;
|
||||||
private readonly CncPageView _cncPageView;
|
private readonly CncPageView _cncPageView;
|
||||||
|
|
||||||
@@ -65,6 +69,10 @@ namespace XplorePlane.ViewModels
|
|||||||
public DelegateCommand OpenLibraryVersionsCommand { get; }
|
public DelegateCommand OpenLibraryVersionsCommand { get; }
|
||||||
public DelegateCommand OpenUserManualCommand { get; }
|
public DelegateCommand OpenUserManualCommand { get; }
|
||||||
public DelegateCommand OpenCameraSettingsCommand { get; }
|
public DelegateCommand OpenCameraSettingsCommand { get; }
|
||||||
|
public DelegateCommand OpenSettingsCommand { get; }
|
||||||
|
public DelegateCommand BrowseDataRootPathCommand { get; }
|
||||||
|
public DelegateCommand ResetDataRootPathCommand { get; }
|
||||||
|
public DelegateCommand SaveDataRootPathCommand { get; }
|
||||||
public DelegateCommand NewCncProgramCommand { get; }
|
public DelegateCommand NewCncProgramCommand { get; }
|
||||||
public DelegateCommand SaveCncProgramCommand { get; }
|
public DelegateCommand SaveCncProgramCommand { get; }
|
||||||
public DelegateCommand LoadCncProgramCommand { get; }
|
public DelegateCommand LoadCncProgramCommand { get; }
|
||||||
@@ -73,6 +81,7 @@ namespace XplorePlane.ViewModels
|
|||||||
public DelegateCommand InsertCompleteProgramCommand { get; }
|
public DelegateCommand InsertCompleteProgramCommand { get; }
|
||||||
public DelegateCommand InsertInspectionMarkerCommand { get; }
|
public DelegateCommand InsertInspectionMarkerCommand { get; }
|
||||||
public DelegateCommand InsertInspectionModuleCommand { get; }
|
public DelegateCommand InsertInspectionModuleCommand { get; }
|
||||||
|
public DelegateCommand InsertBuiltInInspectionModuleCommand { get; }
|
||||||
public DelegateCommand InsertSaveNodeCommand { get; }
|
public DelegateCommand InsertSaveNodeCommand { get; }
|
||||||
public DelegateCommand InsertPauseDialogCommand { get; }
|
public DelegateCommand InsertPauseDialogCommand { get; }
|
||||||
public DelegateCommand InsertWaitDelayCommand { get; }
|
public DelegateCommand InsertWaitDelayCommand { get; }
|
||||||
@@ -118,6 +127,34 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
public bool IsUsingLiveDetectorSource => _mainViewportService.CurrentSourceMode == MainViewportSourceMode.LiveDetector;
|
public bool IsUsingLiveDetectorSource => _mainViewportService.CurrentSourceMode == MainViewportSourceMode.LiveDetector;
|
||||||
|
|
||||||
|
public string DataRootPath
|
||||||
|
{
|
||||||
|
get => _dataRootPath;
|
||||||
|
set => SetProperty(ref _dataRootPath, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PlanRootPath => _xpDataPathService.PlanPath;
|
||||||
|
|
||||||
|
public string ToolsRootPath => _xpDataPathService.ToolsPath;
|
||||||
|
|
||||||
|
public string ResultsRootPath => _xpDataPathService.DataPath;
|
||||||
|
|
||||||
|
public string ReportRootPath => _xpDataPathService.ReportPath;
|
||||||
|
|
||||||
|
public ObservableCollection<BuiltInInspectionModuleItem> BuiltInInspectionModules { get; } = new();
|
||||||
|
|
||||||
|
public BuiltInInspectionModuleItem SelectedBuiltInInspectionModule
|
||||||
|
{
|
||||||
|
get => _selectedBuiltInInspectionModule;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedBuiltInInspectionModule, value))
|
||||||
|
{
|
||||||
|
InsertBuiltInInspectionModuleCommand?.RaiseCanExecuteChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>右侧图像区域内容 | Right-side image panel content</summary>
|
/// <summary>右侧图像区域内容 | Right-side image panel content</summary>
|
||||||
public object ImagePanelContent
|
public object ImagePanelContent
|
||||||
{
|
{
|
||||||
@@ -140,11 +177,11 @@ namespace XplorePlane.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 窗口引用(单例窗口防止重复打开)
|
// 窗口引用(单例窗口防止重复打开)
|
||||||
|
|
||||||
private Window _motionDebugWindow;
|
private Window _motionDebugWindow;
|
||||||
private Window _detectorConfigWindow;
|
private Window _detectorConfigWindow;
|
||||||
private Window _plcAddrConfigWindow;
|
private Window _plcAddrConfigWindow;
|
||||||
private Window _realTimeLogViewerWindow;
|
private Window _realTimeLogViewerWindow;
|
||||||
|
private Window _settingsWindow;
|
||||||
private Window _toolboxWindow;
|
private Window _toolboxWindow;
|
||||||
private Window _raySourceConfigWindow;
|
private Window _raySourceConfigWindow;
|
||||||
private object _imagePanelContent;
|
private object _imagePanelContent;
|
||||||
@@ -153,16 +190,21 @@ namespace XplorePlane.ViewModels
|
|||||||
private bool _isCncEditorMode;
|
private bool _isCncEditorMode;
|
||||||
private string _licenseInfo = "当前时间";
|
private string _licenseInfo = "当前时间";
|
||||||
|
|
||||||
|
private string _dataRootPath = string.Empty;
|
||||||
|
private BuiltInInspectionModuleItem _selectedBuiltInInspectionModule;
|
||||||
|
|
||||||
public MainViewModel(
|
public MainViewModel(
|
||||||
ILoggerService logger,
|
ILoggerService logger,
|
||||||
IContainerProvider containerProvider,
|
IContainerProvider containerProvider,
|
||||||
IEventAggregator eventAggregator,
|
IEventAggregator eventAggregator,
|
||||||
IMainViewportService mainViewportService)
|
IMainViewportService mainViewportService,
|
||||||
|
IXpDataPathService xpDataPathService)
|
||||||
{
|
{
|
||||||
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
_logger = logger?.ForModule<MainViewModel>() ?? throw new ArgumentNullException(nameof(logger));
|
||||||
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
|
_containerProvider = containerProvider ?? throw new ArgumentNullException(nameof(containerProvider));
|
||||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||||
_mainViewportService = mainViewportService ?? throw new ArgumentNullException(nameof(mainViewportService));
|
_mainViewportService = mainViewportService ?? throw new ArgumentNullException(nameof(mainViewportService));
|
||||||
|
_xpDataPathService = xpDataPathService ?? throw new ArgumentNullException(nameof(xpDataPathService));
|
||||||
_cncEditorViewModel = _containerProvider.Resolve<CncEditorViewModel>();
|
_cncEditorViewModel = _containerProvider.Resolve<CncEditorViewModel>();
|
||||||
_cncPageView = new CncPageView { DataContext = _cncEditorViewModel };
|
_cncPageView = new CncPageView { DataContext = _cncEditorViewModel };
|
||||||
|
|
||||||
@@ -183,8 +225,8 @@ namespace XplorePlane.ViewModels
|
|||||||
var node = _cncEditorViewModel.SelectedNode;
|
var node = _cncEditorViewModel.SelectedNode;
|
||||||
if (node?.ResultImage != null)
|
if (node?.ResultImage != null)
|
||||||
{
|
{
|
||||||
_logger.Info("[图像链路] 切换到节点 [{Name}],显示缓存结果图像", node.Name);
|
_logger.Info("[Image] Switched to node [{Name}], showing cached result image.", node.Name);
|
||||||
_mainViewportService.SetManualImage(node.ResultImage, $"CNC节点:{node.Name}");
|
_mainViewportService.SetManualImage(node.ResultImage, $"CNC node: {node.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -207,11 +249,15 @@ namespace XplorePlane.ViewModels
|
|||||||
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
|
||||||
|
|
||||||
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
|
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
|
||||||
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编排"));
|
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编辑"));
|
||||||
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
|
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
|
||||||
OpenLibraryVersionsCommand = new DelegateCommand(() => ShowWindow(new Views.LibraryVersionsWindow(), "关于"));
|
OpenLibraryVersionsCommand = new DelegateCommand(() => ShowWindow(new Views.LibraryVersionsWindow(), "关于"));
|
||||||
OpenUserManualCommand = new DelegateCommand(ExecuteOpenUserManual);
|
OpenUserManualCommand = new DelegateCommand(ExecuteOpenUserManual);
|
||||||
OpenCameraSettingsCommand = new DelegateCommand(ExecuteOpenCameraSettings);
|
OpenCameraSettingsCommand = new DelegateCommand(ExecuteOpenCameraSettings);
|
||||||
|
OpenSettingsCommand = new DelegateCommand(ExecuteOpenSettings);
|
||||||
|
BrowseDataRootPathCommand = new DelegateCommand(ExecuteBrowseDataRootPath);
|
||||||
|
ResetDataRootPathCommand = new DelegateCommand(ExecuteResetDataRootPath);
|
||||||
|
SaveDataRootPathCommand = new DelegateCommand(ExecuteSaveDataRootPath);
|
||||||
NewCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.NewProgramCommand.Execute()));
|
NewCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.NewProgramCommand.Execute()));
|
||||||
SaveCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.SaveProgramCommand.Execute()));
|
SaveCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.SaveProgramCommand.Execute()));
|
||||||
LoadCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.LoadProgramCommand.Execute()));
|
LoadCncProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.LoadProgramCommand.Execute()));
|
||||||
@@ -220,6 +266,9 @@ namespace XplorePlane.ViewModels
|
|||||||
InsertCompleteProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertCompleteProgramCommand.Execute()));
|
InsertCompleteProgramCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertCompleteProgramCommand.Execute()));
|
||||||
InsertInspectionMarkerCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionMarkerCommand.Execute()));
|
InsertInspectionMarkerCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionMarkerCommand.Execute()));
|
||||||
InsertInspectionModuleCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionModuleCommand.Execute()));
|
InsertInspectionModuleCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertInspectionModuleCommand.Execute()));
|
||||||
|
InsertBuiltInInspectionModuleCommand = new DelegateCommand(
|
||||||
|
async () => await ExecuteInsertBuiltInInspectionModuleAsync(),
|
||||||
|
CanExecuteInsertBuiltInInspectionModule);
|
||||||
InsertSaveNodeCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSaveNodeCommand.Execute()));
|
InsertSaveNodeCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertSaveNodeCommand.Execute()));
|
||||||
InsertPauseDialogCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertPauseDialogCommand.Execute()));
|
InsertPauseDialogCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertPauseDialogCommand.Execute()));
|
||||||
InsertWaitDelayCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertWaitDelayCommand.Execute()));
|
InsertWaitDelayCommand = new DelegateCommand(() => ExecuteCncEditorAction(vm => vm.InsertWaitDelayCommand.Execute()));
|
||||||
@@ -238,7 +287,6 @@ namespace XplorePlane.ViewModels
|
|||||||
BubbleMeasureCommand = new DelegateCommand(ExecuteBubbleMeasure);
|
BubbleMeasureCommand = new DelegateCommand(ExecuteBubbleMeasure);
|
||||||
|
|
||||||
// 辅助线命令
|
// 辅助线命令
|
||||||
|
|
||||||
ToggleCrosshairCommand = new DelegateCommand(() =>
|
ToggleCrosshairCommand = new DelegateCommand(() =>
|
||||||
_eventAggregator.GetEvent<ToggleCrosshairEvent>().Publish());
|
_eventAggregator.GetEvent<ToggleCrosshairEvent>().Publish());
|
||||||
|
|
||||||
@@ -256,6 +304,8 @@ namespace XplorePlane.ViewModels
|
|||||||
ImagePanelContent = new PipelineEditorView();
|
ImagePanelContent = new PipelineEditorView();
|
||||||
ViewportPanelWidth = new GridLength(1, GridUnitType.Star);
|
ViewportPanelWidth = new GridLength(1, GridUnitType.Star);
|
||||||
ImagePanelWidth = new GridLength(320);
|
ImagePanelWidth = new GridLength(320);
|
||||||
|
DataRootPath = _xpDataPathService.RootPath;
|
||||||
|
LoadBuiltInInspectionModules();
|
||||||
|
|
||||||
_logger.Info("MainViewModel 已初始化");
|
_logger.Info("MainViewModel 已初始化");
|
||||||
}
|
}
|
||||||
@@ -289,7 +339,7 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private void ExecuteOpenToolbox()
|
private void ExecuteOpenToolbox()
|
||||||
{
|
{
|
||||||
ShowOrActivate(_toolboxWindow, w => _toolboxWindow = w, () => new Views.OperatorToolboxWindow(), "算子工具箱");
|
ShowOrActivate(_toolboxWindow, w => _toolboxWindow = w, () => new Views.OperatorToolboxWindow(), "Operator Toolbox");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteOpenCncEditor()
|
private void ExecuteOpenCncEditor()
|
||||||
@@ -330,15 +380,15 @@ namespace XplorePlane.ViewModels
|
|||||||
var manualPath = ConfigurationManager.AppSettings["UserManual"];
|
var manualPath = ConfigurationManager.AppSettings["UserManual"];
|
||||||
if (string.IsNullOrEmpty(manualPath))
|
if (string.IsNullOrEmpty(manualPath))
|
||||||
{
|
{
|
||||||
_logger.Warn("未配置用户手册路径");
|
_logger.Warn("User manual path is not configured.");
|
||||||
MessageBox.Show("未配置用户手册路径,请检查 App.config 中的 UserManual 配置项。",
|
MessageBox.Show("User manual path is not configured. Please check the UserManual setting in App.config.",
|
||||||
"提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
"提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File.Exists(manualPath))
|
if (!File.Exists(manualPath))
|
||||||
{
|
{
|
||||||
_logger.Warn("用户手册文件不存在:{Path}", manualPath);
|
_logger.Warn("User manual file not found: {Path}", manualPath);
|
||||||
MessageBox.Show($"用户手册文件不存在:\n{manualPath}",
|
MessageBox.Show($"用户手册文件不存在:\n{manualPath}",
|
||||||
"提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
"提示", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
return;
|
return;
|
||||||
@@ -351,8 +401,8 @@ namespace XplorePlane.ViewModels
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "打开用户手册失败");
|
_logger.Error(ex, "Failed to open user manual.");
|
||||||
MessageBox.Show($"打开用户手册失败:{ex.Message}",
|
MessageBox.Show($"Failed to open user manual: {ex.Message}",
|
||||||
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,13 +423,146 @@ namespace XplorePlane.ViewModels
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "打开相机设置失败");
|
_logger.Error(ex, "Failed to open camera settings.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExecuteOpenSettings()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ShowOrActivate(_settingsWindow, w => _settingsWindow = w,
|
||||||
|
() => new Views.SettingsWindow(this), "Settings");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Failed to open settings window");
|
||||||
|
MessageBox.Show($"Failed to open settings window: {ex.Message}",
|
||||||
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteBrowseDataRootPath()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dialog = new OpenFolderDialog
|
||||||
|
{
|
||||||
|
Title = "选择 XP 数据根目录",
|
||||||
|
InitialDirectory = Directory.Exists(DataRootPath) ? DataRootPath : _xpDataPathService.RootPath
|
||||||
|
};
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() == true)
|
||||||
|
{
|
||||||
|
DataRootPath = dialog.FolderName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Failed to browse XP data root.");
|
||||||
|
MessageBox.Show($"Failed to browse data root: {ex.Message}",
|
||||||
|
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteResetDataRootPath()
|
||||||
|
{
|
||||||
|
DataRootPath = _xpDataPathService.DefaultRootPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteSaveDataRootPath()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_xpDataPathService.SaveRootPath(DataRootPath);
|
||||||
|
DataRootPath = _xpDataPathService.RootPath;
|
||||||
|
RaisePropertyChanged(nameof(PlanRootPath));
|
||||||
|
RaisePropertyChanged(nameof(ToolsRootPath));
|
||||||
|
RaisePropertyChanged(nameof(ResultsRootPath));
|
||||||
|
RaisePropertyChanged(nameof(ReportRootPath));
|
||||||
|
LoadBuiltInInspectionModules();
|
||||||
|
|
||||||
|
MessageBox.Show("XP data root saved. New save/load dialogs will use the new path immediately.",
|
||||||
|
"提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Failed to save XP data root.");
|
||||||
|
MessageBox.Show($"Failed to save data root: {ex.Message}",
|
||||||
|
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanExecuteInsertBuiltInInspectionModule()
|
||||||
|
{
|
||||||
|
return SelectedBuiltInInspectionModule != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ExecuteInsertBuiltInInspectionModuleAsync()
|
||||||
|
{
|
||||||
|
var module = SelectedBuiltInInspectionModule;
|
||||||
|
if (module == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ShowCncEditor();
|
||||||
|
await _cncEditorViewModel.InsertInspectionModuleFromPipelineFileAsync(module.FilePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Failed to insert built-in inspection module: {FilePath}", module.FilePath);
|
||||||
|
MessageBox.Show($"Failed to insert built-in inspection module: {ex.Message}",
|
||||||
|
"Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadBuiltInInspectionModules()
|
||||||
|
{
|
||||||
|
BuiltInInspectionModules.Clear();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var toolsPath = _xpDataPathService.ToolsPath;
|
||||||
|
if (!Directory.Exists(toolsPath))
|
||||||
|
{
|
||||||
|
SelectedBuiltInInspectionModule = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var files = Directory
|
||||||
|
.EnumerateFiles(toolsPath, "*.xpm", SearchOption.AllDirectories)
|
||||||
|
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Select(path => new BuiltInInspectionModuleItem(
|
||||||
|
GetBuiltInModuleDisplayName(toolsPath, path),
|
||||||
|
path))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
BuiltInInspectionModules.Add(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectedBuiltInInspectionModule = BuiltInInspectionModules.FirstOrDefault();
|
||||||
|
_logger.Info("Loaded {Count} built-in inspection modules from {ToolsPath}", BuiltInInspectionModules.Count, toolsPath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
SelectedBuiltInInspectionModule = null;
|
||||||
|
_logger.Error(ex, "Failed to load built-in inspection modules.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetBuiltInModuleDisplayName(string toolsPath, string filePath)
|
||||||
|
{
|
||||||
|
var relativePath = Path.GetRelativePath(toolsPath, filePath);
|
||||||
|
var withoutExtension = Path.ChangeExtension(relativePath, null) ?? relativePath;
|
||||||
|
return withoutExtension.Replace(Path.DirectorySeparatorChar, '/');
|
||||||
|
}
|
||||||
|
|
||||||
private void ExecuteAxisReset()
|
private void ExecuteAxisReset()
|
||||||
{
|
{
|
||||||
var result = MessageBox.Show("确认执行轴复位操作?", "轴复位",
|
var result = MessageBox.Show("Confirm axis reset?", "Axis Reset",
|
||||||
MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
||||||
if (result != MessageBoxResult.OK)
|
if (result != MessageBoxResult.OK)
|
||||||
return;
|
return;
|
||||||
@@ -396,7 +579,7 @@ namespace XplorePlane.ViewModels
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "轴复位异常");
|
_logger.Error(ex, "Axis reset failed.");
|
||||||
MessageBox.Show($"轴复位异常:{ex.Message}", "错误",
|
MessageBox.Show($"轴复位异常:{ex.Message}", "错误",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
@@ -407,12 +590,12 @@ namespace XplorePlane.ViewModels
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ShowOrActivate(_detectorConfigWindow, w => _detectorConfigWindow = w,
|
ShowOrActivate(_detectorConfigWindow, w => _detectorConfigWindow = w,
|
||||||
() => new XP.Hardware.Detector.Views.DetectorConfigWindow(), "探测器配置");
|
() => new XP.Hardware.Detector.Views.DetectorConfigWindow(), "Detector Config");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "打开探测器配置窗口失败");
|
_logger.Error(ex, "Failed to open detector config window.");
|
||||||
MessageBox.Show($"打开探测器配置窗口失败:\n{ex.InnerException?.Message ?? ex.Message}",
|
MessageBox.Show($"Failed to open detector config window:\n{ex.InnerException?.Message ?? ex.Message}",
|
||||||
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
"错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,7 +615,7 @@ namespace XplorePlane.ViewModels
|
|||||||
private void ExecuteOpenRaySourceConfig()
|
private void ExecuteOpenRaySourceConfig()
|
||||||
{
|
{
|
||||||
ShowOrActivate(_raySourceConfigWindow, w => _raySourceConfigWindow = w,
|
ShowOrActivate(_raySourceConfigWindow, w => _raySourceConfigWindow = w,
|
||||||
() => new XP.Hardware.RaySource.Views.RaySourceConfigWindow(), "射线源配置");
|
() => new XP.Hardware.RaySource.Views.RaySourceConfigWindow(), "Ray Source Config");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteLoadImage()
|
private void ExecuteLoadImage()
|
||||||
@@ -455,20 +638,20 @@ namespace XplorePlane.ViewModels
|
|||||||
bitmap.EndInit();
|
bitmap.EndInit();
|
||||||
bitmap.Freeze();
|
bitmap.Freeze();
|
||||||
|
|
||||||
_logger.Info("[图像链路] ExecuteLoadImage:加载图像 {Path},准备推送到 MainViewportService 和 ManualImageLoadedEvent", dialog.FileName);
|
_logger.Info("[Image] ExecuteLoadImage loaded image {Path} and will push it to MainViewportService and ManualImageLoadedEvent.", dialog.FileName);
|
||||||
_mainViewportService.SetManualImage(bitmap, dialog.FileName);
|
_mainViewportService.SetManualImage(bitmap, dialog.FileName);
|
||||||
|
|
||||||
// 同时发布事件,让 PipelineEditorViewModel 收到图像并触发流水线执行
|
// Publish the image to the pipeline editor at the same time.
|
||||||
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
_eventAggregator.GetEvent<ManualImageLoadedEvent>()
|
||||||
.Publish(new ManualImageLoadedPayload(bitmap, dialog.FileName));
|
.Publish(new ManualImageLoadedPayload(bitmap, dialog.FileName));
|
||||||
_logger.Info("[图像链路] ManualImageLoadedEvent 已发布");
|
_logger.Info("[Image] ManualImageLoadedEvent published.");
|
||||||
|
|
||||||
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
RaisePropertyChanged(nameof(IsUsingLiveDetectorSource));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "加载图像失败:{Path}", dialog.FileName);
|
_logger.Error(ex, "Failed to load image: {Path}", dialog.FileName);
|
||||||
MessageBox.Show($"加载图像失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show($"Failed to load image: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,18 +674,18 @@ namespace XplorePlane.ViewModels
|
|||||||
var result = raySourceService.WarmUp();
|
var result = raySourceService.WarmUp();
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"暖机失败:{result.ErrorMessage}", "错误",
|
MessageBox.Show($"Warm-up failed: {result.ErrorMessage}", "Error",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.Info("暖机命令已发送");
|
_logger.Info("Warm-up command sent.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "暖机异常");
|
_logger.Error(ex, "Warm-up failed.");
|
||||||
MessageBox.Show($"暖机异常:{ex.Message}", "错误",
|
MessageBox.Show($"Warm-up error: {ex.Message}", "Error",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -525,28 +708,28 @@ namespace XplorePlane.ViewModels
|
|||||||
private void ExecutePointDistanceMeasure()
|
private void ExecutePointDistanceMeasure()
|
||||||
{
|
{
|
||||||
if (!CheckImageLoaded()) return;
|
if (!CheckImageLoaded()) return;
|
||||||
_logger.Info("点点距测量功能已触发");
|
_logger.Info("Point distance measurement triggered.");
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.PointDistance);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.PointDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecutePointLineDistanceMeasure()
|
private void ExecutePointLineDistanceMeasure()
|
||||||
{
|
{
|
||||||
if (!CheckImageLoaded()) return;
|
if (!CheckImageLoaded()) return;
|
||||||
_logger.Info("点线距测量功能已触发");
|
_logger.Info("Point-line distance measurement triggered.");
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.PointLineDistance);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.PointLineDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteAngleMeasure()
|
private void ExecuteAngleMeasure()
|
||||||
{
|
{
|
||||||
if (!CheckImageLoaded()) return;
|
if (!CheckImageLoaded()) return;
|
||||||
_logger.Info("角度测量功能已触发");
|
_logger.Info("Angle measurement triggered.");
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.Angle);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.Angle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteThroughHoleFillRateMeasure()
|
private void ExecuteThroughHoleFillRateMeasure()
|
||||||
{
|
{
|
||||||
if (!CheckImageLoaded()) return;
|
if (!CheckImageLoaded()) return;
|
||||||
_logger.Info("通孔填锡率测量功能已触发");
|
_logger.Info("Through-hole fill-rate measurement triggered.");
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.ThroughHoleFillRate);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.ThroughHoleFillRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,7 +738,7 @@ namespace XplorePlane.ViewModels
|
|||||||
private void ExecuteBgaVoidMeasure()
|
private void ExecuteBgaVoidMeasure()
|
||||||
{
|
{
|
||||||
if (!CheckImageLoaded()) return;
|
if (!CheckImageLoaded()) return;
|
||||||
_logger.Info("BGA空隙测量功能已触发");
|
_logger.Info("BGA void measurement triggered.");
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.BgaVoid);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.BgaVoid);
|
||||||
|
|
||||||
if (_bgaMeasurePanel != null && _bgaMeasurePanel.IsVisible)
|
if (_bgaMeasurePanel != null && _bgaMeasurePanel.IsVisible)
|
||||||
@@ -580,12 +763,12 @@ namespace XplorePlane.ViewModels
|
|||||||
private void ExecuteBubbleMeasure()
|
private void ExecuteBubbleMeasure()
|
||||||
{
|
{
|
||||||
if (!CheckImageLoaded()) return;
|
if (!CheckImageLoaded()) return;
|
||||||
_logger.Info("气泡测量功能已触发");
|
_logger.Info("Bubble measurement triggered.");
|
||||||
|
|
||||||
// 进入气泡测量模式
|
// Enter bubble measurement mode.
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.BubbleMeasure);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.BubbleMeasure);
|
||||||
|
|
||||||
// 弹出工具面板
|
// Open the tool panel.
|
||||||
if (_bubbleMeasurePanel != null && _bubbleMeasurePanel.IsVisible)
|
if (_bubbleMeasurePanel != null && _bubbleMeasurePanel.IsVisible)
|
||||||
{
|
{
|
||||||
_bubbleMeasurePanel.Activate();
|
_bubbleMeasurePanel.Activate();
|
||||||
@@ -598,7 +781,7 @@ namespace XplorePlane.ViewModels
|
|||||||
};
|
};
|
||||||
_bubbleMeasurePanel.Closed += (s, e) =>
|
_bubbleMeasurePanel.Closed += (s, e) =>
|
||||||
{
|
{
|
||||||
// 关闭面板时退出气泡测量模式
|
// Exit bubble measurement mode when the panel closes.
|
||||||
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.None);
|
_eventAggregator.GetEvent<MeasurementToolEvent>().Publish(MeasurementToolMode.None);
|
||||||
};
|
};
|
||||||
_bubbleMeasurePanel.Show();
|
_bubbleMeasurePanel.Show();
|
||||||
@@ -619,7 +802,7 @@ namespace XplorePlane.ViewModels
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.Error(ex, "打开语言设置失败");
|
_logger.Error(ex, "Failed to open language settings.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,38 +814,38 @@ namespace XplorePlane.ViewModels
|
|||||||
|
|
||||||
private void OnNavigateHome()
|
private void OnNavigateHome()
|
||||||
{
|
{
|
||||||
_logger.Info("导航到主页");
|
_logger.Info("Navigated to home.");
|
||||||
LicenseInfo = "主页";
|
LicenseInfo = "首页";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNavigateInspect()
|
private void OnNavigateInspect()
|
||||||
{
|
{
|
||||||
_logger.Info("导航到检测页面");
|
_logger.Info("Navigated to inspection page.");
|
||||||
LicenseInfo = "检测页面";
|
LicenseInfo = "Inspection";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOpenFile()
|
private void OnOpenFile()
|
||||||
{
|
{
|
||||||
_logger.Info("打开文件");
|
_logger.Info("Open file.");
|
||||||
LicenseInfo = "打开文件";
|
LicenseInfo = "打开文件";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExport()
|
private void OnExport()
|
||||||
{
|
{
|
||||||
_logger.Info("导出数据");
|
_logger.Info("Export data.");
|
||||||
LicenseInfo = "导出数据";
|
LicenseInfo = "导出数据";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClear()
|
private void OnClear()
|
||||||
{
|
{
|
||||||
_logger.Info("清除数据");
|
_logger.Info("Clear data.");
|
||||||
LicenseInfo = "清除数据";
|
LicenseInfo = "清除数据";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnEditProperties()
|
private void OnEditProperties()
|
||||||
{
|
{
|
||||||
_logger.Info("编辑属性");
|
_logger.Info("Edit properties.");
|
||||||
LicenseInfo = "编辑属性";
|
LicenseInfo = "Edit properties";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMainViewportStateChanged(object sender, EventArgs e)
|
private void OnMainViewportStateChanged(object sender, EventArgs e)
|
||||||
@@ -678,12 +861,14 @@ namespace XplorePlane.ViewModels
|
|||||||
{
|
{
|
||||||
if (payload?.Image == null)
|
if (payload?.Image == null)
|
||||||
{
|
{
|
||||||
_logger.Warn("[图像链路] OnPipelinePreviewUpdated:payload 或 Image 为 null,跳过");
|
_logger.Warn("[Image] OnPipelinePreviewUpdated skipped because payload or image is null.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_logger.Info("[图像链路] OnPipelinePreviewUpdated:收到流水线结果图像,推送到 MainViewportService");
|
_logger.Info("[Image] OnPipelinePreviewUpdated received a pipeline preview image and pushed it to MainViewportService.");
|
||||||
_mainViewportService.SetManualImage(payload.Image, string.Empty);
|
_mainViewportService.SetManualImage(payload.Image, string.Empty);
|
||||||
}
|
}
|
||||||
|
public sealed record BuiltInInspectionModuleItem(string DisplayName, string FilePath);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="XplorePlane.Views.PipelineEditorView"
|
x:Class="XplorePlane.Views.PipelineEditorView"
|
||||||
x:Name="RootControl"
|
x:Name="RootControl"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
@@ -38,15 +38,18 @@
|
|||||||
|
|
||||||
<Style x:Key="ToolbarBtn" TargetType="Button">
|
<Style x:Key="ToolbarBtn" TargetType="Button">
|
||||||
<Setter Property="Height" Value="28" />
|
<Setter Property="Height" Value="28" />
|
||||||
<Setter Property="MinWidth" Value="52" />
|
<Setter Property="Width" Value="32" />
|
||||||
|
<Setter Property="MinWidth" Value="32" />
|
||||||
<Setter Property="Margin" Value="2,0" />
|
<Setter Property="Margin" Value="2,0" />
|
||||||
<Setter Property="Padding" Value="8,0" />
|
<Setter Property="Padding" Value="0" />
|
||||||
<Setter Property="Background" Value="Transparent" />
|
<Setter Property="Background" Value="Transparent" />
|
||||||
<Setter Property="BorderBrush" Value="#CDCBCB" />
|
<Setter Property="BorderBrush" Value="#CDCBCB" />
|
||||||
<Setter Property="BorderThickness" Value="1" />
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
|
<Setter Property="FontFamily" Value="{StaticResource UiFont}" />
|
||||||
<Setter Property="FontSize" Value="11" />
|
<Setter Property="FontSize" Value="11" />
|
||||||
<Setter Property="Cursor" Value="Hand" />
|
<Setter Property="Cursor" Value="Hand" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||||
</Style>
|
</Style>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
@@ -80,25 +83,40 @@
|
|||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding NewPipelineCommand}"
|
Command="{Binding NewPipelineCommand}"
|
||||||
Content="新建配方"
|
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="新建配方" />
|
ToolTip="新建配方">
|
||||||
|
<TextBlock
|
||||||
|
FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14"
|
||||||
|
Text="" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding SavePipelineCommand}"
|
Command="{Binding SavePipelineCommand}"
|
||||||
Content="保存配方"
|
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="保存当前配方" />
|
ToolTip="保存当前配方">
|
||||||
|
<TextBlock
|
||||||
|
FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14"
|
||||||
|
Text="" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
Width="64"
|
|
||||||
Command="{Binding SaveAsPipelineCommand}"
|
Command="{Binding SaveAsPipelineCommand}"
|
||||||
Content="另存为"
|
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="另存当前配方" />
|
ToolTip="另存当前配方">
|
||||||
|
<TextBlock
|
||||||
|
FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14"
|
||||||
|
Text="" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
Command="{Binding LoadPipelineCommand}"
|
Command="{Binding LoadPipelineCommand}"
|
||||||
Content="加载配方"
|
|
||||||
Style="{StaticResource ToolbarBtn}"
|
Style="{StaticResource ToolbarBtn}"
|
||||||
ToolTip="加载配方" />
|
ToolTip="加载配方">
|
||||||
|
<TextBlock
|
||||||
|
FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="14"
|
||||||
|
Text="" />
|
||||||
|
</Button>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -433,3 +451,4 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|
||||||
|
|||||||
@@ -279,6 +279,33 @@
|
|||||||
Size="Large"
|
Size="Large"
|
||||||
SmallImage="/Assets/Icons/cnc.png"
|
SmallImage="/Assets/Icons/cnc.png"
|
||||||
Text="CNC 编辑" />
|
Text="CNC 编辑" />
|
||||||
|
<telerik:RadRibbonButton
|
||||||
|
telerik:ScreenTip.Description="打开矩阵编排窗口,配置多工件阵列检测方案"
|
||||||
|
telerik:ScreenTip.Title="矩阵编排"
|
||||||
|
Command="{Binding OpenMatrixEditorCommand}"
|
||||||
|
Size="Large"
|
||||||
|
SmallImage="/Assets/Icons/matrix.png"
|
||||||
|
Text="矩阵编排" />
|
||||||
|
<StackPanel Width="170">
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,0,0,4"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="内置检测模块" />
|
||||||
|
<telerik:RadRibbonComboBox
|
||||||
|
Width="160"
|
||||||
|
ItemsSource="{Binding BuiltInInspectionModules}"
|
||||||
|
DisplayMemberPath="DisplayName"
|
||||||
|
SelectedItem="{Binding SelectedBuiltInInspectionModule}"
|
||||||
|
IsEditable="False" />
|
||||||
|
<telerik:RadRibbonButton
|
||||||
|
Margin="0,4,0,0"
|
||||||
|
telerik:ScreenTip.Description="从 Tools 目录扫描到的 .xpm 中选择一个配方,并插入到当前 CNC 程序中"
|
||||||
|
telerik:ScreenTip.Title="插入内置检测模块"
|
||||||
|
Command="{Binding InsertBuiltInInspectionModuleCommand}"
|
||||||
|
Size="Medium"
|
||||||
|
SmallImage="/Assets/Icons/Module.png"
|
||||||
|
Text="插入模块" />
|
||||||
|
</StackPanel>
|
||||||
<!--
|
<!--
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<telerik:RadRibbonButton
|
<telerik:RadRibbonButton
|
||||||
@@ -331,13 +358,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<telerik:RadRibbonButton
|
|
||||||
telerik:ScreenTip.Description="打开矩阵编排窗口,配置多工件阵列检测方案"
|
|
||||||
telerik:ScreenTip.Title="矩阵编排"
|
|
||||||
Command="{Binding OpenMatrixEditorCommand}"
|
|
||||||
Size="Large"
|
|
||||||
SmallImage="/Assets/Icons/matrix.png"
|
|
||||||
Text="矩阵编排" />
|
|
||||||
</telerik:RadRibbonGroup>
|
</telerik:RadRibbonGroup>
|
||||||
|
|
||||||
<telerik:RadRibbonGroup Header="高级模块" IsEnabled="{Binding Path=CellsGroup.IsEnabled}">
|
<telerik:RadRibbonGroup Header="高级模块" IsEnabled="{Binding Path=CellsGroup.IsEnabled}">
|
||||||
@@ -391,6 +412,19 @@
|
|||||||
</telerik:RadRibbonTab>
|
</telerik:RadRibbonTab>
|
||||||
|
|
||||||
<telerik:RadRibbonTab Header="设置">
|
<telerik:RadRibbonTab Header="设置">
|
||||||
|
<telerik:RadRibbonGroup Header="全局设置">
|
||||||
|
<telerik:RadRibbonGroup.Variants>
|
||||||
|
<telerik:GroupVariant Priority="0" Variant="Large" />
|
||||||
|
</telerik:RadRibbonGroup.Variants>
|
||||||
|
<telerik:RadRibbonButton
|
||||||
|
|
||||||
|
Size="Large"
|
||||||
|
SmallImage="/Assets/Icons/setting.png"
|
||||||
|
Command="{Binding OpenSettingsCommand}"
|
||||||
|
Text="全局设置" />
|
||||||
|
|
||||||
|
</telerik:RadRibbonGroup>
|
||||||
|
|
||||||
<telerik:RadRibbonGroup
|
<telerik:RadRibbonGroup
|
||||||
telerik:ScreenTip.Description="Show the Alignment tab of the Format Cells dialog box."
|
telerik:ScreenTip.Description="Show the Alignment tab of the Format Cells dialog box."
|
||||||
telerik:ScreenTip.Title="Format Cells: Alignment"
|
telerik:ScreenTip.Title="Format Cells: Alignment"
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using Telerik.Windows.Controls;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
namespace XplorePlane.Views
|
namespace XplorePlane.Views
|
||||||
@@ -12,6 +16,8 @@ namespace XplorePlane.Views
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
<Window x:Class="XplorePlane.Views.SettingsWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Title="系统设置"
|
||||||
|
Width="860"
|
||||||
|
Height="620"
|
||||||
|
MinWidth="760"
|
||||||
|
MinHeight="540"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
|
ShowInTaskbar="False"
|
||||||
|
Background="#F5F5F5">
|
||||||
|
<Window.Resources>
|
||||||
|
<Style x:Key="SectionTitleStyle" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="16" />
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
|
<Setter Property="Foreground" Value="#1F1F1F" />
|
||||||
|
<Setter Property="Margin" Value="0,0,0,12" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="CardStyle" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="White" />
|
||||||
|
<Setter Property="BorderBrush" Value="#D9D9D9" />
|
||||||
|
<Setter Property="BorderThickness" Value="1" />
|
||||||
|
<Setter Property="CornerRadius" Value="6" />
|
||||||
|
<Setter Property="Padding" Value="16" />
|
||||||
|
<Setter Property="Margin" Value="0,0,0,12" />
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<Style x:Key="ActionButtonStyle" TargetType="Button">
|
||||||
|
<Setter Property="Height" Value="32" />
|
||||||
|
<Setter Property="MinWidth" Value="96" />
|
||||||
|
<Setter Property="Margin" Value="0,0,10,10" />
|
||||||
|
<Setter Property="Padding" Value="12,0" />
|
||||||
|
</Style>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<DockPanel Margin="12">
|
||||||
|
<TabControl Background="White">
|
||||||
|
<TabItem Header="通用">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel Margin="16">
|
||||||
|
<TextBlock Style="{StaticResource SectionTitleStyle}" Text="通用设置" />
|
||||||
|
|
||||||
|
<Border Style="{StaticResource CardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="13"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="XP 数据目录" />
|
||||||
|
<TextBlock Margin="0,8,0,8"
|
||||||
|
Foreground="#666666"
|
||||||
|
Text="Plan 用于 CNC 默认保存和加载,Tools 用于流程图配方 xpm,Data 用于执行结果和中间图像,Report 为报告预留目录。" />
|
||||||
|
|
||||||
|
<Grid Margin="0,4,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="数据根目录" />
|
||||||
|
<TextBox Grid.Column="1"
|
||||||
|
Height="30"
|
||||||
|
Margin="0,0,10,0"
|
||||||
|
Padding="8,0"
|
||||||
|
Text="{Binding DataRootPath, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
|
<Button Grid.Column="2"
|
||||||
|
Command="{Binding BrowseDataRootPathCommand}"
|
||||||
|
Content="浏览"
|
||||||
|
Style="{StaticResource ActionButtonStyle}" />
|
||||||
|
<Button Grid.Column="3"
|
||||||
|
Command="{Binding ResetDataRootPathCommand}"
|
||||||
|
Content="恢复默认"
|
||||||
|
Style="{StaticResource ActionButtonStyle}" />
|
||||||
|
<Button Grid.Column="4"
|
||||||
|
Command="{Binding SaveDataRootPathCommand}"
|
||||||
|
Content="保存"
|
||||||
|
Style="{StaticResource ActionButtonStyle}" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Style="{StaticResource CardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="13"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="界面与使用习惯" />
|
||||||
|
<CheckBox Margin="0,12,0,0"
|
||||||
|
Content="启动时默认显示实时图像" />
|
||||||
|
<CheckBox Margin="0,8,0,0"
|
||||||
|
Content="允许自动恢复上次工作状态" />
|
||||||
|
<CheckBox Margin="0,8,0,0"
|
||||||
|
Content="启用状态栏详细提示" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Style="{StaticResource CardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="13"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="调试" />
|
||||||
|
<WrapPanel>
|
||||||
|
<Button Command="{Binding OpenLibraryVersionsCommand}"
|
||||||
|
Content="版本信息"
|
||||||
|
Style="{StaticResource ActionButtonStyle}" />
|
||||||
|
</WrapPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<TabItem Header="报告">
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
|
<StackPanel Margin="16">
|
||||||
|
<TextBlock Style="{StaticResource SectionTitleStyle}" Text="报告设置" />
|
||||||
|
|
||||||
|
<Border Style="{StaticResource CardStyle}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock FontSize="13"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Text="报告模板" />
|
||||||
|
<TextBlock Margin="0,10,0,8"
|
||||||
|
Foreground="#666666"
|
||||||
|
Text="这里预留报告输出相关设置,可继续扩展公司信息、模板路径、签核信息和导出规则。" />
|
||||||
|
|
||||||
|
<Grid Margin="0,4,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="报告标题" />
|
||||||
|
<TextBox Grid.Column="1"
|
||||||
|
Height="30"
|
||||||
|
Padding="8,0"
|
||||||
|
Text="检测报告" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Margin="0,10,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="120" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="模板路径" />
|
||||||
|
<TextBox Grid.Column="1"
|
||||||
|
Height="30"
|
||||||
|
Padding="8,0"
|
||||||
|
Text="D:\XPData\Report\Templates\Default" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<CheckBox Margin="0,12,0,0"
|
||||||
|
Content="导出时自动附带原始图像" />
|
||||||
|
<CheckBox Margin="0,8,0,0"
|
||||||
|
Content="导出时自动附带处理结果图像" />
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</TabItem>
|
||||||
|
</TabControl>
|
||||||
|
</DockPanel>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace XplorePlane.Views
|
||||||
|
{
|
||||||
|
public partial class SettingsWindow : Window
|
||||||
|
{
|
||||||
|
public SettingsWindow(object viewModel)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContext = viewModel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user