修复运行错误
This commit is contained in:
Binary file not shown.
@@ -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)
|
||||
|
||||
@@ -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<long>(
|
||||
"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=@name",
|
||||
new Dictionary<string, object> { ["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<long>(
|
||||
$"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<long>(
|
||||
|
||||
@@ -28,5 +28,11 @@ namespace XplorePlane.Services.MainViewport
|
||||
/// CNC 开始运行时传入 true,结束时传入 false。
|
||||
/// </summary>
|
||||
void SetCncRunning(bool isRunning);
|
||||
|
||||
/// <summary>
|
||||
/// 由 CNC 执行引擎内部调用,将节点结果图像推送到 viewport。
|
||||
/// 与 <see cref="SetManualImage"/> 不同,此方法在 CNC 运行期间不会被阻断。
|
||||
/// </summary>
|
||||
void SetCncResultImage(ImageSource image, string label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user