修复运行错误
This commit is contained in:
Binary file not shown.
@@ -362,7 +362,7 @@ namespace XplorePlane.Services.Cnc
|
|||||||
Height = resultImage.PixelHeight
|
Height = resultImage.PixelHeight
|
||||||
});
|
});
|
||||||
nodeResult.Status = InspectionNodeStatus.Succeeded;
|
nodeResult.Status = InspectionNodeStatus.Succeeded;
|
||||||
_mainViewportService?.SetManualImage(resultImage, $"CNC Node: {inspectionNode.Name}");
|
_mainViewportService?.SetCncResultImage(resultImage, $"CNC 节点结果:{inspectionNode.Name}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
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()
|
private async Task EnsureInitializedAsync()
|
||||||
{
|
{
|
||||||
if (_initialized)
|
if (_initialized)
|
||||||
@@ -650,6 +663,16 @@ WHERE run_id = @run_id";
|
|||||||
}
|
}
|
||||||
|
|
||||||
Directory.CreateDirectory(_baseDirectory);
|
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);
|
var result = await _db.ExecuteNonQueryAsync(CreateTableSql).ConfigureAwait(false);
|
||||||
if (!result.IsSuccess)
|
if (!result.IsSuccess)
|
||||||
{
|
{
|
||||||
@@ -660,6 +683,49 @@ WHERE run_id = @run_id";
|
|||||||
_initialized = true;
|
_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)
|
private async Task EnsureRunExistsAsync(Guid runId)
|
||||||
{
|
{
|
||||||
var (result, value) = await _db.ExecuteScalarAsync<long>(
|
var (result, value) = await _db.ExecuteScalarAsync<long>(
|
||||||
|
|||||||
@@ -28,5 +28,11 @@ namespace XplorePlane.Services.MainViewport
|
|||||||
/// CNC 开始运行时传入 true,结束时传入 false。
|
/// CNC 开始运行时传入 true,结束时传入 false。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void SetCncRunning(bool isRunning);
|
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();
|
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)
|
public void SetManualImage(ImageSource image, string filePath)
|
||||||
{
|
{
|
||||||
if (image == null)
|
if (image == null)
|
||||||
|
|||||||
Reference in New Issue
Block a user