From cfdfe330a5d821e882433479684393a4bfca75a5 Mon Sep 17 00:00:00 2001 From: "zhengxuan.zhang" Date: Mon, 11 May 2026 16:15:19 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BF=90=E8=A1=8C=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DataBase/XP.db | Bin 0 -> 176128 bytes .../Services/Cnc/CncExecutionService.cs | 2 +- .../InspectionResultStore.cs | 66 ++++++++++++++++++ .../MainViewport/IMainViewportService.cs | 6 ++ .../MainViewport/MainViewportService.cs | 22 ++++++ 5 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 DataBase/XP.db diff --git a/DataBase/XP.db b/DataBase/XP.db new file mode 100644 index 0000000000000000000000000000000000000000..cbad8ab29a89a20d19f3a103868125b5c828cdcb GIT binary patch literal 176128 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 C=_9-V literal 0 HcmV?d00001 diff --git a/XplorePlane/Services/Cnc/CncExecutionService.cs b/XplorePlane/Services/Cnc/CncExecutionService.cs index 9fefa9f..7bbc3a6 100644 --- a/XplorePlane/Services/Cnc/CncExecutionService.cs +++ b/XplorePlane/Services/Cnc/CncExecutionService.cs @@ -362,7 +362,7 @@ namespace XplorePlane.Services.Cnc Height = resultImage.PixelHeight }); nodeResult.Status = InspectionNodeStatus.Succeeded; - _mainViewportService?.SetManualImage(resultImage, $"CNC Node: {inspectionNode.Name}"); + _mainViewportService?.SetCncResultImage(resultImage, $"CNC 节点结果:{inspectionNode.Name}"); } } catch (Exception ex) diff --git a/XplorePlane/Services/InspectionResults/InspectionResultStore.cs b/XplorePlane/Services/InspectionResults/InspectionResultStore.cs index f825299..2e61f60 100644 --- a/XplorePlane/Services/InspectionResults/InspectionResultStore.cs +++ b/XplorePlane/Services/InspectionResults/InspectionResultStore.cs @@ -642,6 +642,19 @@ WHERE run_id = @run_id"; }; } + // Schema migration: columns added after initial release that may be missing in existing databases. + private static readonly (string Table, string Column, string Definition)[] MigrationColumns = + [ + ("inspection_runs", "status", "TEXT NOT NULL DEFAULT 'pending'"), + ("inspection_runs", "workpiece_id", "TEXT NOT NULL DEFAULT ''"), + ("inspection_runs", "serial_number", "TEXT NOT NULL DEFAULT ''"), + ("inspection_runs", "source_image_path", "TEXT NOT NULL DEFAULT ''"), + ("inspection_runs", "result_root_path", "TEXT NOT NULL DEFAULT ''"), + ("inspection_runs", "node_count", "INTEGER NOT NULL DEFAULT 0"), + ("inspection_node_results", "status", "TEXT NOT NULL DEFAULT 'Pending'"), + ("inspection_node_results", "duration_ms", "INTEGER NOT NULL DEFAULT 0"), + ]; + private async Task EnsureInitializedAsync() { if (_initialized) @@ -650,6 +663,16 @@ WHERE run_id = @run_id"; } Directory.CreateDirectory(_baseDirectory); + + // Step 1: Apply column migrations BEFORE running CreateTableSql. + // CreateTableSql contains CREATE INDEX statements that reference columns (e.g. "status") + // which may not exist in databases created by older versions of the app. + // Those index statements will fail with "no such column" even though they are + // guarded by IF NOT EXISTS, because SQLite validates column references at parse time. + // Running ALTER TABLE ADD COLUMN first ensures the columns exist before the index SQL runs. + await ApplySchemaMigrationsAsync().ConfigureAwait(false); + + // Step 2: Create any tables / indexes that do not yet exist. var result = await _db.ExecuteNonQueryAsync(CreateTableSql).ConfigureAwait(false); if (!result.IsSuccess) { @@ -660,6 +683,49 @@ WHERE run_id = @run_id"; _initialized = true; } + private async Task ApplySchemaMigrationsAsync() + { + foreach (var (table, column, definition) in MigrationColumns) + { + // Check if the table exists at all. If not, CreateTableSql will create it with all + // columns, so there is nothing to migrate. + var (tableExistResult, tableCount) = await _db.ExecuteScalarAsync( + "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=@name", + new Dictionary { ["name"] = table }).ConfigureAwait(false); + + if (!tableExistResult.IsSuccess || tableCount == 0) + continue; + + // Check whether the column already exists. + // pragma_table_info is a table-valued function and does not accept bound parameters, + // so the table name is inlined. All values in MigrationColumns are internal constants. + // ExecuteScalarAsync uses ExecuteScalarAsync() directly and does not go through + // DataTable, so it is not affected by the dflt_value BLOB mapping issue. + var (colExistResult, colCount) = await _db.ExecuteScalarAsync( + $"SELECT COUNT(*) FROM pragma_table_info('{table}') WHERE name='{column}'" + ).ConfigureAwait(false); + + if (!colExistResult.IsSuccess) + { + _logger.Warn("Schema migration: could not check column '{0}' in '{1}', skipping: {2}", + column, table, colExistResult.Message); + continue; + } + + if (colCount == 0) + { + var alterResult = await _db.ExecuteNonQueryAsync( + $"ALTER TABLE {table} ADD COLUMN {column} {definition}").ConfigureAwait(false); + + if (alterResult.IsSuccess) + _logger.Info("Schema migration: added column '{0}' to table '{1}'", column, table); + else + _logger.Warn("Schema migration: failed to add column '{0}' to '{1}': {2}", + column, table, alterResult.Message); + } + } + } + private async Task EnsureRunExistsAsync(Guid runId) { var (result, value) = await _db.ExecuteScalarAsync( diff --git a/XplorePlane/Services/MainViewport/IMainViewportService.cs b/XplorePlane/Services/MainViewport/IMainViewportService.cs index 54c212b..f1b7e33 100644 --- a/XplorePlane/Services/MainViewport/IMainViewportService.cs +++ b/XplorePlane/Services/MainViewport/IMainViewportService.cs @@ -28,5 +28,11 @@ namespace XplorePlane.Services.MainViewport /// CNC 开始运行时传入 true,结束时传入 false。 /// void SetCncRunning(bool isRunning); + + /// + /// 由 CNC 执行引擎内部调用,将节点结果图像推送到 viewport。 + /// 与 不同,此方法在 CNC 运行期间不会被阻断。 + /// + void SetCncResultImage(ImageSource image, string label); } } diff --git a/XplorePlane/Services/MainViewport/MainViewportService.cs b/XplorePlane/Services/MainViewport/MainViewportService.cs index 86be1fa..385ee60 100644 --- a/XplorePlane/Services/MainViewport/MainViewportService.cs +++ b/XplorePlane/Services/MainViewport/MainViewportService.cs @@ -186,6 +186,28 @@ namespace XplorePlane.Services.MainViewport RaiseStateChanged(); } + public void SetCncResultImage(ImageSource image, string label) + { + if (image == null) + return; + + var displayLabel = string.IsNullOrWhiteSpace(label) ? "CNC 节点结果" : label; + + lock (_syncRoot) + { + // Intentionally bypasses the _isCncRunning guard so the node result + // is visible in the viewport immediately after each node completes. + _latestManualImage = image; + _latestManualInfo = displayLabel; + _currentSourceMode = MainViewportSourceMode.ManualImage; + _currentDisplayImage = _latestManualImage; + _currentDisplayInfo = _latestManualInfo; + } + + _logger.Info("[图像链路] MainViewportService.SetCncResultImage:已推送 CNC 节点结果图像 {Label}", displayLabel); + RaiseStateChanged(); + } + public void SetManualImage(ImageSource image, string filePath) { if (image == null)