using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using OrpaonVision.Core.Results; using OrpaonVision.SiteApp.Runtime.Contracts; using OrpaonVision.SiteApp.Runtime.Options; using System.Text.Json; namespace OrpaonVision.SiteApp.Runtime.Services; /// /// 高级规则引擎服务实现。 /// 支持复杂规则配置、多条件判断、动态规则加载等功能。 /// public sealed class AdvancedRuleEngineService : IRuleEngineService, IDisposable { private readonly ILogger _logger; private readonly RuntimeOptions _options; private readonly Dictionary _rules; private readonly object _lockObject = new(); private bool _isInitialized; /// /// 构造函数。 /// public AdvancedRuleEngineService(ILogger logger, IOptions options) { _logger = logger; _options = options.Value; _rules = new Dictionary(); InitializeDefaultRules(); } /// public Result Evaluate(int currentLayer, InferenceResultDto inference) { try { _logger.LogDebug("执行规则引擎评估:当前层={CurrentLayer},推理结果={Label},置信度={Confidence:F3}", currentLayer, inference.Label, inference.Confidence); if (!_isInitialized) { _logger.LogWarning("规则引擎未初始化,使用默认规则"); return EvaluateWithDefaultRule(currentLayer, inference); } // 获取当前层的规则 var layerRules = GetRulesForLayer(currentLayer); if (layerRules.Count == 0) { _logger.LogWarning("第 {CurrentLayer} 层没有配置规则,使用默认规则", currentLayer); return EvaluateWithDefaultRule(currentLayer, inference); } // 执行规则评估 var evaluationContext = new RuleEvaluationContext { CurrentLayer = currentLayer, Inference = inference, Timestamp = DateTime.UtcNow }; var results = new List(); foreach (var rule in layerRules) { var result = EvaluateRule(rule, evaluationContext); results.Add(result); _logger.LogDebug("规则 {RuleName} 评估结果:{IsPass} - {Message}", rule.Name, result.IsPass, result.Message); // 如果是关键规则且失败,可以提前终止 if (!result.IsPass && rule.IsCritical) { _logger.LogInformation("关键规则 {RuleName} 失败,提前终止评估", rule.Name); break; } } // 汇总评估结果 var finalDecision = AggregateEvaluationResults(results, evaluationContext); _logger.LogInformation("规则引擎评估完成:{DecisionCode} - {DecisionMessage}", finalDecision.Code, finalDecision.Message); return Result.Success(finalDecision, message: "规则判定完成"); } catch (Exception ex) { var traceId = Guid.NewGuid().ToString("N"); _logger.LogError(ex, "规则引擎评估失败。TraceId: {TraceId}", traceId); var result = Result.FromException(ex, "RULE_ENGINE_EVALUATION_FAILED", "规则引擎评估失败", traceId); return Result.FailWithTrace(result.Code, result.Message, result.TraceId ?? traceId, result.Errors.ToArray()); } } /// /// 初始化默认规则。 /// private void InitializeDefaultRules() { lock (_lockObject) { try { _logger.LogInformation("正在初始化默认规则..."); // 添加基础缺陷检测规则 var defectRule = new RuleDefinition { Name = "DefectDetection", Description = "缺陷检测规则", Layer = 0, // 适用于所有层 Conditions = new List { new() { Type = ConditionType.LabelEquals, Parameter = "defect", Operator = "equals", ExpectedValue = "defect" }, new() { Type = ConditionType.ConfidenceThreshold, Parameter = "confidence", Operator = ">=", ExpectedValue = _options.NgConfidenceThreshold.ToString("F3") } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "检测到缺陷", ["severity"] = "high" } }, IsCritical = true, Priority = 1, Enabled = true }; // 添加置信度规则 var confidenceRule = new RuleDefinition { Name = "ConfidenceCheck", Description = "置信度检查规则", Layer = 0, // 适用于所有层 Conditions = new List { new() { Type = ConditionType.ConfidenceThreshold, Parameter = "confidence", Operator = "<", ExpectedValue = _options.NgConfidenceThreshold.ToString("F3") } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "置信度过低", ["severity"] = "medium" } }, IsCritical = false, Priority = 2, Enabled = true }; // 添加正常通过规则 var passRule = new RuleDefinition { Name = "NormalPass", Description = "正常通过规则", Layer = 0, // 适用于所有层 Conditions = new List { new() { Type = ConditionType.LabelEquals, Parameter = "normal", Operator = "equals", ExpectedValue = "normal" }, new() { Type = ConditionType.ConfidenceThreshold, Parameter = "confidence", Operator = ">=", ExpectedValue = _options.NgConfidenceThreshold.ToString("F3") } }, Action = new RuleAction { Type = ActionType.MarkAsOK, Parameters = new Dictionary { ["reason"] = "正常检测" } }, IsCritical = false, Priority = 3, Enabled = true }; // 添加数量检查规则 var quantityRule = new RuleDefinition { Name = "QuantityCheck", Description = "部件数量检查规则", Layer = 0, // 适用于所有层 Conditions = new List { new() { Type = ConditionType.QuantityCheck, Parameter = "", Operator = "equals", ExpectedValue = "3-5" // 期望3-5个部件 } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "部件数量不符合要求", ["severity"] = "medium" } }, IsCritical = false, Priority = 4, Enabled = true }; // 添加位置检查规则 var positionRule = new RuleDefinition { Name = "PositionCheck", Description = "部件位置检查规则", Layer = 0, Conditions = new List { new() { Type = ConditionType.PositionCheck, Parameter = "screw", // 检查螺丝位置 Operator = "within", ExpectedValue = "100,100,50" // 圆形区域:中心(100,100),半径50 } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "部件位置偏离", ["severity"] = "high" } }, IsCritical = true, Priority = 5, Enabled = true }; // 添加到位检查规则 var placementRule = new RuleDefinition { Name = "PlacementCheck", Description = "部件到位检查规则", Layer = 0, Conditions = new List { new() { Type = ConditionType.PlacementCheck, Parameter = "", Operator = "equals", ExpectedValue = "bracket=2" // 期望2个支架 } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "部件未完全到位", ["severity"] = "medium" } }, IsCritical = false, Priority = 6, Enabled = true }; // 添加禁装检查规则 var forbiddenRule = new RuleDefinition { Name = "ForbiddenCheck", Description = "禁装部件检查规则", Layer = 0, Conditions = new List { new() { Type = ConditionType.ForbiddenCheck, Parameter = "", Operator = "not_contains", ExpectedValue = "wrong_part,temp_part" // 禁止错误的部件和临时部件 } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "发现禁装部件", ["severity"] = "high" } }, IsCritical = true, Priority = 7, Enabled = true }; // 添加顺序检查规则 var sequenceRule = new RuleDefinition { Name = "SequenceCheck", Description = "装配顺序检查规则", Layer = 0, Conditions = new List { new() { Type = ConditionType.SequenceCheck, Parameter = "", Operator = "equals", ExpectedValue = "base>bracket>screw>cover" // 期望装配顺序 } }, Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "装配顺序错误", ["severity"] = "high" } }, IsCritical = true, Priority = 8, Enabled = true }; _rules["DefectDetection"] = defectRule; _rules["ConfidenceCheck"] = confidenceRule; _rules["NormalPass"] = passRule; _rules["QuantityCheck"] = quantityRule; _rules["PositionCheck"] = positionRule; _rules["PlacementCheck"] = placementRule; _rules["ForbiddenCheck"] = forbiddenRule; _rules["SequenceCheck"] = sequenceRule; _isInitialized = true; _logger.LogInformation("默认规则初始化完成,共加载 {Count} 条规则", _rules.Count); } catch (Exception ex) { _logger.LogError(ex, "初始化默认规则失败"); } } } /// /// 获取指定层的规则。 /// private List GetRulesForLayer(int layer) { return _rules.Values .Where(rule => rule.Layer == 0 || rule.Layer == layer) .Where(rule => rule.Enabled) .OrderBy(rule => rule.Priority) .ToList(); } /// /// 评估单个规则。 /// private RuleEvaluationResult EvaluateRule(RuleDefinition rule, RuleEvaluationContext context) { try { var conditionResults = new List(); foreach (var condition in rule.Conditions) { var result = EvaluateCondition(condition, context); conditionResults.Add(result); // 如果是AND逻辑且有一个条件失败,可以提前退出 if (!result && rule.ConditionLogic == ConditionLogic.And) { break; } } // 根据逻辑运算符确定最终结果 var isPass = rule.ConditionLogic == ConditionLogic.And ? conditionResults.All(r => r) : conditionResults.Any(r => r); var message = isPass ? $"规则 {rule.Name} 通过" : $"规则 {rule.Name} 失败: {string.Join(", ", rule.Conditions.Select(c => c.Type))}"; return new RuleEvaluationResult { RuleName = rule.Name, IsPass = isPass, Message = message, Action = isPass ? null : rule.Action, ExecutionTime = DateTime.UtcNow }; } catch (Exception ex) { _logger.LogError(ex, "评估规则 {RuleName} 失败", rule.Name); return new RuleEvaluationResult { RuleName = rule.Name, IsPass = false, Message = $"规则评估异常: {ex.Message}", Action = new RuleAction { Type = ActionType.MarkAsNG, Parameters = new Dictionary { ["reason"] = "规则评估异常", ["severity"] = "high" } }, ExecutionTime = DateTime.UtcNow }; } } /// /// 评估条件。 /// private bool EvaluateCondition(RuleCondition condition, RuleEvaluationContext context) { return condition.Type switch { ConditionType.LabelEquals => EvaluateLabelEquals(condition, context), ConditionType.ConfidenceThreshold => EvaluateConfidenceThreshold(condition, context), ConditionType.QuantityCheck => EvaluateQuantityCheck(condition, context), ConditionType.PositionCheck => EvaluatePositionCheck(condition, context), ConditionType.PlacementCheck => EvaluatePlacementCheck(condition, context), ConditionType.ForbiddenCheck => EvaluateForbiddenCheck(condition, context), ConditionType.SequenceCheck => EvaluateSequenceCheck(condition, context), _ => false }; } /// /// 评估标签等于条件。 /// private bool EvaluateLabelEquals(RuleCondition condition, RuleEvaluationContext context) { var expectedLabel = condition.ExpectedValue; var actualLabel = context.Inference.Label; return condition.Operator switch { "equals" => actualLabel == expectedLabel, "not_equals" => actualLabel != expectedLabel, "contains" => actualLabel.Contains(expectedLabel), "not_contains" => !actualLabel.Contains(expectedLabel), _ => actualLabel == expectedLabel }; } /// /// 评估置信度阈值条件。 /// private bool EvaluateConfidenceThreshold(RuleCondition condition, RuleEvaluationContext context) { if (!decimal.TryParse(condition.ExpectedValue, out var threshold)) { _logger.LogWarning("置信度阈值解析失败: {Value}", condition.ExpectedValue); return false; } var actualConfidence = (decimal)context.Inference.Confidence; return condition.Operator switch { ">=" => actualConfidence >= threshold, "<=" => actualConfidence <= threshold, ">" => actualConfidence > threshold, "<" => actualConfidence < threshold, "==" => actualConfidence == threshold, "!=" => actualConfidence != threshold, _ => actualConfidence >= threshold }; } /// /// 评估数量检查条件。 /// private bool EvaluateQuantityCheck(RuleCondition condition, RuleEvaluationContext context) { try { // 解析期望数量范围,格式如 "min-max" 或 "exact" var expectedRange = condition.ExpectedValue; var actualCount = context.Inference.Detections.Count; _logger.LogDebug("数量检查:期望={ExpectedRange},实际={ActualCount}", expectedRange, actualCount); if (expectedRange.Contains('-')) { // 范围检查,格式:min-max var parts = expectedRange.Split('-'); if (parts.Length == 2 && int.TryParse(parts[0], out var min) && int.TryParse(parts[1], out var max)) { return actualCount >= min && actualCount <= max; } } else if (int.TryParse(expectedRange, out var exact)) { // 精确数量检查 return actualCount == exact; } _logger.LogWarning("数量检查条件解析失败: {Value}", expectedRange); return false; } catch (Exception ex) { _logger.LogError(ex, "数量检查评估失败"); return false; } } /// /// 评估位置检查条件。 /// private bool EvaluatePositionCheck(RuleCondition condition, RuleEvaluationContext context) { try { // 解析期望位置,格式如 "x_min:x_max,y_min:y_max" 或 "center_x,center_y,radius" var expectedPosition = condition.ExpectedValue; var detections = context.Inference.Detections.Where(d => d.ClassName == condition.Parameter || string.IsNullOrEmpty(condition.Parameter)).ToList(); if (!detections.Any()) { _logger.LogDebug("位置检查:未找到匹配的检测对象"); return false; } // 检查是否所有检测对象都在期望位置范围内 foreach (var detection in detections) { var isInRange = CheckPositionInRange(detection.CenterX, detection.CenterY, expectedPosition); if (!isInRange) { _logger.LogDebug("位置检查失败:检测对象 {ClassName} 位置 ({X}, {Y}) 不在期望范围内 {Range}", detection.ClassName, detection.CenterX, detection.CenterY, expectedPosition); return false; } } _logger.LogDebug("位置检查通过:所有 {Count} 个检测对象都在期望范围内", detections.Count); return true; } catch (Exception ex) { _logger.LogError(ex, "位置检查评估失败"); return false; } } /// /// 评估到位检查条件。 /// private bool EvaluatePlacementCheck(RuleCondition condition, RuleEvaluationContext context) { try { // 解析期望的到位状态,格式:className=expected_count var placementRule = condition.ExpectedValue; var parts = placementRule.Split('='); if (parts.Length != 2 || !int.TryParse(parts[1], out var expectedCount)) { _logger.LogWarning("到位检查条件解析失败: {Value}", placementRule); return false; } var targetClass = parts[0]; var actualCount = context.Inference.Detections.Count(d => d.ClassName == targetClass); _logger.LogDebug("到位检查:{ClassName} 期望={ExpectedCount},实际={ActualCount}", targetClass, expectedCount, actualCount); return actualCount >= expectedCount; } catch (Exception ex) { _logger.LogError(ex, "到位检查评估失败"); return false; } } /// /// 评估禁装检查条件。 /// private bool EvaluateForbiddenCheck(RuleCondition condition, RuleEvaluationContext context) { try { // 检查是否存在禁用的部件 var forbiddenClasses = condition.ExpectedValue.Split(',', StringSplitOptions.RemoveEmptyEntries); var detections = context.Inference.Detections; foreach (var forbiddenClass in forbiddenClasses) { var forbiddenDetections = detections.Where(d => d.ClassName.Equals(forbiddenClass.Trim(), StringComparison.OrdinalIgnoreCase)).ToList(); if (forbiddenDetections.Any()) { _logger.LogDebug("禁装检查失败:发现禁用部件 {ClassName},数量={Count}", forbiddenClass, forbiddenDetections.Count); return false; // 发现禁用部件,检查失败 } } _logger.LogDebug("禁装检查通过:未发现禁用部件"); return true; } catch (Exception ex) { _logger.LogError(ex, "禁装检查评估失败"); return false; } } /// /// 评估顺序检查条件。 /// private bool EvaluateSequenceCheck(RuleCondition condition, RuleEvaluationContext context) { try { // 解析期望顺序,格式如 "class1>class2>class3" var expectedSequence = condition.ExpectedValue; var sequenceParts = expectedSequence.Split('>', StringSplitOptions.RemoveEmptyEntries); // 按Y坐标排序检测对象(假设装配顺序是从上到下) var sortedDetections = context.Inference.Detections .Where(d => sequenceParts.Contains(d.ClassName)) .OrderBy(d => d.CenterY) .Select(d => d.ClassName) .ToList(); var actualSequence = string.Join(">", sortedDetections); var isCorrectOrder = actualSequence.Equals(expectedSequence, StringComparison.OrdinalIgnoreCase); _logger.LogDebug("顺序检查:期望={Expected},实际={Actual},结果={Result}", expectedSequence, actualSequence, isCorrectOrder ? "通过" : "失败"); return isCorrectOrder; } catch (Exception ex) { _logger.LogError(ex, "顺序检查评估失败"); return false; } } /// /// 检查位置是否在指定范围内。 /// private static bool CheckPositionInRange(float centerX, float centerY, string rangeDefinition) { if (rangeDefinition.Contains(',')) { // 圆形范围检查,格式:center_x,center_y,radius var parts = rangeDefinition.Split(','); if (parts.Length == 3 && float.TryParse(parts[0], out var expectedCenterX) && float.TryParse(parts[1], out var expectedCenterY) && float.TryParse(parts[2], out var radius)) { var distance = Math.Sqrt(Math.Pow(centerX - expectedCenterX, 2) + Math.Pow(centerY - expectedCenterY, 2)); return distance <= radius; } } else if (rangeDefinition.Contains(':')) { // 矩形范围检查,格式:x_min:x_max,y_min:y_max var coordinates = rangeDefinition.Split(','); if (coordinates.Length == 2) { var xRange = coordinates[0].Split(':'); var yRange = coordinates[1].Split(':'); if (xRange.Length == 2 && yRange.Length == 2 && float.TryParse(xRange[0], out var xMin) && float.TryParse(xRange[1], out var xMax) && float.TryParse(yRange[0], out var yMin) && float.TryParse(yRange[1], out var yMax)) { return centerX >= xMin && centerX <= xMax && centerY >= yMin && centerY <= yMax; } } } return false; } /// /// 汇总评估结果。 /// private RuntimeDecisionDto AggregateEvaluationResults(List results, RuleEvaluationContext context) { var failedRules = results.Where(r => !r.IsPass).ToList(); var criticalFailures = failedRules.Where(r => r.Action?.Parameters?.ContainsKey("severity") == true && r.Action.Parameters["severity"].ToString() == "high").ToList(); var isPass = failedRules.Count == 0; var decisionCode = isPass ? "RULE_PASS" : "RULE_NG"; var decisionMessage = isPass ? $"第 {context.CurrentLayer} 层所有规则通过" : $"第 {context.CurrentLayer} 层规则失败: {string.Join(", ", failedRules.Select(r => r.RuleName))}"; // 如果有关键失败,提升消息级别 if (criticalFailures.Count > 0) { decisionCode = "RULE_CRITICAL_NG"; decisionMessage = $"第 {context.CurrentLayer} 层关键规则失败: {string.Join(", ", criticalFailures.Select(r => r.RuleName))}"; } return new RuntimeDecisionDto { IsPass = isPass, Code = decisionCode, Message = decisionMessage }; } /// /// 使用默认规则评估。 /// private Result EvaluateWithDefaultRule(int currentLayer, InferenceResultDto inference) { var isPass = inference.Label == "normal" || inference.Label == "OK" || inference.Confidence < _options.NgConfidenceThreshold; var decision = new RuntimeDecisionDto { IsPass = isPass, Code = isPass ? "DEFAULT_RULE_PASS" : "DEFAULT_RULE_NG", Message = isPass ? $"第 {currentLayer} 层默认规则通过。" : $"第 {currentLayer} 层默认规则 NG:{inference.Label} (置信度: {inference.Confidence:F3})" }; return Result.Success(decision, message: "默认规则判定完成"); } /// /// 释放资源。 /// public void Dispose() { try { _rules.Clear(); _isInitialized = false; _logger.LogInformation("规则引擎服务已释放"); } catch (Exception ex) { _logger.LogError(ex, "释放规则引擎服务资源时发生错误"); } } } #region 规则引擎内部类型 /// /// 规则定义。 /// internal sealed class RuleDefinition { public string Name { get; init; } = string.Empty; public string Description { get; init; } = string.Empty; public int Layer { get; init; } public List Conditions { get; init; } = new(); public RuleAction Action { get; init; } = new(); public bool IsCritical { get; init; } public int Priority { get; init; } public bool Enabled { get; init; } public ConditionLogic ConditionLogic { get; init; } = ConditionLogic.And; } /// /// 规则条件。 /// internal sealed class RuleCondition { public ConditionType Type { get; init; } public string Parameter { get; init; } = string.Empty; public string Operator { get; init; } = string.Empty; public string ExpectedValue { get; init; } = string.Empty; } /// /// 规则动作。 /// internal sealed class RuleAction { public ActionType Type { get; init; } public Dictionary Parameters { get; init; } = new(); } /// /// 规则评估上下文。 /// internal sealed class RuleEvaluationContext { public int CurrentLayer { get; init; } public InferenceResultDto Inference { get; init; } = new(); public DateTime Timestamp { get; init; } } /// /// 规则评估结果。 /// internal sealed class RuleEvaluationResult { public string RuleName { get; init; } = string.Empty; public bool IsPass { get; init; } public string Message { get; init; } = string.Empty; public RuleAction? Action { get; init; } public DateTime ExecutionTime { get; init; } } /// /// 条件类型。 /// internal enum ConditionType { LabelEquals, ConfidenceThreshold, QuantityCheck, PositionCheck, PlacementCheck, ForbiddenCheck, SequenceCheck } /// /// 动作类型。 /// internal enum ActionType { MarkAsOK, MarkAsNG, LogWarning, Custom } /// /// 条件逻辑。 /// internal enum ConditionLogic { And, Or } #endregion