Files
OrpaonVision/OrpaonVision.SiteApp/Runtime/Services/LayerCompletionService.cs
2026-04-06 22:04:05 +08:00

1392 lines
62 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
/// <summary>
/// 层级完成判定服务实现。
/// </summary>
#if false
public sealed class LayerCompletionService : ILayerCompletionService
{
private readonly ILogger<LayerCompletionService> _logger;
private readonly RuntimeOptions _options;
private readonly ConcurrentDictionary<Guid, LayerCompletionRule> _completionRules = new();
private readonly ConcurrentDictionary<Guid, LayerCompletionResult> _completionHistory = new();
private readonly object _lock = new();
/// <summary>
/// 构造函数。
/// </summary>
public LayerCompletionService(ILogger<LayerCompletionService> logger, IOptions<RuntimeOptions> options)
{
_logger = logger;
_options = options.Value;
InitializeDefaultCompletionRules();
}
#endif
/// <summary>
/// 层级完成判定服务最小实现。
/// </summary>
public sealed class LayerCompletionService : ILayerCompletionService
{
private readonly ILogger<LayerCompletionService> _logger;
private readonly ConcurrentDictionary<Guid, LayerCompletionRule> _completionRules = new();
private readonly ConcurrentDictionary<Guid, LayerCompletionResult> _completionHistory = new();
public LayerCompletionService(ILogger<LayerCompletionService> logger, IOptions<RuntimeOptions> options)
{
_logger = logger;
_ = options;
}
public async Task<Result<LayerCompletionResult>> JudgeLayerCompletionAsync(LayerContext layerContext, IReadOnlyList<LayerCompletionRule> completionRules, CancellationToken cancellationToken = default)
{
var required = await JudgeRequiredPartsAsync(layerContext.DetectedParts, Array.Empty<RequiredPart>(), cancellationToken);
var critical = await JudgeCriticalPartsAsync(layerContext.DetectedParts, Array.Empty<CriticalPart>(), cancellationToken);
var stability = await JudgeStabilityAsync(layerContext.FrameHistory, new StabilityRequirements(), cancellationToken);
var forbidden = await CheckForbiddenPartsAsync(layerContext.DetectedParts, Array.Empty<ForbiddenPart>(), 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<HardCondition>(),
CompletionQuality = CompletionQuality.Excellent,
Details = "最小实现:层级完成"
};
_completionHistory[Guid.NewGuid()] = result;
return Result<LayerCompletionResult>.Success(result);
}
public Task<Result<RequiredPartsCompletionResult>> JudgeRequiredPartsAsync(IReadOnlyList<MappedPart> parts, IReadOnlyList<RequiredPart> requiredParts, CancellationToken cancellationToken = default)
{
var partCodes = new HashSet<string>(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<RequiredPartsCompletionResult>.Success(result));
}
public Task<Result<CriticalPartsCompletionResult>> JudgeCriticalPartsAsync(IReadOnlyList<MappedPart> parts, IReadOnlyList<CriticalPart> criticalParts, CancellationToken cancellationToken = default)
{
var partCodes = new HashSet<string>(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<CriticalPartsCompletionResult>.Success(result));
}
public Task<Result<StabilityCompletionResult>> JudgeStabilityAsync(IReadOnlyList<InferenceResultDto> 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<StabilityCompletionResult>.Success(result));
}
public Task<Result<ForbiddenPartsCheckResult>> CheckForbiddenPartsAsync(IReadOnlyList<MappedPart> parts, IReadOnlyList<ForbiddenPart> forbiddenParts, CancellationToken cancellationToken = default)
{
var forbiddenCodes = new HashSet<string>(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<ForbiddenPartsCheckResult>.Success(result));
}
public async Task<Result<BatchLayerCompletionResult>> BatchJudgeLayerCompletionAsync(IReadOnlyList<LayerContext> layerContexts, IReadOnlyList<LayerCompletionRule> completionRules, CancellationToken cancellationToken = default)
{
var list = new List<LayerCompletionResult>();
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<BatchLayerCompletionResult>.Success(result);
}
public Task<Result<LayerCompletionStatistics>> 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<LayerCompletionType, CompletionTypeStatistics>(),
ByProductType = new Dictionary<string, ProductTypeCompletionStatistics>(),
ByLayer = new Dictionary<int, LayerCompletionStatisticsByLayer>()
};
return Task.FromResult(Result<LayerCompletionStatistics>.Success(result));
}
public Task<Result<LayerCompletionRule>> 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<string, object>(rule.ExtendedProperties)
};
_completionRules[created.RuleId] = created;
return Task.FromResult(Result<LayerCompletionRule>.Success(created));
}
public Task<Result> 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<Result> 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<Result<IReadOnlyList<LayerCompletionRule>>> 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<LayerCompletionRule> result = query.ToList();
_logger.LogDebug("最小实现:返回层级规则数量={Count}", result.Count);
return Task.FromResult(Result<IReadOnlyList<LayerCompletionRule>>.Success(result));
}
}
#if false
/// <inheritdoc />
public async Task<Result<LayerCompletionResult>> JudgeLayerCompletionAsync(LayerContext layerContext, IReadOnlyList<LayerCompletionRule> 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<HardCondition>();
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<double>();
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<string, object>
{
["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<LayerCompletionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<RequiredPartsCompletionResult>> JudgeRequiredPartsAsync(IReadOnlyList<MappedPart> parts, IReadOnlyList<RequiredPart> requiredParts, CancellationToken cancellationToken = default)
{
try
{
_logger.LogDebug("开始必装部件完成判定:检测部件数量={DetectedCount},必装部件数量={RequiredCount}", parts.Count, requiredParts.Count);
var startTime = DateTime.UtcNow;
var installedRequiredParts = 0;
var missingRequiredParts = new List<RequiredPart>();
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<string, object>
{
["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<RequiredPartsCompletionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<CriticalPartsCompletionResult>> JudgeCriticalPartsAsync(IReadOnlyList<MappedPart> parts, IReadOnlyList<CriticalPart> criticalParts, CancellationToken cancellationToken = default)
{
try
{
_logger.LogDebug("开始关键部件完成判定:检测部件数量={DetectedCount},关键部件数量={CriticalCount}", parts.Count, criticalParts.Count);
var startTime = DateTime.UtcNow;
var installedCriticalParts = 0;
var missingCriticalParts = new List<CriticalPart>();
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<string, object>
{
["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<CriticalPartsCompletionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<StabilityCompletionResult>> JudgeStabilityAsync(IReadOnlyList<InferenceResultDto> 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<string, object>
{
["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<string, object>
{
["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<StabilityCompletionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<ForbiddenPartsCheckResult>> CheckForbiddenPartsAsync(IReadOnlyList<MappedPart> parts, IReadOnlyList<ForbiddenPart> forbiddenParts, CancellationToken cancellationToken = default)
{
try
{
_logger.LogDebug("开始禁装部件检查:检测部件数量={DetectedCount},禁装部件数量={ForbiddenCount}", parts.Count, forbiddenParts.Count);
var startTime = DateTime.UtcNow;
var detectedForbiddenParts = new List<ForbiddenPartViolation>();
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<string, object>
{
["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<ForbiddenPartsCheckResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<BatchLayerCompletionResult>> BatchJudgeLayerCompletionAsync(IReadOnlyList<LayerContext> layerContexts, IReadOnlyList<LayerCompletionRule> completionRules, CancellationToken cancellationToken = default)
{
try
{
_logger.LogInformation("开始批量层级完成判定:层级数量={LayerCount},规则数量={RuleCount}",
layerContexts.Count, completionRules.Count);
var startTime = DateTime.UtcNow;
var completionResults = new List<LayerCompletionResult>();
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<string, object>
{
["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<BatchLayerCompletionResult>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerCompletionStatistics>> 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<LayerCompletionStatistics>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result<LayerCompletionRule>> 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<string, object>(rule.ExtendedProperties)
};
_completionRules[createdRule.RuleId] = createdRule;
}
_logger.LogInformation("层级完成规则创建成功规则ID={RuleId},规则名称={RuleName}",
createdRule.RuleId, createdRule.RuleName);
return Result<LayerCompletionRule>.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<LayerCompletionRule>(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
/// <inheritdoc />
public async Task<Result> 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<string, object>(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());
}
}
/// <inheritdoc />
public async Task<Result> 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());
}
}
/// <inheritdoc />
public async Task<Result<IReadOnlyList<LayerCompletionRule>>> 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<IReadOnlyList<LayerCompletionRule>>.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<IReadOnlyList<LayerCompletionRule>>.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray());
}
}
#region
/// <summary>
/// 初始化默认完成规则。
/// </summary>
private void InitializeDefaultCompletionRules()
{
var defaultRules = new List<LayerCompletionRule>
{
new()
{
RuleId = Guid.NewGuid(),
RuleName = "必装部件完成规则",
RuleDescription = "必装部件必须全部安装",
ProductTypeCode = "default",
LayerNumber = 0,
CompletionType = LayerCompletionType.RequiredParts,
HardConditions = new List<HardCondition>
{
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<HardCondition>
{
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<HardCondition>
{
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<HardCondition>
{
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<HardCondition>
{
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;
}
}
/// <summary>
/// 计算完成度百分比。
/// </summary>
private double CalculateCompletionPercentage(
RequiredPartsCompletionResult? requiredResult,
CriticalPartsCompletionResult? criticalResult,
StabilityCompletionResult? stabilityResult,
ForbiddenPartsCheckResult? forbiddenResult)
{
var scores = new List<double>();
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;
}
/// <summary>
/// 确定完成质量。
/// </summary>
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
};
}
/// <summary>
/// 计算稳定性指标。
/// </summary>
private async Task<StabilityMetrics> CalculateStabilityMetricsAsync(IReadOnlyList<InferenceResultDto> 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
};
}
/// <summary>
/// 创建默认必装部件。
/// </summary>
private IReadOnlyList<RequiredPart> CreateDefaultRequiredParts(LayerContext layerContext)
{
// 简化处理:根据层级上下文创建默认必装部件
return new List<RequiredPart>
{
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
}
};
}
/// <summary>
/// 创建默认关键部件。
/// </summary>
private IReadOnlyList<CriticalPart> CreateDefaultCriticalParts(LayerContext layerContext)
{
// 简化处理:根据层级上下文创建默认关键部件
return new List<CriticalPart>
{
new()
{
PartId = Guid.NewGuid(),
PartCode = "CRT001",
PartType = PartType.Critical,
PartName = "默认关键部件1",
CriticalityLevel = CriticalityLevel.Important,
RequiredQuantity = 1,
Weight = 1.0
}
};
}
/// <summary>
/// 创建默认稳定性要求。
/// </summary>
private StabilityRequirements CreateDefaultStabilityRequirements()
{
return new StabilityRequirements
{
StabilityThreshold = _options.DefaultStabilityThreshold,
MinimumStableFrames = _options.DefaultStabilityWindowSize,
PositionChangeThreshold = 5.0,
AreaChangeThreshold = 0.1,
ConfidenceChangeThreshold = 0.1
};
}
/// <summary>
/// 创建默认禁装部件。
/// </summary>
private IReadOnlyList<ForbiddenPart> CreateDefaultForbiddenParts(LayerContext layerContext)
{
// 简化处理:根据层级上下文创建默认禁装部件
return new List<ForbiddenPart>
{
new()
{
PartId = Guid.NewGuid(),
PartCode = "FRB001",
PartType = PartType.Forbidden,
PartName = "默认禁装部件1",
ViolationSeverity = ViolationSeverity.Serious,
ForbiddenReason = "此部件在此层级不允许安装"
}
};
}
/// <summary>
/// 添加完成历史。
/// </summary>
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
/// <summary>
/// 稳定性指标。
/// </summary>
private sealed class StabilityMetrics
{
public double OverallStabilityScore { get; init; }
public int StableFrameCount { get; init; }
}
}
#endif