手动阀
This commit is contained in:
@@ -68,6 +68,9 @@ public static class ServiceCollectionExtensions
|
||||
// 运行时部署快照管理服务
|
||||
services.AddSingleton<OrpaonVision.Core.Training.IRuntimeDeploymentSnapshotAppService, OrpaonVision.ConfigApp.Infrastructure.Services.RuntimeDeploymentSnapshotAppService>();
|
||||
|
||||
// 规则校验服务
|
||||
services.AddSingleton<OrpaonVision.Core.Training.IRuleValidationService, OrpaonVision.ConfigApp.Infrastructure.Services.RuleValidationService>();
|
||||
|
||||
// 用户权限管理服务
|
||||
services.AddSingleton<IUserService, UserService>();
|
||||
services.AddSingleton<IRoleService, RoleService>();
|
||||
|
||||
@@ -42,19 +42,23 @@ public class ModelPackageAppService : IModelPackageAppService
|
||||
private readonly ILogger<ModelPackageAppService> _logger;
|
||||
private readonly ModelPackageOptions _options;
|
||||
private readonly string _basePath;
|
||||
private readonly IRuleValidationService _ruleValidationService;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化模型包应用服务。
|
||||
/// </summary>
|
||||
/// <param name="logger">日志记录器。</param>
|
||||
/// <param name="options">模型包配置选项。</param>
|
||||
/// <param name="options">配置选项。</param>
|
||||
/// <param name="ruleValidationService">规则校验服务。</param>
|
||||
public ModelPackageAppService(
|
||||
ILogger<ModelPackageAppService> logger,
|
||||
IOptions<ModelPackageOptions> options)
|
||||
IOptions<ModelPackageOptions> options,
|
||||
IRuleValidationService ruleValidationService)
|
||||
{
|
||||
_logger = logger;
|
||||
_options = options.Value;
|
||||
_basePath = _options.BasePath ?? "ModelPackages";
|
||||
_ruleValidationService = ruleValidationService;
|
||||
|
||||
// 确保基础目录存在
|
||||
if (!Directory.Exists(_basePath))
|
||||
@@ -272,6 +276,31 @@ public class ModelPackageAppService : IModelPackageAppService
|
||||
return Result<ModelPackageImportResultDto>.Fail(validationResult.Code, validationResult.Message);
|
||||
}
|
||||
|
||||
// 校验规则快照包(如果存在)
|
||||
var ruleSnapshotPath = Path.Combine(extractDir, "rules", "snapshot.ovpkg");
|
||||
if (File.Exists(ruleSnapshotPath))
|
||||
{
|
||||
_logger.LogInformation("发现规则快照包,开始校验: {RuleSnapshotPath}", ruleSnapshotPath);
|
||||
|
||||
var ruleValidationResult = await _ruleValidationService.ValidateRuleSnapshotPackageAsync(ruleSnapshotPath, cancellationToken);
|
||||
if (!ruleValidationResult.Succeeded)
|
||||
{
|
||||
// 清理失败的导入
|
||||
if (Directory.Exists(extractDir))
|
||||
{
|
||||
Directory.Delete(extractDir, true);
|
||||
}
|
||||
return Result<ModelPackageImportResultDto>.Fail(ruleValidationResult.Code, $"规则快照包校验失败: {ruleValidationResult.Message}");
|
||||
}
|
||||
|
||||
if (!ruleValidationResult.Data!.IsValid)
|
||||
{
|
||||
var errors = string.Join("; ", ruleValidationResult.Data.Errors);
|
||||
_logger.LogWarning("规则快照包校验发现问题: {Errors}", errors);
|
||||
// 可以选择是否将规则校验失败视为导入失败,这里仅记录警告
|
||||
}
|
||||
}
|
||||
|
||||
var importResult = new ModelPackageImportResultDto
|
||||
{
|
||||
ModelPackageId = packageId,
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OrpaonVision.Core.Results;
|
||||
using OrpaonVision.Core.Training;
|
||||
using OrpaonVision.Core.Training.Contracts;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Schema;
|
||||
|
||||
namespace OrpaonVision.ConfigApp.Infrastructure.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 规则校验服务实现。
|
||||
/// </summary>
|
||||
public class RuleValidationService : IRuleValidationService
|
||||
{
|
||||
private readonly ILogger<RuleValidationService> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化规则校验服务。
|
||||
/// </summary>
|
||||
/// <param name="logger">日志记录器。</param>
|
||||
public RuleValidationService(ILogger<RuleValidationService> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验规则快照包。
|
||||
/// </summary>
|
||||
public async Task<Result<RuleSnapshotValidationResultDto>> ValidateRuleSnapshotPackageAsync(
|
||||
string packagePath,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("开始校验规则快照包: {PackagePath}", packagePath);
|
||||
|
||||
if (!File.Exists(packagePath))
|
||||
{
|
||||
return Result<RuleSnapshotValidationResultDto>.Fail("PACKAGE_NOT_FOUND", "规则快照包文件不存在。");
|
||||
}
|
||||
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(tempDir);
|
||||
|
||||
try
|
||||
{
|
||||
// 解压包文件
|
||||
ZipFile.ExtractToDirectory(packagePath, tempDir);
|
||||
|
||||
// 校验各个部分
|
||||
var layerOrderResult = await ValidateLayerOrderInPackageAsync(tempDir, cancellationToken);
|
||||
var partCodeResult = await ValidatePartCodeUniquenessInPackageAsync(tempDir, cancellationToken);
|
||||
var schemaResult = await ValidateSchemaInPackageAsync(tempDir, cancellationToken);
|
||||
|
||||
var validationResult = new RuleSnapshotValidationResultDto
|
||||
{
|
||||
IsValid = layerOrderResult.Succeeded && layerOrderResult.Data!.IsValid &&
|
||||
partCodeResult.Succeeded && partCodeResult.Data!.IsValid &&
|
||||
schemaResult.Succeeded && schemaResult.Data!.IsValid,
|
||||
Errors = CombineErrors(layerOrderResult, partCodeResult, schemaResult),
|
||||
Warnings = CombineWarnings(layerOrderResult, partCodeResult, schemaResult),
|
||||
LayerOrderValidation = layerOrderResult.Data ?? new LayerOrderValidationResultDto(),
|
||||
PartCodeUniquenessValidation = partCodeResult.Data ?? new PartCodeUniquenessValidationResultDto(),
|
||||
SchemaValidation = schemaResult.Data ?? new SchemaValidationResultDto()
|
||||
};
|
||||
|
||||
_logger.LogInformation("规则快照包校验完成: {PackagePath}, IsValid: {IsValid}",
|
||||
packagePath, validationResult.IsValid);
|
||||
|
||||
return Result<RuleSnapshotValidationResultDto>.Success(validationResult);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 清理临时目录
|
||||
if (Directory.Exists(tempDir))
|
||||
{
|
||||
Directory.Delete(tempDir, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var traceId = Guid.NewGuid().ToString("N");
|
||||
_logger.LogError(ex, "校验规则快照包失败。TraceId: {TraceId}", traceId);
|
||||
return Result<RuleSnapshotValidationResultDto>.FailWithTrace(
|
||||
"VALIDATE_PACKAGE_FAILED", "校验规则快照包失败。", traceId, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验层顺序。
|
||||
/// </summary>
|
||||
public async Task<Result<LayerOrderValidationResultDto>> ValidateLayerOrderAsync(
|
||||
string[] layerOrderData,
|
||||
string[] expectedOrder,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("开始校验层顺序");
|
||||
|
||||
var errors = new List<string>();
|
||||
var isValid = true;
|
||||
|
||||
// 检查层顺序是否匹配
|
||||
if (layerOrderData.Length != expectedOrder.Length)
|
||||
{
|
||||
errors.Add($"层数量不匹配,期望: {expectedOrder.Length},实际: {layerOrderData.Length}");
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Math.Min(layerOrderData.Length, expectedOrder.Length); i++)
|
||||
{
|
||||
if (layerOrderData[i] != expectedOrder[i])
|
||||
{
|
||||
errors.Add($"第 {i + 1} 层不匹配,期望: {expectedOrder[i]},实际: {layerOrderData[i]}");
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
var result = new LayerOrderValidationResultDto
|
||||
{
|
||||
IsValid = isValid,
|
||||
Errors = errors.ToArray(),
|
||||
ActualLayerOrder = layerOrderData,
|
||||
ExpectedLayerOrder = expectedOrder
|
||||
};
|
||||
|
||||
_logger.LogInformation("层顺序校验完成: IsValid: {IsValid}", isValid);
|
||||
return Result<LayerOrderValidationResultDto>.Success(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var traceId = Guid.NewGuid().ToString("N");
|
||||
_logger.LogError(ex, "校验层顺序失败。TraceId: {TraceId}", traceId);
|
||||
return Result<LayerOrderValidationResultDto>.FailWithTrace(
|
||||
"VALIDATE_LAYER_ORDER_FAILED", "校验层顺序失败。", traceId, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验部件编码唯一性。
|
||||
/// </summary>
|
||||
public async Task<Result<PartCodeUniquenessValidationResultDto>> ValidatePartCodeUniquenessAsync(
|
||||
string[] partCodes,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("开始校验部件编码唯一性");
|
||||
|
||||
var errors = new List<string>();
|
||||
var duplicateCodes = partCodes
|
||||
.GroupBy(code => code)
|
||||
.Where(group => group.Count() > 1)
|
||||
.Select(group => group.Key)
|
||||
.ToArray();
|
||||
|
||||
var isValid = duplicateCodes.Length == 0;
|
||||
|
||||
if (!isValid)
|
||||
{
|
||||
errors.AddRange(duplicateCodes.Select(code => $"部件编码 '{code}' 重复出现"));
|
||||
}
|
||||
|
||||
var result = new PartCodeUniquenessValidationResultDto
|
||||
{
|
||||
IsValid = isValid,
|
||||
Errors = errors.ToArray(),
|
||||
DuplicatePartCodes = duplicateCodes,
|
||||
AllPartCodes = partCodes
|
||||
};
|
||||
|
||||
_logger.LogInformation("部件编码唯一性校验完成: IsValid: {IsValid}, DuplicateCount: {DuplicateCount}",
|
||||
isValid, duplicateCodes.Length);
|
||||
|
||||
return Result<PartCodeUniquenessValidationResultDto>.Success(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var traceId = Guid.NewGuid().ToString("N");
|
||||
_logger.LogError(ex, "校验部件编码唯一性失败。TraceId: {TraceId}", traceId);
|
||||
return Result<PartCodeUniquenessValidationResultDto>.FailWithTrace(
|
||||
"VALIDATE_PART_CODE_FAILED", "校验部件编码唯一性失败。", traceId, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验Schema合法性。
|
||||
/// </summary>
|
||||
public async Task<Result<SchemaValidationResultDto>> ValidateSchemaAsync(
|
||||
string[] schemaFiles,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("开始校验Schema合法性");
|
||||
|
||||
var errors = new List<string>();
|
||||
var warnings = new List<string>();
|
||||
var validatedFiles = new List<string>();
|
||||
var isValid = true;
|
||||
|
||||
foreach (var schemaFile in schemaFiles)
|
||||
{
|
||||
if (!File.Exists(schemaFile))
|
||||
{
|
||||
errors.Add($"Schema文件不存在: {schemaFile}");
|
||||
isValid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var schemaContent = await File.ReadAllTextAsync(schemaFile, cancellationToken);
|
||||
|
||||
// 简单的JSON Schema校验
|
||||
if (!IsValidJson(schemaContent))
|
||||
{
|
||||
errors.Add($"Schema文件格式无效: {schemaFile}");
|
||||
isValid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
validatedFiles.Add(schemaFile);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add($"读取Schema文件失败: {schemaFile}, 错误: {ex.Message}");
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
var result = new SchemaValidationResultDto
|
||||
{
|
||||
IsValid = isValid,
|
||||
Errors = errors.ToArray(),
|
||||
Warnings = warnings.ToArray(),
|
||||
SchemaVersion = "1.0", // 可以从配置中读取
|
||||
ValidatedFiles = validatedFiles.ToArray()
|
||||
};
|
||||
|
||||
_logger.LogInformation("Schema合法性校验完成: IsValid: {IsValid}, ValidatedFileCount: {ValidatedFileCount}",
|
||||
isValid, validatedFiles.Count);
|
||||
|
||||
return Result<SchemaValidationResultDto>.Success(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var traceId = Guid.NewGuid().ToString("N");
|
||||
_logger.LogError(ex, "校验Schema合法性失败。TraceId: {TraceId}", traceId);
|
||||
return Result<SchemaValidationResultDto>.FailWithTrace(
|
||||
"VALIDATE_SCHEMA_FAILED", "校验Schema合法性失败。", traceId, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#region 辅助方法
|
||||
|
||||
/// <summary>
|
||||
/// 在包中校验层顺序。
|
||||
/// </summary>
|
||||
private async Task<Result<LayerOrderValidationResultDto>> ValidateLayerOrderInPackageAsync(
|
||||
string packageDir,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var layerOrderFile = Path.Combine(packageDir, "rules", "layer-order.json");
|
||||
|
||||
if (!File.Exists(layerOrderFile))
|
||||
{
|
||||
return Result<LayerOrderValidationResultDto>.Fail("LAYER_ORDER_FILE_NOT_FOUND", "层顺序配置文件不存在。");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var layerOrderContent = await File.ReadAllTextAsync(layerOrderFile, cancellationToken);
|
||||
var layerOrderData = JsonSerializer.Deserialize<string[]>(layerOrderContent) ?? Array.Empty<string>();
|
||||
|
||||
// 默认期望的层顺序 - 可以从配置中读取
|
||||
var expectedOrder = new[] { "background", "foreground", "defect", "annotation" };
|
||||
|
||||
return await ValidateLayerOrderAsync(layerOrderData, expectedOrder, cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result<LayerOrderValidationResultDto>.Fail("PARSE_LAYER_ORDER_FAILED", $"解析层顺序配置失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在包中校验部件编码唯一性。
|
||||
/// </summary>
|
||||
private async Task<Result<PartCodeUniquenessValidationResultDto>> ValidatePartCodeUniquenessInPackageAsync(
|
||||
string packageDir,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var partsFile = Path.Combine(packageDir, "rules", "parts.json");
|
||||
|
||||
if (!File.Exists(partsFile))
|
||||
{
|
||||
return Result<PartCodeUniquenessValidationResultDto>.Fail("PARTS_FILE_NOT_FOUND", "部件配置文件不存在。");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var partsContent = await File.ReadAllTextAsync(partsFile, cancellationToken);
|
||||
var partsData = JsonSerializer.Deserialize<JsonElement>(partsContent);
|
||||
|
||||
var partCodes = new List<string>();
|
||||
if (partsData.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var part in partsData.EnumerateArray())
|
||||
{
|
||||
if (part.TryGetProperty("code", out var codeProperty))
|
||||
{
|
||||
partCodes.Add(codeProperty.GetString() ?? string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await ValidatePartCodeUniquenessAsync(partCodes.ToArray(), cancellationToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result<PartCodeUniquenessValidationResultDto>.Fail("PARSE_PARTS_FAILED", $"解析部件配置失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在包中校验Schema。
|
||||
/// </summary>
|
||||
private async Task<Result<SchemaValidationResultDto>> ValidateSchemaInPackageAsync(
|
||||
string packageDir,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var schemaDir = Path.Combine(packageDir, "schema");
|
||||
|
||||
if (!Directory.Exists(schemaDir))
|
||||
{
|
||||
return Result<SchemaValidationResultDto>.Fail("SCHEMA_DIR_NOT_FOUND", "Schema目录不存在。");
|
||||
}
|
||||
|
||||
var schemaFiles = Directory.GetFiles(schemaDir, "*.json", SearchOption.AllDirectories);
|
||||
return await ValidateSchemaAsync(schemaFiles, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否为有效的JSON。
|
||||
/// </summary>
|
||||
private static bool IsValidJson(string jsonString)
|
||||
{
|
||||
try
|
||||
{
|
||||
JsonDocument.Parse(jsonString);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并错误信息。
|
||||
/// </summary>
|
||||
private static string[] CombineErrors(
|
||||
Result<LayerOrderValidationResultDto> layerOrderResult,
|
||||
Result<PartCodeUniquenessValidationResultDto> partCodeResult,
|
||||
Result<SchemaValidationResultDto> schemaResult)
|
||||
{
|
||||
var errors = new List<string>();
|
||||
|
||||
if (!layerOrderResult.Succeeded)
|
||||
{
|
||||
errors.Add($"层顺序校验失败: {layerOrderResult.Message}");
|
||||
}
|
||||
else if (layerOrderResult.Data?.Errors.Length > 0)
|
||||
{
|
||||
errors.AddRange(layerOrderResult.Data.Errors);
|
||||
}
|
||||
|
||||
if (!partCodeResult.Succeeded)
|
||||
{
|
||||
errors.Add($"部件编码校验失败: {partCodeResult.Message}");
|
||||
}
|
||||
else if (partCodeResult.Data?.Errors.Length > 0)
|
||||
{
|
||||
errors.AddRange(partCodeResult.Data.Errors);
|
||||
}
|
||||
|
||||
if (!schemaResult.Succeeded)
|
||||
{
|
||||
errors.Add($"Schema校验失败: {schemaResult.Message}");
|
||||
}
|
||||
else if (schemaResult.Data?.Errors.Length > 0)
|
||||
{
|
||||
errors.AddRange(schemaResult.Data.Errors);
|
||||
}
|
||||
|
||||
return errors.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并警告信息。
|
||||
/// </summary>
|
||||
private static string[] CombineWarnings(
|
||||
Result<LayerOrderValidationResultDto> layerOrderResult,
|
||||
Result<PartCodeUniquenessValidationResultDto> partCodeResult,
|
||||
Result<SchemaValidationResultDto> schemaResult)
|
||||
{
|
||||
var warnings = new List<string>();
|
||||
|
||||
if (layerOrderResult.Succeeded && layerOrderResult.Data != null)
|
||||
{
|
||||
// 层顺序校验通常没有警告
|
||||
}
|
||||
|
||||
if (partCodeResult.Succeeded && partCodeResult.Data != null)
|
||||
{
|
||||
// 部件编码校验通常没有警告
|
||||
}
|
||||
|
||||
if (schemaResult.Succeeded && schemaResult.Data?.Warnings.Length > 0)
|
||||
{
|
||||
warnings.AddRange(schemaResult.Data.Warnings);
|
||||
}
|
||||
|
||||
return warnings.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -448,11 +448,31 @@ public class RuntimeDeploymentSnapshotAppService : IRuntimeDeploymentSnapshotApp
|
||||
|
||||
if (snapshot != null)
|
||||
{
|
||||
snapshot.Status = RuntimeDeploymentSnapshotStatus.RolledBack;
|
||||
snapshot.RolledBackAtUtc = DateTime.UtcNow;
|
||||
snapshot.RolledBackBy = "System"; // 系统自动回滚
|
||||
// 创建新的快照对象用于更新
|
||||
var updatedSnapshot = new RuntimeDeploymentSnapshot
|
||||
{
|
||||
Id = snapshot.Id,
|
||||
Name = snapshot.Name,
|
||||
Description = snapshot.Description,
|
||||
ProductTypeId = snapshot.ProductTypeId,
|
||||
ProductTypeCode = snapshot.ProductTypeCode,
|
||||
RuleVersionId = snapshot.RuleVersionId,
|
||||
RuleVersionNo = snapshot.RuleVersionNo,
|
||||
ModelPackageVersionId = snapshot.ModelPackageVersionId,
|
||||
ModelPackageVersionNo = snapshot.ModelPackageVersionNo,
|
||||
RuntimeParameterVersionId = snapshot.RuntimeParameterVersionId,
|
||||
RuntimeParameterVersionNo = snapshot.RuntimeParameterVersionNo,
|
||||
Status = RuntimeDeploymentSnapshotStatus.RolledBack,
|
||||
CreatedAtUtc = snapshot.CreatedAtUtc,
|
||||
CreatedBy = snapshot.CreatedBy,
|
||||
ActivatedAtUtc = snapshot.ActivatedAtUtc,
|
||||
ActivatedBy = snapshot.ActivatedBy,
|
||||
RolledBackAtUtc = DateTime.UtcNow,
|
||||
RolledBackBy = "System", // 系统自动回滚
|
||||
MetadataJson = snapshot.MetadataJson
|
||||
};
|
||||
|
||||
await SaveSnapshotAsync(snapshot, cancellationToken);
|
||||
await SaveSnapshotAsync(updatedSnapshot, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
namespace OrpaonVision.Core.Training.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// 规则快照包校验结果。
|
||||
/// </summary>
|
||||
public sealed class RuleSnapshotValidationResultDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否通过校验。
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 校验错误信息。
|
||||
/// </summary>
|
||||
public string[] Errors { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 校验警告信息。
|
||||
/// </summary>
|
||||
public string[] Warnings { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 层顺序校验结果。
|
||||
/// </summary>
|
||||
public LayerOrderValidationResultDto LayerOrderValidation { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 部件编码唯一性校验结果。
|
||||
/// </summary>
|
||||
public PartCodeUniquenessValidationResultDto PartCodeUniquenessValidation { get; init; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Schema合法性校验结果。
|
||||
/// </summary>
|
||||
public SchemaValidationResultDto SchemaValidation { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 层顺序校验结果。
|
||||
/// </summary>
|
||||
public sealed class LayerOrderValidationResultDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否通过校验。
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息。
|
||||
/// </summary>
|
||||
public string[] Errors { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 实际层顺序。
|
||||
/// </summary>
|
||||
public string[] ActualLayerOrder { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 期望层顺序。
|
||||
/// </summary>
|
||||
public string[] ExpectedLayerOrder { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 部件编码唯一性校验结果。
|
||||
/// </summary>
|
||||
public sealed class PartCodeUniquenessValidationResultDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否通过校验。
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息。
|
||||
/// </summary>
|
||||
public string[] Errors { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 重复的部件编码。
|
||||
/// </summary>
|
||||
public string[] DuplicatePartCodes { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 所有部件编码。
|
||||
/// </summary>
|
||||
public string[] AllPartCodes { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Schema合法性校验结果。
|
||||
/// </summary>
|
||||
public sealed class SchemaValidationResultDto
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否通过校验。
|
||||
/// </summary>
|
||||
public bool IsValid { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息。
|
||||
/// </summary>
|
||||
public string[] Errors { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 警告信息。
|
||||
/// </summary>
|
||||
public string[] Warnings { get; init; } = Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Schema版本。
|
||||
/// </summary>
|
||||
public string SchemaVersion { get; init; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 校验的文件路径。
|
||||
/// </summary>
|
||||
public string[] ValidatedFiles { get; init; } = Array.Empty<string>();
|
||||
}
|
||||
52
OrpaonVision.Core/Training/IRuleValidationService.cs
Normal file
52
OrpaonVision.Core/Training/IRuleValidationService.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using OrpaonVision.Core.Results;
|
||||
using OrpaonVision.Core.Training.Contracts;
|
||||
|
||||
namespace OrpaonVision.Core.Training;
|
||||
|
||||
/// <summary>
|
||||
/// 规则校验服务接口。
|
||||
/// </summary>
|
||||
public interface IRuleValidationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 校验规则快照包。
|
||||
/// </summary>
|
||||
/// <param name="packagePath">包路径。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>校验结果。</returns>
|
||||
Task<Result<RuleSnapshotValidationResultDto>> ValidateRuleSnapshotPackageAsync(
|
||||
string packagePath,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 校验层顺序。
|
||||
/// </summary>
|
||||
/// <param name="layerOrderData">层顺序数据。</param>
|
||||
/// <param name="expectedOrder">期望的层顺序。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>校验结果。</returns>
|
||||
Task<Result<LayerOrderValidationResultDto>> ValidateLayerOrderAsync(
|
||||
string[] layerOrderData,
|
||||
string[] expectedOrder,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 校验部件编码唯一性。
|
||||
/// </summary>
|
||||
/// <param name="partCodes">部件编码列表。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>校验结果。</returns>
|
||||
Task<Result<PartCodeUniquenessValidationResultDto>> ValidatePartCodeUniquenessAsync(
|
||||
string[] partCodes,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 校验Schema合法性。
|
||||
/// </summary>
|
||||
/// <param name="schemaFiles">Schema文件路径列表。</param>
|
||||
/// <param name="cancellationToken">取消令牌。</param>
|
||||
/// <returns>校验结果。</returns>
|
||||
Task<Result<SchemaValidationResultDto>> ValidateSchemaAsync(
|
||||
string[] schemaFiles,
|
||||
CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -71,6 +71,9 @@ namespace OrpaonVision.SiteApp.DependencyInjection
|
||||
services.AddSingleton<IImageProcessingService, ImageProcessingService>();
|
||||
services.AddSingleton<OptimizedMainWindowViewModel>();
|
||||
|
||||
// 一致性检查服务
|
||||
services.AddSingleton<IConsistencyCheckService, ConsistencyCheckService>();
|
||||
|
||||
// 日志
|
||||
services.AddLogging(builder =>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
namespace OrpaonVision.SiteApp.Runtime.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// 一致性状态枚举。
|
||||
/// </summary>
|
||||
public enum ConsistencyStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 正常。
|
||||
/// </summary>
|
||||
Ok,
|
||||
|
||||
/// <summary>
|
||||
/// 警告。
|
||||
/// </summary>
|
||||
Warning,
|
||||
|
||||
/// <summary>
|
||||
/// 错误。
|
||||
/// </summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 健康状态枚举。
|
||||
/// </summary>
|
||||
public enum HealthStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// 健康。
|
||||
/// </summary>
|
||||
Healthy,
|
||||
|
||||
/// <summary>
|
||||
/// 降级。
|
||||
/// </summary>
|
||||
Degraded,
|
||||
|
||||
/// <summary>
|
||||
/// 不健康。
|
||||
/// </summary>
|
||||
Unhealthy
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 一致性检查结果。
|
||||
/// </summary>
|
||||
public sealed class ConsistencyCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查时间戳。
|
||||
/// </summary>
|
||||
public DateTime CheckTimestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 检查状态。
|
||||
/// </summary>
|
||||
public ConsistencyStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 检查消息。
|
||||
/// </summary>
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 相机是否连接。
|
||||
/// </summary>
|
||||
public bool CameraConnected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// YOLO是否初始化。
|
||||
/// </summary>
|
||||
public bool YoloInitialized { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 像素格式是否一致。
|
||||
/// </summary>
|
||||
public bool PixelFormatConsistent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 像素格式检查详情。
|
||||
/// </summary>
|
||||
public List<string> PixelFormatDetails { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 图像尺寸是否一致。
|
||||
/// </summary>
|
||||
public bool ImageSizeConsistent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图像尺寸检查详情。
|
||||
/// </summary>
|
||||
public List<string> ImageSizeDetails { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 数据流完整性。
|
||||
/// </summary>
|
||||
public bool DataFlowIntegrity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 数据流检查详情。
|
||||
/// </summary>
|
||||
public List<string> DataFlowDetails { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 建议列表。
|
||||
/// </summary>
|
||||
public List<string> Recommendations { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 健康检查结果。
|
||||
/// </summary>
|
||||
public sealed class HealthCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查时间戳。
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 整体状态。
|
||||
/// </summary>
|
||||
public HealthStatus OverallStatus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 相机健康状态。
|
||||
/// </summary>
|
||||
public ComponentHealth CameraHealth { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// YOLO健康状态。
|
||||
/// </summary>
|
||||
public ComponentHealth YoloHealth { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 系统健康状态。
|
||||
/// </summary>
|
||||
public ComponentHealth SystemHealth { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 健康检查摘要。
|
||||
/// </summary>
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 组件健康状态。
|
||||
/// </summary>
|
||||
public sealed class ComponentHealth
|
||||
{
|
||||
/// <summary>
|
||||
/// 组件名称。
|
||||
/// </summary>
|
||||
public string ComponentName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 健康状态。
|
||||
/// </summary>
|
||||
public HealthStatus Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 详细信息。
|
||||
/// </summary>
|
||||
public List<string> Details { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 指标数据。
|
||||
/// </summary>
|
||||
public Dictionary<string, object> Metrics { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 像素格式检查结果。
|
||||
/// </summary>
|
||||
internal sealed class PixelFormatCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否一致。
|
||||
/// </summary>
|
||||
public bool IsConsistent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 检查详情。
|
||||
/// </summary>
|
||||
public List<string> Details { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 建议列表。
|
||||
/// </summary>
|
||||
public List<string> Recommendations { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 图像尺寸检查结果。
|
||||
/// </summary>
|
||||
internal sealed class ImageSizeCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否一致。
|
||||
/// </summary>
|
||||
public bool IsConsistent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 检查详情。
|
||||
/// </summary>
|
||||
public List<string> Details { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 建议列表。
|
||||
/// </summary>
|
||||
public List<string> Recommendations { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 数据流检查结果。
|
||||
/// </summary>
|
||||
internal sealed class DataFlowCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否完整。
|
||||
/// </summary>
|
||||
public bool IsIntegrity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 检查详情。
|
||||
/// </summary>
|
||||
public List<string> Details { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 建议列表。
|
||||
/// </summary>
|
||||
public List<string> Recommendations { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace OrpaonVision.SiteApp.Runtime.Contracts;
|
||||
|
||||
/// <summary>
|
||||
/// 一致性检查服务接口。
|
||||
/// </summary>
|
||||
public interface IConsistencyCheckService
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查相机与推理服务的一致性。
|
||||
/// </summary>
|
||||
/// <returns>一致性检查结果。</returns>
|
||||
Result<ConsistencyCheckResult> CheckCameraInferenceConsistency();
|
||||
|
||||
/// <summary>
|
||||
/// 获取最小健康信息。
|
||||
/// </summary>
|
||||
/// <returns>健康检查结果。</returns>
|
||||
Result<HealthCheckResult> GetMinimalHealthInfo();
|
||||
}
|
||||
Reference in New Issue
Block a user