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