diff --git a/XplorePlane/Models/MatrixModels.cs b/XplorePlane/Models/MatrixModels.cs index ddea94e..42582ab 100644 --- a/XplorePlane/Models/MatrixModels.cs +++ b/XplorePlane/Models/MatrixModels.cs @@ -40,7 +40,8 @@ namespace XplorePlane.Models double StartOffsetX, double StartOffsetY, string CncProgramPath, - IReadOnlyList Cells + IReadOnlyList Cells, + string CncProgramContent = null ); // ── 矩阵执行摘要模型(用于 matrix_summary.json 序列化)──────────── diff --git a/XplorePlane/Services/Matrix/MatrixService.cs b/XplorePlane/Services/Matrix/MatrixService.cs index cad176a..99f2c1c 100644 --- a/XplorePlane/Services/Matrix/MatrixService.cs +++ b/XplorePlane/Services/Matrix/MatrixService.cs @@ -7,6 +7,7 @@ using System.Text.Json.Serialization; using System.Threading.Tasks; using XP.Common.Logging.Interfaces; using XplorePlane.Models; +using XplorePlane.Services.Cnc; namespace XplorePlane.Services.Matrix { @@ -19,6 +20,7 @@ namespace XplorePlane.Services.Matrix public class MatrixService : IMatrixService { private readonly ILoggerService _logger; + private readonly ICncProgramService _cncProgramService; // ── 序列化配置 | Serialization options ── private static readonly JsonSerializerOptions MatrixJsonOptions = new() @@ -29,11 +31,13 @@ namespace XplorePlane.Services.Matrix Converters = { new JsonStringEnumConverter() } }; - public MatrixService(ILoggerService logger) + public MatrixService(ILoggerService logger, ICncProgramService cncProgramService) { ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(cncProgramService); _logger = logger.ForModule(); + _cncProgramService = cncProgramService; _logger.Info("MatrixService 已初始化 | MatrixService initialized"); } @@ -130,7 +134,11 @@ namespace XplorePlane.Services.Matrix // 优先使用传入的文件路径(绝对路径),否则回退到程序名 var programPath = !string.IsNullOrWhiteSpace(filePath) ? filePath : program.Name; - var updated = layout with { CncProgramPath = programPath }; + + // 序列化 CNC 程序内容嵌入矩阵布局,便于直接运行无需依赖外部文件 + var programContent = _cncProgramService.Serialize(program); + + var updated = layout with { CncProgramPath = programPath, CncProgramContent = programContent }; _logger.Info("已关联 CNC 程序到矩阵布局 | Associated CNC program with matrix layout: LayoutId={LayoutId}, Program={ProgramPath}", layout.Id, programPath); diff --git a/XplorePlane/ViewModels/Cnc/MatrixEditorViewModel.cs b/XplorePlane/ViewModels/Cnc/MatrixEditorViewModel.cs index dc366cc..e3e7bf6 100644 --- a/XplorePlane/ViewModels/Cnc/MatrixEditorViewModel.cs +++ b/XplorePlane/ViewModels/Cnc/MatrixEditorViewModel.cs @@ -3,6 +3,7 @@ using Prism.Commands; using Prism.Events; using Prism.Mvvm; using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; @@ -524,7 +525,23 @@ namespace XplorePlane.ViewModels.Cnc CncProgram templateProgram; try { - templateProgram = await _cncProgramService.LoadAsync(AssociatedProgramPath); + // 优先从文件加载,文件不存在时回退到嵌入的 CNC 内容 + // Prefer loading from file; fall back to embedded CNC content if file is missing + if (File.Exists(AssociatedProgramPath)) + { + templateProgram = await _cncProgramService.LoadAsync(AssociatedProgramPath); + } + else if (!string.IsNullOrWhiteSpace(_currentLayout.CncProgramContent)) + { + templateProgram = _cncProgramService.Deserialize(_currentLayout.CncProgramContent); + _logger.Info("CNC 文件不存在,使用嵌入内容运行 | CNC file not found, using embedded content: {Path}", AssociatedProgramPath); + } + else + { + MessageBox.Show($"无法加载程序文件:{AssociatedProgramName}\n文件不存在且矩阵方案中未嵌入程序内容,请重新关联。", + "错误", MessageBoxButton.OK, MessageBoxImage.Error); + return; + } } catch (Exception ex) { @@ -594,6 +611,10 @@ namespace XplorePlane.ViewModels.Cnc } finally { + // 将 ViewModel 中的执行状态同步回 _currentLayout,确保保存时反映实际执行结果 + // Sync execution status from ViewModels back to _currentLayout so saves reflect actual results + SyncCellStatusToLayout(); + IsExecuting = false; _executionCts?.Dispose(); _executionCts = null; @@ -641,5 +662,37 @@ namespace XplorePlane.ViewModels.Cnc SelectedCell = match ?? Cells.FirstOrDefault(); } } + + /// + /// 将 ViewModel 中各单元格的执行状态同步回 _currentLayout 不可变模型。 + /// 执行后调用,确保保存矩阵时 Status/ErrorMessage 字段反映实际执行结果。 + /// Sync cell execution status from ViewModels back to the immutable _currentLayout model. + /// Called after execution to ensure saved matrix reflects actual execution results. + /// + private void SyncCellStatusToLayout() + { + if (_currentLayout == null || Cells == null || Cells.Count == 0) + return; + + var updatedCells = new List(_currentLayout.Cells.Count); + foreach (var original in _currentLayout.Cells) + { + var cellVm = Cells.FirstOrDefault(c => c.Row == original.Row && c.Column == original.Column); + if (cellVm != null) + { + updatedCells.Add(original with + { + Status = cellVm.Status, + ErrorMessage = cellVm.ErrorMessage ?? original.ErrorMessage + }); + } + else + { + updatedCells.Add(original); + } + } + + _currentLayout = _currentLayout with { Cells = updatedCells.AsReadOnly() }; + } } }