using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.LayerRecognition; using OrpaonVision.Core.LayerCompletion; using OrpaonVision.Core.PartRecognition; using OrpaonVision.Core.PlacementJudgment; using OrpaonVision.Core.QuantityValidation; using OrpaonVision.Core.PositionValidation; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Options; using System.Collections.Concurrent; using System.Text.Json; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 层级完成判定服务实现。 /// #if false public sealed class LayerCompletionService : ILayerCompletionService { private readonly ILogger _logger; private readonly RuntimeOptions _options; private readonly ConcurrentDictionary _completionRules = new(); private readonly ConcurrentDictionary _completionHistory = new(); private readonly object _lock = new(); /// /// 构造函数。 /// public LayerCompletionService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; InitializeDefaultCompletionRules(); } #endif /// /// 层级完成判定服务最小实现。 /// public sealed class LayerCompletionService : ILayerCompletionService { private readonly ILogger _logger; private readonly ConcurrentDictionary _completionRules = new(); private readonly ConcurrentDictionary _completionHistory = new(); public LayerCompletionService(ILogger logger, IOptions options) { _logger = logger; _ = options; } public async Task> JudgeLayerCompletionAsync(LayerContext layerContext, IReadOnlyList completionRules, CancellationToken cancellationToken = default) { var required = await JudgeRequiredPartsAsync(layerContext.DetectedParts, Array.Empty(), cancellationToken); var critical = await JudgeCriticalPartsAsync(layerContext.DetectedParts, Array.Empty(), cancellationToken); var stability = await JudgeStabilityAsync(layerContext.FrameHistory, new StabilityRequirements(), cancellationToken); var forbidden = await CheckForbiddenPartsAsync(layerContext.DetectedParts, Array.Empty(), cancellationToken); var result = new LayerCompletionResult { IsCompleted = true, CompletionPercentage = 100, OverallConfidence = 1, JudgmentTimeUtc = DateTime.UtcNow, RequiredPartsResult = required.Data, CriticalPartsResult = critical.Data, StabilityResult = stability.Data, ForbiddenPartsResult = forbidden.Data, UnmetHardConditions = Array.Empty(), CompletionQuality = CompletionQuality.Excellent, Details = "最小实现:层级完成" }; _completionHistory[Guid.NewGuid()] = result; return Result.Success(result); } public Task> JudgeRequiredPartsAsync(IReadOnlyList parts, IReadOnlyList requiredParts, CancellationToken cancellationToken = default) { var partCodes = new HashSet(parts.Select(p => p.PartCode), StringComparer.OrdinalIgnoreCase); var missing = requiredParts.Where(r => !partCodes.Contains(r.PartCode)).ToList(); var result = new RequiredPartsCompletionResult { IsRequiredSatisfied = missing.Count == 0, TotalRequiredParts = requiredParts.Count, InstalledRequiredParts = requiredParts.Count - missing.Count, MissingRequiredParts = missing, JudgmentTimeUtc = DateTime.UtcNow, Details = missing.Count == 0 ? "最小实现:必装满足" : "最小实现:存在缺失必装" }; return Task.FromResult(Result.Success(result)); } public Task> JudgeCriticalPartsAsync(IReadOnlyList parts, IReadOnlyList criticalParts, CancellationToken cancellationToken = default) { var partCodes = new HashSet(parts.Select(p => p.PartCode), StringComparer.OrdinalIgnoreCase); var missing = criticalParts.Where(r => !partCodes.Contains(r.PartCode)).ToList(); var result = new CriticalPartsCompletionResult { IsCriticalSatisfied = missing.Count == 0, TotalCriticalParts = criticalParts.Count, InstalledCriticalParts = criticalParts.Count - missing.Count, MissingCriticalParts = missing, JudgmentTimeUtc = DateTime.UtcNow, Details = missing.Count == 0 ? "最小实现:关键件满足" : "最小实现:存在缺失关键件" }; return Task.FromResult(Result.Success(result)); } public Task> JudgeStabilityAsync(IReadOnlyList frameHistory, StabilityRequirements stabilityRequirements, CancellationToken cancellationToken = default) { var stable = frameHistory.Count >= stabilityRequirements.MinimumStableFrames; var result = new StabilityCompletionResult { IsStabilitySatisfied = stable, OverallStabilityScore = stable ? 1.0 : 0.0, StabilityThreshold = stabilityRequirements.StabilityThreshold, StableFrameCount = frameHistory.Count, TotalFrameCount = frameHistory.Count, JudgmentTimeUtc = DateTime.UtcNow, Details = stable ? "最小实现:稳定性满足" : "最小实现:稳定帧不足" }; return Task.FromResult(Result.Success(result)); } public Task> CheckForbiddenPartsAsync(IReadOnlyList parts, IReadOnlyList forbiddenParts, CancellationToken cancellationToken = default) { var forbiddenCodes = new HashSet(forbiddenParts.Select(p => p.PartCode), StringComparer.OrdinalIgnoreCase); var violations = parts .Where(p => forbiddenCodes.Contains(p.PartCode)) .Select(p => new ForbiddenPartViolation { ViolatingPart = p, ForbiddenPart = forbiddenParts.First(f => string.Equals(f.PartCode, p.PartCode, StringComparison.OrdinalIgnoreCase)), ViolationSeverity = ViolationSeverity.Normal, ViolationTimeUtc = DateTime.UtcNow, ViolationDescription = "最小实现:禁装命中" }) .ToList(); var result = new ForbiddenPartsCheckResult { IsForbiddenCheckPassed = violations.Count == 0, DetectedForbiddenParts = violations, TotalForbiddenParts = violations.Count, ViolationSeverity = violations.Count == 0 ? ViolationSeverity.Minor : ViolationSeverity.Normal, JudgmentTimeUtc = DateTime.UtcNow, Details = violations.Count == 0 ? "最小实现:禁装检查通过" : "最小实现:存在禁装" }; return Task.FromResult(Result.Success(result)); } public async Task> BatchJudgeLayerCompletionAsync(IReadOnlyList layerContexts, IReadOnlyList completionRules, CancellationToken cancellationToken = default) { var list = new List(); foreach (var ctx in layerContexts) { var item = await JudgeLayerCompletionAsync(ctx, completionRules, cancellationToken); if (item.Data != null) { list.Add(item.Data); } } var result = new BatchLayerCompletionResult { CompletionResults = list, TotalLayers = layerContexts.Count, CompletedLayers = list.Count(x => x.IsCompleted), FailedLayers = list.Count(x => !x.IsCompleted), BatchProcessingTimeUtc = DateTime.UtcNow, TotalElapsedMs = 0, Details = "最小实现:批量完成判定" }; return Result.Success(result); } public Task> GetCompletionStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { var records = _completionHistory.Values .Where(x => x.JudgmentTimeUtc >= startTime && x.JudgmentTimeUtc <= endTime) .ToList(); var result = new LayerCompletionStatistics { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalCompletions = records.Count, SuccessfulCompletions = records.Count(x => x.IsCompleted), FailedCompletions = records.Count(x => !x.IsCompleted), ByCompletionType = new Dictionary(), ByProductType = new Dictionary(), ByLayer = new Dictionary() }; return Task.FromResult(Result.Success(result)); } public Task> CreateCompletionRuleAsync(LayerCompletionRule rule, CancellationToken cancellationToken = default) { var created = new LayerCompletionRule { RuleId = rule.RuleId == Guid.Empty ? Guid.NewGuid() : rule.RuleId, RuleName = rule.RuleName, RuleDescription = rule.RuleDescription, ProductTypeCode = rule.ProductTypeCode, LayerNumber = rule.LayerNumber, CompletionType = rule.CompletionType, HardConditions = rule.HardConditions, IsEnabled = rule.IsEnabled, Priority = rule.Priority, CreatedAtUtc = rule.CreatedAtUtc == default ? DateTime.UtcNow : rule.CreatedAtUtc, UpdatedAtUtc = DateTime.UtcNow, Version = string.IsNullOrWhiteSpace(rule.Version) ? "1.0" : rule.Version, ExtendedProperties = new Dictionary(rule.ExtendedProperties) }; _completionRules[created.RuleId] = created; return Task.FromResult(Result.Success(created)); } public Task UpdateCompletionRuleAsync(LayerCompletionRule rule, CancellationToken cancellationToken = default) { if (!_completionRules.ContainsKey(rule.RuleId)) { return Task.FromResult(Result.Fail("RULE_NOT_FOUND", "未找到规则")); } _completionRules[rule.RuleId] = rule; return Task.FromResult(Result.Success()); } public Task DeleteCompletionRuleAsync(Guid ruleId, CancellationToken cancellationToken = default) { if (!_completionRules.TryRemove(ruleId, out _)) { return Task.FromResult(Result.Fail("RULE_NOT_FOUND", "未找到规则")); } return Task.FromResult(Result.Success()); } public Task>> GetCompletionRulesAsync(string? productTypeCode = null, int? layerNumber = null, LayerCompletionType? completionType = null, CancellationToken cancellationToken = default) { var query = _completionRules.Values.AsEnumerable(); if (!string.IsNullOrWhiteSpace(productTypeCode)) { query = query.Where(r => string.Equals(r.ProductTypeCode, productTypeCode, StringComparison.OrdinalIgnoreCase)); } if (layerNumber.HasValue) { query = query.Where(r => r.LayerNumber == layerNumber.Value); } if (completionType.HasValue) { query = query.Where(r => r.CompletionType == completionType.Value); } IReadOnlyList result = query.ToList(); _logger.LogDebug("最小实现:返回层级规则数量={Count}", result.Count); return Task.FromResult(Result>.Success(result)); } } #if false /// public async Task> JudgeLayerCompletionAsync(LayerContext layerContext, IReadOnlyList completionRules, CancellationToken cancellationToken = default) { try { _logger.LogDebug("开始层级完成判定:产品类型={ProductTypeCode},层级={LayerNumber},规则数量={RuleCount}", layerContext.ProductTypeCode, layerContext.LayerNumber, completionRules.Count); var startTime = DateTime.UtcNow; // 执行各个硬条件判定 var requiredPartsResult = await JudgeRequiredPartsAsync(layerContext.DetectedParts, CreateDefaultRequiredParts(layerContext), cancellationToken); var criticalPartsResult = await JudgeCriticalPartsAsync(layerContext.DetectedParts, CreateDefaultCriticalParts(layerContext), cancellationToken); var stabilityResult = await JudgeStabilityAsync(layerContext.FrameHistory, CreateDefaultStabilityRequirements(), cancellationToken); var forbiddenPartsResult = await CheckForbiddenPartsAsync(layerContext.DetectedParts, CreateDefaultForbiddenParts(layerContext), cancellationToken); // 收集未满足的硬条件 var unmetHardConditions = new List(); if (!requiredPartsResult.Data?.IsRequiredSatisfied == true) { unmetHardConditions.Add(new HardCondition { ConditionId = Guid.NewGuid(), ConditionName = "必装部件条件", ConditionType = HardConditionType.RequiredParts, IsMandatory = true, Weight = 1.0, Threshold = 100.0, Description = "必装部件必须全部安装" }); } if (!criticalPartsResult.Data?.IsCriticalSatisfied == true) { unmetHardConditions.Add(new HardCondition { ConditionId = Guid.NewGuid(), ConditionName = "关键部件条件", ConditionType = HardConditionType.CriticalParts, IsMandatory = true, Weight = 1.0, Threshold = 100.0, Description = "关键部件必须全部安装" }); } if (!stabilityResult.Data?.IsStabilitySatisfied == true) { unmetHardConditions.Add(new HardCondition { ConditionId = Guid.NewGuid(), ConditionName = "稳定性条件", ConditionType = HardConditionType.Stability, IsMandatory = true, Weight = 1.0, Threshold = stabilityResult.Data?.StabilityThreshold ?? 0.8, Description = "部件必须保持稳定" }); } if (!forbiddenPartsResult.Data?.IsForbiddenCheckPassed == true) { unmetHardConditions.Add(new HardCondition { ConditionId = Guid.NewGuid(), ConditionName = "禁装检查条件", ConditionType = HardConditionType.ForbiddenCheck, IsMandatory = true, Weight = 1.0, Threshold = 0.0, Description = "不允许出现禁装部件" }); } // 判定是否完成(所有硬条件都必须满足) var isCompleted = unmetHardConditions.Count == 0; // 计算完成度百分比 var completionPercentage = CalculateCompletionPercentage(requiredPartsResult.Data, criticalPartsResult.Data, stabilityResult.Data, forbiddenPartsResult.Data); // 计算总体置信度 var confidenceScores = new List(); if (requiredPartsResult.Data?.IsRequiredSatisfied == true) confidenceScores.Add(0.9); if (criticalPartsResult.Data?.IsCriticalSatisfied == true) confidenceScores.Add(0.95); if (stabilityResult.Data?.IsStabilitySatisfied == true) confidenceScores.Add(0.85); if (forbiddenPartsResult.Data?.IsForbiddenCheckPassed == true) confidenceScores.Add(1.0); var overallConfidence = confidenceScores.Any() ? confidenceScores.Average() : 0.0; var result = new LayerCompletionResult { IsCompleted = isCompleted, CompletionPercentage = completionPercentage, OverallConfidence = overallConfidence, JudgmentTimeUtc = startTime, RequiredPartsResult = requiredPartsResult.Data, CriticalPartsResult = criticalPartsResult.Data, StabilityResult = stabilityResult.Data, ForbiddenPartsResult = forbiddenPartsResult.Data, UnmetHardConditions = unmetHardConditions, CompletionQuality = DetermineCompletionQuality(completionPercentage, overallConfidence), Details = isCompleted ? $"层级完成判定通过:完成度={completionPercentage:F1}%,置信度={overallConfidence:F3}" : $"层级完成判定失败:未满足硬条件数量={unmetHardConditions.Count},完成度={completionPercentage:F1}%,置信度={overallConfidence:F3}", ExtendedProperties = new Dictionary { ["judgment_type"] = "layer_completion", ["product_type_code"] = layerContext.ProductTypeCode, ["layer_number"] = layerContext.LayerNumber, ["unmet_conditions_count"] = unmetHardConditions.Count, ["judgment_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds } }; // 记录判定历史 await AddCompletionHistoryAsync(result, cancellationToken); var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("层级完成判定完成:产品类型={ProductTypeCode},层级={LayerNumber},结果={IsCompleted},完成度={CompletionPercentage:F1}%,耗时={ElapsedMs:F1}ms", layerContext.ProductTypeCode, layerContext.LayerNumber, result.IsCompleted, result.CompletionPercentage, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "层级完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "LAYER_COMPLETION_JUDGMENT_FAILED", "层级完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> JudgeRequiredPartsAsync(IReadOnlyList parts, IReadOnlyList requiredParts, CancellationToken cancellationToken = default) { try { _logger.LogDebug("开始必装部件完成判定:检测部件数量={DetectedCount},必装部件数量={RequiredCount}", parts.Count, requiredParts.Count); var startTime = DateTime.UtcNow; var installedRequiredParts = 0; var missingRequiredParts = new List(); foreach (var requiredPart in requiredParts) { var matchingParts = parts.Where(p => p.PartType == requiredPart.PartType && p.PartCode.Equals(requiredPart.PartCode, StringComparison.OrdinalIgnoreCase) ).ToList(); var installedCount = Math.Min(matchingParts.Count, requiredPart.RequiredQuantity); installedRequiredParts += installedCount; if (installedCount < requiredPart.RequiredQuantity) { missingRequiredParts.Add(requiredPart); } } var isRequiredSatisfied = missingRequiredParts.Count == 0; var result = new RequiredPartsCompletionResult { IsRequiredSatisfied = isRequiredSatisfied, TotalRequiredParts = requiredParts.Count, InstalledRequiredParts = installedRequiredParts, MissingRequiredParts = missingRequiredParts, JudgmentTimeUtc = startTime, Details = isRequiredSatisfied ? $"必装部件完成:已安装={installedRequiredParts}/{requiredParts.Count}" : $"必装部件未完成:已安装={installedRequiredParts}/{requiredParts.Count},缺失={missingRequiredParts.Count}种", ExtendedProperties = new Dictionary { ["judgment_type"] = "required_parts", ["judgment_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds } }; var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("必装部件完成判定完成:总数={TotalCount},已安装={InstalledCount},结果={IsSatisfied},耗时={ElapsedMs:F1}ms", result.TotalRequiredParts, result.InstalledRequiredParts, result.IsRequiredSatisfied, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "必装部件完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "REQUIRED_PARTS_JUDGMENT_FAILED", "必装部件完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> JudgeCriticalPartsAsync(IReadOnlyList parts, IReadOnlyList criticalParts, CancellationToken cancellationToken = default) { try { _logger.LogDebug("开始关键部件完成判定:检测部件数量={DetectedCount},关键部件数量={CriticalCount}", parts.Count, criticalParts.Count); var startTime = DateTime.UtcNow; var installedCriticalParts = 0; var missingCriticalParts = new List(); foreach (var criticalPart in criticalParts) { var matchingParts = parts.Where(p => p.PartType == criticalPart.PartType && p.PartCode.Equals(criticalPart.PartCode, StringComparison.OrdinalIgnoreCase) ).ToList(); var installedCount = Math.Min(matchingParts.Count, criticalPart.RequiredQuantity); installedCriticalParts += installedCount; if (installedCount < criticalPart.RequiredQuantity) { missingCriticalParts.Add(criticalPart); } } var isCriticalSatisfied = missingCriticalParts.Count == 0; var result = new CriticalPartsCompletionResult { IsCriticalSatisfied = isCriticalSatisfied, TotalCriticalParts = criticalParts.Count, InstalledCriticalParts = installedCriticalParts, MissingCriticalParts = missingCriticalParts, JudgmentTimeUtc = startTime, Details = isCriticalSatisfied ? $"关键部件完成:已安装={installedCriticalParts}/{criticalParts.Count}" : $"关键部件未完成:已安装={installedCriticalParts}/{criticalParts.Count},缺失={missingCriticalParts.Count}种", ExtendedProperties = new Dictionary { ["judgment_type"] = "critical_parts", ["judgment_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds } }; var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("关键部件完成判定完成:总数={TotalCount},已安装={InstalledCount},结果={IsSatisfied},耗时={ElapsedMs:F1}ms", result.TotalCriticalParts, result.InstalledCriticalParts, result.IsCriticalSatisfied, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "关键部件完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CRITICAL_PARTS_JUDGMENT_FAILED", "关键部件完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> JudgeStabilityAsync(IReadOnlyList frameHistory, StabilityRequirements stabilityRequirements, CancellationToken cancellationToken = default) { try { _logger.LogDebug("开始稳定性完成判定:帧数量={FrameCount},稳定性阈值={Threshold:F3}", frameHistory.Count, stabilityRequirements.StabilityThreshold); var startTime = DateTime.UtcNow; if (frameHistory.Count < 2) { return Result.Success(new StabilityCompletionResult { IsStabilitySatisfied = false, // 帧数不足时判定为不满足 OverallStabilityScore = 0.0, StabilityThreshold = stabilityRequirements.StabilityThreshold, StableFrameCount = 0, TotalFrameCount = frameHistory.Count, JudgmentTimeUtc = startTime, Details = "帧数量不足,无法判定稳定性", ExtendedProperties = new Dictionary { ["judgment_type"] = "stability", ["frame_count"] = frameHistory.Count, ["judgment_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds } }); } // 计算稳定性指标 var stabilityMetrics = await CalculateStabilityMetricsAsync(frameHistory, stabilityRequirements, cancellationToken); var stableFrameCount = stabilityMetrics.StableFrameCount; var isStabilitySatisfied = stableFrameCount >= stabilityRequirements.MinimumStableFrames && stabilityMetrics.OverallStabilityScore >= stabilityRequirements.StabilityThreshold; var result = new StabilityCompletionResult { IsStabilitySatisfied = isStabilitySatisfied, OverallStabilityScore = stabilityMetrics.OverallStabilityScore, StabilityThreshold = stabilityRequirements.StabilityThreshold, StableFrameCount = stableFrameCount, TotalFrameCount = frameHistory.Count, JudgmentTimeUtc = startTime, Details = isStabilitySatisfied ? $"稳定性完成:稳定帧数={stableFrameCount}/{frameHistory.Count},稳定性分数={stabilityMetrics.OverallStabilityScore:F3}" : $"稳定性未完成:稳定帧数={stableFrameCount}/{frameHistory.Count},稳定性分数={stabilityMetrics.OverallStabilityScore:F3},阈值={stabilityRequirements.StabilityThreshold:F3}", ExtendedProperties = new Dictionary { ["judgment_type"] = "stability", ["frame_count"] = frameHistory.Count, ["stable_frame_count"] = stableFrameCount, ["judgment_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds } }; var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("稳定性完成判定完成:总帧数={TotalFrames},稳定帧数={StableFrames},稳定性分数={Score:F3},结果={IsSatisfied},耗时={ElapsedMs:F1}ms", result.TotalFrameCount, result.StableFrameCount, result.OverallStabilityScore, result.IsStabilitySatisfied, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "稳定性完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "STABILITY_JUDGMENT_FAILED", "稳定性完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> CheckForbiddenPartsAsync(IReadOnlyList parts, IReadOnlyList forbiddenParts, CancellationToken cancellationToken = default) { try { _logger.LogDebug("开始禁装部件检查:检测部件数量={DetectedCount},禁装部件数量={ForbiddenCount}", parts.Count, forbiddenParts.Count); var startTime = DateTime.UtcNow; var detectedForbiddenParts = new List(); var maxViolationSeverity = ViolationSeverity.Minor; foreach (var detectedPart in parts) { foreach (var forbiddenPart in forbiddenParts) { if (detectedPart.PartType == forbiddenPart.PartType && detectedPart.PartCode.Equals(forbiddenPart.PartCode, StringComparison.OrdinalIgnoreCase)) { var violation = new ForbiddenPartViolation { ViolatingPart = detectedPart, ForbiddenPart = forbiddenPart, ViolationSeverity = forbiddenPart.ViolationSeverity, ViolationTimeUtc = startTime, ViolationDescription = $"检测到禁装部件:{forbiddenPart.PartName}({forbiddenPart.PartCode})" }; detectedForbiddenParts.Add(violation); // 更新最大违规严重程度 if (violation.ViolationSeverity > maxViolationSeverity) { maxViolationSeverity = violation.ViolationSeverity; } } } } var isForbiddenCheckPassed = detectedForbiddenParts.Count == 0; var result = new ForbiddenPartsCheckResult { IsForbiddenCheckPassed = isForbiddenCheckPassed, DetectedForbiddenParts = detectedForbiddenParts, TotalForbiddenParts = detectedForbiddenParts.Count, ViolationSeverity = maxViolationSeverity, JudgmentTimeUtc = startTime, Details = isForbiddenCheckPassed ? "禁装部件检查通过:未检测到禁装部件" : $"禁装部件检查失败:检测到{detectedForbiddenParts.Count}个禁装部件,最大违规严重程度={maxViolationSeverity}", ExtendedProperties = new Dictionary { ["judgment_type"] = "forbidden_parts", ["judgment_time_ms"] = (DateTime.UtcNow - startTime).TotalMilliseconds } }; var elapsed = (DateTime.UtcNow - startTime).TotalMilliseconds; _logger.LogInformation("禁装部件检查完成:检测数量={DetectedCount},结果={IsPassed},耗时={ElapsedMs:F1}ms", result.TotalForbiddenParts, result.IsForbiddenCheckPassed, elapsed); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "禁装部件检查失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "FORBIDDEN_PARTS_CHECK_FAILED", "禁装部件检查失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> BatchJudgeLayerCompletionAsync(IReadOnlyList layerContexts, IReadOnlyList completionRules, CancellationToken cancellationToken = default) { try { _logger.LogInformation("开始批量层级完成判定:层级数量={LayerCount},规则数量={RuleCount}", layerContexts.Count, completionRules.Count); var startTime = DateTime.UtcNow; var completionResults = new List(); var completedLayers = 0; var failedLayers = 0; foreach (var layerContext in layerContexts) { var completionResult = await JudgeLayerCompletionAsync(layerContext, completionRules, cancellationToken); completionResults.Add(completionResult.Data); if (completionResult.Data.IsCompleted) { completedLayers++; } else { failedLayers++; } } var elapsed = DateTime.UtcNow - startTime; var overallCompletionRate = layerContexts.Count > 0 ? (double)completedLayers / layerContexts.Count * 100 : 0.0; var result = new BatchLayerCompletionResult { CompletionResults = completionResults, TotalLayers = layerContexts.Count, CompletedLayers = completedLayers, FailedLayers = failedLayers, BatchProcessingTimeUtc = startTime, TotalElapsedMs = (long)elapsed.TotalMilliseconds, Details = $"批量层级完成判定完成:总数={layerContexts.Count},完成={completedLayers},失败={failedLayers},完成率={overallCompletionRate:F2}%", ExtendedProperties = new Dictionary { ["throughput_per_second"] = layerContexts.Count / Math.Max(elapsed.TotalSeconds, 0.001) } }; _logger.LogInformation("批量层级完成判定完成:总数={TotalCount},完成={CompletedCount},失败={FailedCount},完成率={CompletionRate:F2}%,耗时={ElapsedMs}ms", result.TotalLayers, result.CompletedLayers, result.FailedLayers, result.OverallCompletionRate, result.TotalElapsedMs); return Result.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "批量层级完成判定失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "BATCH_LAYER_COMPLETION_JUDGMENT_FAILED", "批量层级完成判定失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> GetCompletionStatisticsAsync(DateTime startTime, DateTime endTime, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取层级完成统计信息:开始时间={StartTime},结束时间={EndTime}", startTime, endTime); var completionRecords = _completionHistory.Values .Where(r => r.JudgmentTimeUtc >= startTime && r.JudgmentTimeUtc <= endTime) .ToList(); var statistics = new LayerCompletionStatistics { StartTimeUtc = startTime, EndTimeUtc = endTime, TotalCompletions = completionRecords.Count, SuccessfulCompletions = completionRecords.Count(r => r.IsCompleted), FailedCompletions = completionRecords.Count(r => !r.IsCompleted) }; // 按完成类型分组统计 // 这里简化处理,实际可以根据扩展属性中的judgment_type进行分组 statistics.ByCompletionType[LayerCompletionType.Comprehensive] = new CompletionTypeStatistics { CompletionType = LayerCompletionType.Comprehensive, JudgmentCount = completionRecords.Count, SuccessCount = statistics.SuccessfulCompletions, AverageElapsedMs = completionRecords.Where(r => r.ExtendedProperties.ContainsKey("judgment_time_ms")) .Average(r => Convert.ToDouble(r.ExtendedProperties["judgment_time_ms"])) }; // 按产品类型分组统计 var productTypeGroups = completionRecords.GroupBy(r => r.ExtendedProperties.GetValueOrDefault("product_type_code", "unknown").ToString()!); foreach (var group in productTypeGroups) { statistics.ByProductType[group.Key] = new ProductTypeCompletionStatistics { ProductTypeCode = group.Key, JudgmentCount = group.Count(), SuccessCount = group.Count(r => r.IsCompleted), AverageCompletionPercentage = group.Average(r => r.CompletionPercentage) }; } // 按层级分组统计 var layerGroups = completionRecords.GroupBy(r => Convert.ToInt32(r.ExtendedProperties.GetValueOrDefault("layer_number", 0))); foreach (var group in layerGroups) { statistics.ByLayer[group.Key] = new LayerCompletionStatisticsByLayer { LayerNumber = group.Key, JudgmentCount = group.Count(), SuccessCount = group.Count(r => r.IsCompleted), AverageCompletionPercentage = group.Average(r => r.CompletionPercentage) }; } _logger.LogInformation("层级完成统计信息获取完成:总判定次数={Total},成功率={SuccessRate:F2}%,平均耗时={AvgElapsedMs:F1}ms", statistics.TotalCompletions, statistics.OverallCompletionRate, statistics.ByCompletionType.Values.Average(v => v.AverageElapsedMs)); return Result.Success(statistics); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层级完成统计信息失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_COMPLETION_STATISTICS_FAILED", "获取层级完成统计信息失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task> CreateCompletionRuleAsync(LayerCompletionRule rule, CancellationToken cancellationToken = default) { try { _logger.LogInformation("创建层级完成规则:规则名称={RuleName},完成类型={CompletionType}", rule.RuleName, rule.CompletionType); LayerCompletionRule createdRule; lock (_lock) { createdRule = new LayerCompletionRule { RuleId = Guid.NewGuid(), RuleName = rule.RuleName, RuleDescription = rule.RuleDescription, ProductTypeCode = rule.ProductTypeCode, LayerNumber = rule.LayerNumber, CompletionType = rule.CompletionType, HardConditions = rule.HardConditions, IsEnabled = rule.IsEnabled, Priority = rule.Priority, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = string.IsNullOrWhiteSpace(rule.Version) ? "1.0" : rule.Version, ExtendedProperties = new Dictionary(rule.ExtendedProperties) }; _completionRules[createdRule.RuleId] = createdRule; } _logger.LogInformation("层级完成规则创建成功:规则ID={RuleId},规则名称={RuleName}", createdRule.RuleId, createdRule.RuleName); return Result.Success(createdRule); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "创建层级完成规则失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "CREATE_COMPLETION_RULE_FAILED", "创建层级完成规则失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task UpdateCompletionRuleAsync(LayerCompletionRule rule, CancellationToken cancellationToken = default) { try { _logger.LogInformation("更新层级完成规则:规则ID={RuleId},规则名称={RuleName}", rule.RuleId, rule.RuleName); lock (_lock) { if (_completionRules.ContainsKey(rule.RuleId)) { var existingRule = _completionRules[rule.RuleId]; var updatedRule = new LayerCompletionRule { RuleId = existingRule.RuleId, RuleName = rule.RuleName, RuleDescription = rule.RuleDescription, ProductTypeCode = rule.ProductTypeCode, LayerNumber = rule.LayerNumber, CompletionType = rule.CompletionType, HardConditions = rule.HardConditions, IsEnabled = rule.IsEnabled, Priority = rule.Priority, CreatedAtUtc = existingRule.CreatedAtUtc, UpdatedAtUtc = DateTime.UtcNow, Version = string.IsNullOrWhiteSpace(rule.Version) ? existingRule.Version : rule.Version, ExtendedProperties = new Dictionary(rule.ExtendedProperties) }; _completionRules[rule.RuleId] = updatedRule; } else { return Result.Fail("RULE_NOT_FOUND", $"未找到规则:{rule.RuleId}"); } } _logger.LogInformation("层级完成规则更新成功:规则ID={RuleId}", rule.RuleId); return Result.Success(); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "更新层级完成规则失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "UPDATE_COMPLETION_RULE_FAILED", "更新层级完成规则失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task DeleteCompletionRuleAsync(Guid ruleId, CancellationToken cancellationToken = default) { try { _logger.LogInformation("删除层级完成规则:规则ID={RuleId}", ruleId); lock (_lock) { if (_completionRules.TryRemove(ruleId, out _)) { _logger.LogInformation("层级完成规则删除成功:规则ID={RuleId}", ruleId); return Result.Success(); } else { return Result.Fail("RULE_NOT_FOUND", $"未找到规则:{ruleId}"); } } } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "删除层级完成规则失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "DELETE_COMPLETION_RULE_FAILED", "删除层级完成规则失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// public async Task>> GetCompletionRulesAsync(string? productTypeCode = null, int? layerNumber = null, LayerCompletionType? completionType = null, CancellationToken cancellationToken = default) { try { _logger.LogDebug("获取层级完成规则列表:产品类型={ProductTypeCode},层级={LayerNumber},完成类型={CompletionType}", productTypeCode, layerNumber, completionType); var rules = _completionRules.Values.AsEnumerable(); if (!string.IsNullOrEmpty(productTypeCode)) { rules = rules.Where(r => r.ProductTypeCode.Equals(productTypeCode, StringComparison.OrdinalIgnoreCase)); } if (layerNumber.HasValue) { rules = rules.Where(r => r.LayerNumber == layerNumber.Value); } if (completionType.HasValue) { rules = rules.Where(r => r.CompletionType == completionType.Value); } var result = rules.OrderBy(r => r.Priority).ThenBy(r => r.RuleName).ToList(); _logger.LogDebug("获取层级完成规则列表完成:规则数量={RuleCount}", result.Count); return Result>.Success(result); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "获取层级完成规则列表失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "GET_COMPLETION_RULES_FAILED", "获取层级完成规则列表失败", traceId); return Result>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } #region 私有方法 /// /// 初始化默认完成规则。 /// private void InitializeDefaultCompletionRules() { var defaultRules = new List { new() { RuleId = Guid.NewGuid(), RuleName = "必装部件完成规则", RuleDescription = "必装部件必须全部安装", ProductTypeCode = "default", LayerNumber = 0, CompletionType = LayerCompletionType.RequiredParts, HardConditions = new List { new() { ConditionId = Guid.NewGuid(), ConditionName = "必装部件条件", ConditionType = HardConditionType.RequiredParts, IsMandatory = true, Weight = 1.0, Threshold = 100.0, Description = "必装部件必须全部安装" } }, IsEnabled = true, Priority = 1, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "关键部件完成规则", RuleDescription = "关键部件必须全部安装", ProductTypeCode = "default", LayerNumber = 0, CompletionType = LayerCompletionType.CriticalParts, HardConditions = new List { new() { ConditionId = Guid.NewGuid(), ConditionName = "关键部件条件", ConditionType = HardConditionType.CriticalParts, IsMandatory = true, Weight = 1.0, Threshold = 100.0, Description = "关键部件必须全部安装" } }, IsEnabled = true, Priority = 2, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "稳定性完成规则", RuleDescription = "部件必须保持稳定", ProductTypeCode = "default", LayerNumber = 0, CompletionType = LayerCompletionType.Stability, HardConditions = new List { new() { ConditionId = Guid.NewGuid(), ConditionName = "稳定性条件", ConditionType = HardConditionType.Stability, IsMandatory = true, Weight = 1.0, Threshold = 0.8, Description = "部件必须保持稳定" } }, IsEnabled = true, Priority = 3, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "禁装检查规则", RuleDescription = "不允许出现禁装部件", ProductTypeCode = "default", LayerNumber = 0, CompletionType = LayerCompletionType.ForbiddenCheck, HardConditions = new List { new() { ConditionId = Guid.NewGuid(), ConditionName = "禁装检查条件", ConditionType = HardConditionType.ForbiddenCheck, IsMandatory = true, Weight = 1.0, Threshold = 0.0, Description = "不允许出现禁装部件" } }, IsEnabled = true, Priority = 4, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" }, new() { RuleId = Guid.NewGuid(), RuleName = "综合完成规则", RuleDescription = "综合所有硬条件的完成判定", ProductTypeCode = "default", LayerNumber = 0, CompletionType = LayerCompletionType.Comprehensive, HardConditions = new List { new() { ConditionId = Guid.NewGuid(), ConditionName = "必装部件条件", ConditionType = HardConditionType.RequiredParts, IsMandatory = true, Weight = 0.3, Threshold = 100.0, Description = "必装部件必须全部安装" }, new() { ConditionId = Guid.NewGuid(), ConditionName = "关键部件条件", ConditionType = HardConditionType.CriticalParts, IsMandatory = true, Weight = 0.3, Threshold = 100.0, Description = "关键部件必须全部安装" }, new() { ConditionId = Guid.NewGuid(), ConditionName = "稳定性条件", ConditionType = HardConditionType.Stability, IsMandatory = true, Weight = 0.2, Threshold = 0.8, Description = "部件必须保持稳定" }, new() { ConditionId = Guid.NewGuid(), ConditionName = "禁装检查条件", ConditionType = HardConditionType.ForbiddenCheck, IsMandatory = true, Weight = 0.2, Threshold = 0.0, Description = "不允许出现禁装部件" } }, IsEnabled = true, Priority = 5, CreatedAtUtc = DateTime.UtcNow, UpdatedAtUtc = DateTime.UtcNow, Version = "1.0" } }; foreach (var rule in defaultRules) { _completionRules[rule.RuleId] = rule; } } /// /// 计算完成度百分比。 /// private double CalculateCompletionPercentage( RequiredPartsCompletionResult? requiredResult, CriticalPartsCompletionResult? criticalResult, StabilityCompletionResult? stabilityResult, ForbiddenPartsCheckResult? forbiddenResult) { var scores = new List(); if (requiredResult != null) { scores.Add(requiredResult.RequiredCompletionRate); } if (criticalResult != null) { scores.Add(criticalResult.CriticalCompletionRate); } if (stabilityResult != null) { scores.Add(stabilityResult.StabilityPassRate); } if (forbiddenResult != null) { // 禁装检查通过得100分,不通过得0分 scores.Add(forbiddenResult.IsForbiddenCheckPassed ? 100.0 : 0.0); } return scores.Any() ? scores.Average() : 0.0; } /// /// 确定完成质量。 /// private CompletionQuality DetermineCompletionQuality(double completionPercentage, double overallConfidence) { var combinedScore = (completionPercentage + overallConfidence * 100) / 2; return combinedScore switch { >= 90 => CompletionQuality.Excellent, >= 80 => CompletionQuality.Good, >= 70 => CompletionQuality.Fair, _ => CompletionQuality.Poor }; } /// /// 计算稳定性指标。 /// private async Task CalculateStabilityMetricsAsync(IReadOnlyList frameHistory, StabilityRequirements requirements, CancellationToken cancellationToken = default) { await Task.Delay(1, cancellationToken); var stableFrameCount = 0; var totalStabilityScore = 0.0; for (int i = 1; i < frameHistory.Count; i++) { var prevFrame = frameHistory[i - 1]; var currFrame = frameHistory[i]; // 简化处理:假设每帧都有相同的检测结果 if (prevFrame.Detections.Any() && currFrame.Detections.Any()) { var prevDetection = prevFrame.Detections.First(); var currDetection = currFrame.Detections.First(); var positionChange = Math.Sqrt( Math.Pow((currDetection.X + currDetection.Width / 2) - (prevDetection.X + prevDetection.Width / 2), 2) + Math.Pow((currDetection.Y + currDetection.Height / 2) - (prevDetection.Y + prevDetection.Height / 2), 2) ); var currArea = currDetection.Width * currDetection.Height; var prevArea = prevDetection.Width * prevDetection.Height; var areaChange = prevArea > 0 ? Math.Abs(currArea - prevArea) / prevArea : 0.0; var confidenceChange = Math.Abs(currDetection.Confidence - prevDetection.Confidence); // 计算单帧稳定性分数 var frameStabilityScore = 1.0 - ( (positionChange / requirements.PositionChangeThreshold) * 0.4 + (areaChange / requirements.AreaChangeThreshold) * 0.3 + (confidenceChange / requirements.ConfidenceChangeThreshold) * 0.3 ); frameStabilityScore = Math.Max(0.0, Math.Min(1.0, frameStabilityScore)); totalStabilityScore += frameStabilityScore; // 简化的稳定性判定 if (positionChange <= requirements.PositionChangeThreshold && areaChange <= requirements.AreaChangeThreshold && confidenceChange <= requirements.ConfidenceChangeThreshold) { stableFrameCount++; } } } var overallStabilityScore = frameHistory.Count > 1 ? totalStabilityScore / (frameHistory.Count - 1) : 0.0; return new StabilityMetrics { OverallStabilityScore = overallStabilityScore, StableFrameCount = stableFrameCount }; } /// /// 创建默认必装部件。 /// private IReadOnlyList CreateDefaultRequiredParts(LayerContext layerContext) { // 简化处理:根据层级上下文创建默认必装部件 return new List { new() { PartId = Guid.NewGuid(), PartCode = "REQ001", PartType = PartType.Required, PartName = "默认必装部件1", RequiredQuantity = 1, Weight = 1.0 }, new() { PartId = Guid.NewGuid(), PartCode = "REQ002", PartType = PartType.Required, PartName = "默认必装部件2", RequiredQuantity = 1, Weight = 1.0 } }; } /// /// 创建默认关键部件。 /// private IReadOnlyList CreateDefaultCriticalParts(LayerContext layerContext) { // 简化处理:根据层级上下文创建默认关键部件 return new List { new() { PartId = Guid.NewGuid(), PartCode = "CRT001", PartType = PartType.Critical, PartName = "默认关键部件1", CriticalityLevel = CriticalityLevel.Important, RequiredQuantity = 1, Weight = 1.0 } }; } /// /// 创建默认稳定性要求。 /// private StabilityRequirements CreateDefaultStabilityRequirements() { return new StabilityRequirements { StabilityThreshold = _options.DefaultStabilityThreshold, MinimumStableFrames = _options.DefaultStabilityWindowSize, PositionChangeThreshold = 5.0, AreaChangeThreshold = 0.1, ConfidenceChangeThreshold = 0.1 }; } /// /// 创建默认禁装部件。 /// private IReadOnlyList CreateDefaultForbiddenParts(LayerContext layerContext) { // 简化处理:根据层级上下文创建默认禁装部件 return new List { new() { PartId = Guid.NewGuid(), PartCode = "FRB001", PartType = PartType.Forbidden, PartName = "默认禁装部件1", ViolationSeverity = ViolationSeverity.Serious, ForbiddenReason = "此部件在此层级不允许安装" } }; } /// /// 添加完成历史。 /// private async Task AddCompletionHistoryAsync(LayerCompletionResult completionResult, CancellationToken cancellationToken = default) { await Task.Run(() => { _completionHistory.TryAdd(Guid.NewGuid(), completionResult); // 保持历史记录数量在合理范围内 if (_completionHistory.Count > _options.MaxLayerCompletionHistoryCount) { var oldestRecords = _completionHistory.Values .OrderBy(r => r.JudgmentTimeUtc) .Take(_completionHistory.Count - _options.MaxLayerCompletionHistoryCount) .Select(r => r.JudgmentTimeUtc) .ToList(); foreach (var time in oldestRecords) { var toRemove = _completionHistory.FirstOrDefault(kvp => kvp.Value.JudgmentTimeUtc == time); if (toRemove.Key != Guid.Empty) { _completionHistory.TryRemove(toRemove.Key, out _); } } } }, cancellationToken); } #endregion /// /// 稳定性指标。 /// private sealed class StabilityMetrics { public double OverallStabilityScore { get; init; } public int StableFrameCount { get; init; } } } #endif