diff --git a/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs b/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs index dda6d99..13b1339 100644 --- a/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs +++ b/OrpaonVision.ConfigApp/DependencyInjection/ServiceCollectionExtensions.cs @@ -68,6 +68,9 @@ public static class ServiceCollectionExtensions // 运行时部署快照管理服务 services.AddSingleton(); + // 规则校验服务 + services.AddSingleton(); + // 用户权限管理服务 services.AddSingleton(); services.AddSingleton(); diff --git a/OrpaonVision.ConfigApp/Infrastructure/Services/ModelPackageAppService.cs b/OrpaonVision.ConfigApp/Infrastructure/Services/ModelPackageAppService.cs index 65b2654..bd46c3c 100644 --- a/OrpaonVision.ConfigApp/Infrastructure/Services/ModelPackageAppService.cs +++ b/OrpaonVision.ConfigApp/Infrastructure/Services/ModelPackageAppService.cs @@ -42,19 +42,23 @@ public class ModelPackageAppService : IModelPackageAppService private readonly ILogger _logger; private readonly ModelPackageOptions _options; private readonly string _basePath; + private readonly IRuleValidationService _ruleValidationService; /// /// 初始化模型包应用服务。 /// /// 日志记录器。 - /// 模型包配置选项。 + /// 配置选项。 + /// 规则校验服务。 public ModelPackageAppService( ILogger logger, - IOptions options) + IOptions 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.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.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, diff --git a/OrpaonVision.ConfigApp/Infrastructure/Services/RuleValidationService.cs b/OrpaonVision.ConfigApp/Infrastructure/Services/RuleValidationService.cs new file mode 100644 index 0000000..645c6b7 --- /dev/null +++ b/OrpaonVision.ConfigApp/Infrastructure/Services/RuleValidationService.cs @@ -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; + +/// +/// 规则校验服务实现。 +/// +public class RuleValidationService : IRuleValidationService +{ + private readonly ILogger _logger; + + /// + /// 初始化规则校验服务。 + /// + /// 日志记录器。 + public RuleValidationService(ILogger logger) + { + _logger = logger; + } + + /// + /// 校验规则快照包。 + /// + public async Task> ValidateRuleSnapshotPackageAsync( + string packagePath, + CancellationToken cancellationToken = default) + { + try + { + _logger.LogInformation("开始校验规则快照包: {PackagePath}", packagePath); + + if (!File.Exists(packagePath)) + { + return Result.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.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.FailWithTrace( + "VALIDATE_PACKAGE_FAILED", "校验规则快照包失败。", traceId, ex.Message); + } + } + + /// + /// 校验层顺序。 + /// + public async Task> ValidateLayerOrderAsync( + string[] layerOrderData, + string[] expectedOrder, + CancellationToken cancellationToken = default) + { + try + { + _logger.LogInformation("开始校验层顺序"); + + var errors = new List(); + 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.Success(result); + } + catch (Exception ex) + { + var traceId = Guid.NewGuid().ToString("N"); + _logger.LogError(ex, "校验层顺序失败。TraceId: {TraceId}", traceId); + return Result.FailWithTrace( + "VALIDATE_LAYER_ORDER_FAILED", "校验层顺序失败。", traceId, ex.Message); + } + } + + /// + /// 校验部件编码唯一性。 + /// + public async Task> ValidatePartCodeUniquenessAsync( + string[] partCodes, + CancellationToken cancellationToken = default) + { + try + { + _logger.LogInformation("开始校验部件编码唯一性"); + + var errors = new List(); + 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.Success(result); + } + catch (Exception ex) + { + var traceId = Guid.NewGuid().ToString("N"); + _logger.LogError(ex, "校验部件编码唯一性失败。TraceId: {TraceId}", traceId); + return Result.FailWithTrace( + "VALIDATE_PART_CODE_FAILED", "校验部件编码唯一性失败。", traceId, ex.Message); + } + } + + /// + /// 校验Schema合法性。 + /// + public async Task> ValidateSchemaAsync( + string[] schemaFiles, + CancellationToken cancellationToken = default) + { + try + { + _logger.LogInformation("开始校验Schema合法性"); + + var errors = new List(); + var warnings = new List(); + var validatedFiles = new List(); + 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.Success(result); + } + catch (Exception ex) + { + var traceId = Guid.NewGuid().ToString("N"); + _logger.LogError(ex, "校验Schema合法性失败。TraceId: {TraceId}", traceId); + return Result.FailWithTrace( + "VALIDATE_SCHEMA_FAILED", "校验Schema合法性失败。", traceId, ex.Message); + } + } + + #region 辅助方法 + + /// + /// 在包中校验层顺序。 + /// + private async Task> ValidateLayerOrderInPackageAsync( + string packageDir, + CancellationToken cancellationToken) + { + var layerOrderFile = Path.Combine(packageDir, "rules", "layer-order.json"); + + if (!File.Exists(layerOrderFile)) + { + return Result.Fail("LAYER_ORDER_FILE_NOT_FOUND", "层顺序配置文件不存在。"); + } + + try + { + var layerOrderContent = await File.ReadAllTextAsync(layerOrderFile, cancellationToken); + var layerOrderData = JsonSerializer.Deserialize(layerOrderContent) ?? Array.Empty(); + + // 默认期望的层顺序 - 可以从配置中读取 + var expectedOrder = new[] { "background", "foreground", "defect", "annotation" }; + + return await ValidateLayerOrderAsync(layerOrderData, expectedOrder, cancellationToken); + } + catch (Exception ex) + { + return Result.Fail("PARSE_LAYER_ORDER_FAILED", $"解析层顺序配置失败: {ex.Message}"); + } + } + + /// + /// 在包中校验部件编码唯一性。 + /// + private async Task> ValidatePartCodeUniquenessInPackageAsync( + string packageDir, + CancellationToken cancellationToken) + { + var partsFile = Path.Combine(packageDir, "rules", "parts.json"); + + if (!File.Exists(partsFile)) + { + return Result.Fail("PARTS_FILE_NOT_FOUND", "部件配置文件不存在。"); + } + + try + { + var partsContent = await File.ReadAllTextAsync(partsFile, cancellationToken); + var partsData = JsonSerializer.Deserialize(partsContent); + + var partCodes = new List(); + 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.Fail("PARSE_PARTS_FAILED", $"解析部件配置失败: {ex.Message}"); + } + } + + /// + /// 在包中校验Schema。 + /// + private async Task> ValidateSchemaInPackageAsync( + string packageDir, + CancellationToken cancellationToken) + { + var schemaDir = Path.Combine(packageDir, "schema"); + + if (!Directory.Exists(schemaDir)) + { + return Result.Fail("SCHEMA_DIR_NOT_FOUND", "Schema目录不存在。"); + } + + var schemaFiles = Directory.GetFiles(schemaDir, "*.json", SearchOption.AllDirectories); + return await ValidateSchemaAsync(schemaFiles, cancellationToken); + } + + /// + /// 检查是否为有效的JSON。 + /// + private static bool IsValidJson(string jsonString) + { + try + { + JsonDocument.Parse(jsonString); + return true; + } + catch + { + return false; + } + } + + /// + /// 合并错误信息。 + /// + private static string[] CombineErrors( + Result layerOrderResult, + Result partCodeResult, + Result schemaResult) + { + var errors = new List(); + + 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(); + } + + /// + /// 合并警告信息。 + /// + private static string[] CombineWarnings( + Result layerOrderResult, + Result partCodeResult, + Result schemaResult) + { + var warnings = new List(); + + 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 +} diff --git a/OrpaonVision.ConfigApp/Infrastructure/Services/RuntimeDeploymentSnapshotAppService.cs b/OrpaonVision.ConfigApp/Infrastructure/Services/RuntimeDeploymentSnapshotAppService.cs index 58f3117..d23332f 100644 --- a/OrpaonVision.ConfigApp/Infrastructure/Services/RuntimeDeploymentSnapshotAppService.cs +++ b/OrpaonVision.ConfigApp/Infrastructure/Services/RuntimeDeploymentSnapshotAppService.cs @@ -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); } } } diff --git a/OrpaonVision.Core/Training/Contracts/RuleSnapshotValidationResultDto.cs b/OrpaonVision.Core/Training/Contracts/RuleSnapshotValidationResultDto.cs new file mode 100644 index 0000000..8eb73fd --- /dev/null +++ b/OrpaonVision.Core/Training/Contracts/RuleSnapshotValidationResultDto.cs @@ -0,0 +1,120 @@ +namespace OrpaonVision.Core.Training.Contracts; + +/// +/// 规则快照包校验结果。 +/// +public sealed class RuleSnapshotValidationResultDto +{ + /// + /// 是否通过校验。 + /// + public bool IsValid { get; init; } + + /// + /// 校验错误信息。 + /// + public string[] Errors { get; init; } = Array.Empty(); + + /// + /// 校验警告信息。 + /// + public string[] Warnings { get; init; } = Array.Empty(); + + /// + /// 层顺序校验结果。 + /// + public LayerOrderValidationResultDto LayerOrderValidation { get; init; } = new(); + + /// + /// 部件编码唯一性校验结果。 + /// + public PartCodeUniquenessValidationResultDto PartCodeUniquenessValidation { get; init; } = new(); + + /// + /// Schema合法性校验结果。 + /// + public SchemaValidationResultDto SchemaValidation { get; init; } = new(); +} + +/// +/// 层顺序校验结果。 +/// +public sealed class LayerOrderValidationResultDto +{ + /// + /// 是否通过校验。 + /// + public bool IsValid { get; init; } + + /// + /// 错误信息。 + /// + public string[] Errors { get; init; } = Array.Empty(); + + /// + /// 实际层顺序。 + /// + public string[] ActualLayerOrder { get; init; } = Array.Empty(); + + /// + /// 期望层顺序。 + /// + public string[] ExpectedLayerOrder { get; init; } = Array.Empty(); +} + +/// +/// 部件编码唯一性校验结果。 +/// +public sealed class PartCodeUniquenessValidationResultDto +{ + /// + /// 是否通过校验。 + /// + public bool IsValid { get; init; } + + /// + /// 错误信息。 + /// + public string[] Errors { get; init; } = Array.Empty(); + + /// + /// 重复的部件编码。 + /// + public string[] DuplicatePartCodes { get; init; } = Array.Empty(); + + /// + /// 所有部件编码。 + /// + public string[] AllPartCodes { get; init; } = Array.Empty(); +} + +/// +/// Schema合法性校验结果。 +/// +public sealed class SchemaValidationResultDto +{ + /// + /// 是否通过校验。 + /// + public bool IsValid { get; init; } + + /// + /// 错误信息。 + /// + public string[] Errors { get; init; } = Array.Empty(); + + /// + /// 警告信息。 + /// + public string[] Warnings { get; init; } = Array.Empty(); + + /// + /// Schema版本。 + /// + public string SchemaVersion { get; init; } = string.Empty; + + /// + /// 校验的文件路径。 + /// + public string[] ValidatedFiles { get; init; } = Array.Empty(); +} diff --git a/OrpaonVision.Core/Training/IRuleValidationService.cs b/OrpaonVision.Core/Training/IRuleValidationService.cs new file mode 100644 index 0000000..695b497 --- /dev/null +++ b/OrpaonVision.Core/Training/IRuleValidationService.cs @@ -0,0 +1,52 @@ +using OrpaonVision.Core.Results; +using OrpaonVision.Core.Training.Contracts; + +namespace OrpaonVision.Core.Training; + +/// +/// 规则校验服务接口。 +/// +public interface IRuleValidationService +{ + /// + /// 校验规则快照包。 + /// + /// 包路径。 + /// 取消令牌。 + /// 校验结果。 + Task> ValidateRuleSnapshotPackageAsync( + string packagePath, + CancellationToken cancellationToken = default); + + /// + /// 校验层顺序。 + /// + /// 层顺序数据。 + /// 期望的层顺序。 + /// 取消令牌。 + /// 校验结果。 + Task> ValidateLayerOrderAsync( + string[] layerOrderData, + string[] expectedOrder, + CancellationToken cancellationToken = default); + + /// + /// 校验部件编码唯一性。 + /// + /// 部件编码列表。 + /// 取消令牌。 + /// 校验结果。 + Task> ValidatePartCodeUniquenessAsync( + string[] partCodes, + CancellationToken cancellationToken = default); + + /// + /// 校验Schema合法性。 + /// + /// Schema文件路径列表。 + /// 取消令牌。 + /// 校验结果。 + Task> ValidateSchemaAsync( + string[] schemaFiles, + CancellationToken cancellationToken = default); +} diff --git a/OrpaonVision.SiteApp/DependencyInjection/ServiceCollectionExtensions.cs b/OrpaonVision.SiteApp/DependencyInjection/ServiceCollectionExtensions.cs index 4694b0d..fc30f26 100644 --- a/OrpaonVision.SiteApp/DependencyInjection/ServiceCollectionExtensions.cs +++ b/OrpaonVision.SiteApp/DependencyInjection/ServiceCollectionExtensions.cs @@ -71,6 +71,9 @@ namespace OrpaonVision.SiteApp.DependencyInjection services.AddSingleton(); services.AddSingleton(); + // 一致性检查服务 + services.AddSingleton(); + // 日志 services.AddLogging(builder => { diff --git a/OrpaonVision.SiteApp/Runtime/Contracts/ConsistencyCheckContracts.cs b/OrpaonVision.SiteApp/Runtime/Contracts/ConsistencyCheckContracts.cs new file mode 100644 index 0000000..778afc3 --- /dev/null +++ b/OrpaonVision.SiteApp/Runtime/Contracts/ConsistencyCheckContracts.cs @@ -0,0 +1,234 @@ +namespace OrpaonVision.SiteApp.Runtime.Contracts; + +/// +/// 一致性状态枚举。 +/// +public enum ConsistencyStatus +{ + /// + /// 正常。 + /// + Ok, + + /// + /// 警告。 + /// + Warning, + + /// + /// 错误。 + /// + Error +} + +/// +/// 健康状态枚举。 +/// +public enum HealthStatus +{ + /// + /// 健康。 + /// + Healthy, + + /// + /// 降级。 + /// + Degraded, + + /// + /// 不健康。 + /// + Unhealthy +} + +/// +/// 一致性检查结果。 +/// +public sealed class ConsistencyCheckResult +{ + /// + /// 检查时间戳。 + /// + public DateTime CheckTimestamp { get; set; } + + /// + /// 检查状态。 + /// + public ConsistencyStatus Status { get; set; } + + /// + /// 检查消息。 + /// + public string Message { get; set; } = string.Empty; + + /// + /// 相机是否连接。 + /// + public bool CameraConnected { get; set; } + + /// + /// YOLO是否初始化。 + /// + public bool YoloInitialized { get; set; } + + /// + /// 像素格式是否一致。 + /// + public bool PixelFormatConsistent { get; set; } + + /// + /// 像素格式检查详情。 + /// + public List PixelFormatDetails { get; set; } = new(); + + /// + /// 图像尺寸是否一致。 + /// + public bool ImageSizeConsistent { get; set; } + + /// + /// 图像尺寸检查详情。 + /// + public List ImageSizeDetails { get; set; } = new(); + + /// + /// 数据流完整性。 + /// + public bool DataFlowIntegrity { get; set; } + + /// + /// 数据流检查详情。 + /// + public List DataFlowDetails { get; set; } = new(); + + /// + /// 建议列表。 + /// + public List Recommendations { get; set; } = new(); +} + +/// +/// 健康检查结果。 +/// +public sealed class HealthCheckResult +{ + /// + /// 检查时间戳。 + /// + public DateTime Timestamp { get; set; } + + /// + /// 整体状态。 + /// + public HealthStatus OverallStatus { get; set; } + + /// + /// 相机健康状态。 + /// + public ComponentHealth CameraHealth { get; set; } = new(); + + /// + /// YOLO健康状态。 + /// + public ComponentHealth YoloHealth { get; set; } = new(); + + /// + /// 系统健康状态。 + /// + public ComponentHealth SystemHealth { get; set; } = new(); + + /// + /// 健康检查摘要。 + /// + public string Summary { get; set; } = string.Empty; +} + +/// +/// 组件健康状态。 +/// +public sealed class ComponentHealth +{ + /// + /// 组件名称。 + /// + public string ComponentName { get; set; } = string.Empty; + + /// + /// 健康状态。 + /// + public HealthStatus Status { get; set; } + + /// + /// 详细信息。 + /// + public List Details { get; set; } = new(); + + /// + /// 指标数据。 + /// + public Dictionary Metrics { get; set; } = new(); +} + +/// +/// 像素格式检查结果。 +/// +internal sealed class PixelFormatCheckResult +{ + /// + /// 是否一致。 + /// + public bool IsConsistent { get; set; } + + /// + /// 检查详情。 + /// + public List Details { get; set; } = new(); + + /// + /// 建议列表。 + /// + public List Recommendations { get; set; } = new(); +} + +/// +/// 图像尺寸检查结果。 +/// +internal sealed class ImageSizeCheckResult +{ + /// + /// 是否一致。 + /// + public bool IsConsistent { get; set; } + + /// + /// 检查详情。 + /// + public List Details { get; set; } = new(); + + /// + /// 建议列表。 + /// + public List Recommendations { get; set; } = new(); +} + +/// +/// 数据流检查结果。 +/// +internal sealed class DataFlowCheckResult +{ + /// + /// 是否完整。 + /// + public bool IsIntegrity { get; set; } + + /// + /// 检查详情。 + /// + public List Details { get; set; } = new(); + + /// + /// 建议列表。 + /// + public List Recommendations { get; set; } = new(); +} diff --git a/OrpaonVision.SiteApp/Runtime/Contracts/IConsistencyCheckService.cs b/OrpaonVision.SiteApp/Runtime/Contracts/IConsistencyCheckService.cs new file mode 100644 index 0000000..59b4e1e --- /dev/null +++ b/OrpaonVision.SiteApp/Runtime/Contracts/IConsistencyCheckService.cs @@ -0,0 +1,19 @@ +namespace OrpaonVision.SiteApp.Runtime.Contracts; + +/// +/// 一致性检查服务接口。 +/// +public interface IConsistencyCheckService +{ + /// + /// 检查相机与推理服务的一致性。 + /// + /// 一致性检查结果。 + Result CheckCameraInferenceConsistency(); + + /// + /// 获取最小健康信息。 + /// + /// 健康检查结果。 + Result GetMinimalHealthInfo(); +}