DeadlockDetector.cs 7.1 KB
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Rcs.Application.Services;
using Rcs.Application.Services.PathFind.Models;

namespace Rcs.Infrastructure.PathFinding.Services
{
    /// <summary>
    /// 死锁检测与解决服务(统一锁模式)
    /// @author zzy
    /// </summary>
    public class DeadlockDetector
    {
        private readonly ILogger<DeadlockDetector> _logger;
        private readonly IUnifiedTrafficControlService _unifiedTrafficControlService;
        private readonly ConcurrentDictionary<Guid, RobotRuntimeState> _robotStates = new();

        /// <summary>
        /// 死锁检测间隔(毫秒)
        /// </summary>
        public int DetectionIntervalMs { get; set; } = 500;

        public DeadlockDetector(ILogger<DeadlockDetector> logger, IUnifiedTrafficControlService unifiedTrafficControlService)
        {
            _logger = logger;
            _unifiedTrafficControlService = unifiedTrafficControlService;
        }

        /// <summary>
        /// 更新机器人状态
        /// @author zzy
        /// </summary>
        public void UpdateRobotState(RobotRuntimeState state)
        {
            _robotStates[state.RobotId] = state;
        }

        /// <summary>
        /// 移除机器人状态
        /// @author zzy
        /// </summary>
        public void RemoveRobotState(Guid robotId)
        {
            _robotStates.TryRemove(robotId, out _);
        }

        /// <summary>
        /// 检测死锁(基于等待图的环检测)
        /// @author zzy
        /// </summary>
        public List<DeadlockInfo> DetectDeadlocks()
        {
            var deadlocks = new List<DeadlockInfo>();
            var waitGraph = BuildWaitGraph();
            var visited = new HashSet<Guid>();
            var recursionStack = new HashSet<Guid>();

            foreach (var robotId in waitGraph.Keys)
            {
                if (!visited.Contains(robotId))
                {
                    var cycle = new List<Guid>();
                    if (DetectCycle(robotId, waitGraph, visited, recursionStack, cycle))
                    {
                        deadlocks.Add(new DeadlockInfo
                        {
                            DetectedAt = DateTime.Now,
                            InvolvedRobots = cycle.ToList(),
                            WaitGraph = waitGraph.Where(kv => cycle.Contains(kv.Key))
                                .ToDictionary(kv => kv.Key, kv => kv.Value)
                        });
                    }
                }
            }

            return deadlocks;
        }

        /// <summary>
        /// 解决死锁(返回需要退让的机器人)
        /// @author zzy
        /// </summary>
        public DeadlockResolution ResolveDeadlock(DeadlockInfo deadlock)
        {
            var resolution = new DeadlockResolution { DeadlockInfo = deadlock };
            var involvedRobots = deadlock.InvolvedRobots
                .Select(id => _robotStates.TryGetValue(id, out var state) ? state : null)
                .Where(s => s != null)
                .ToList();

            if (involvedRobots.Count < 2)
            {
                resolution.Success = false;
                resolution.Reason = "无法获取足够的机器人状态信息";
                return resolution;
            }

            // 选择退让机器人的优先级规则:
            // 1. 任务优先级低让高
            // 2. 空载让重载
            // 3. 高电量让低电量
            var robotToYield = involvedRobots
                .OrderBy(r => r!.Priority)
                .ThenBy(r => r!.IsLoaded ? 1 : 0)
                .ThenByDescending(r => r!.BatteryLevel)
                .First();

            resolution.Success = true;
            resolution.RobotToYield = robotToYield!.RobotId;
            resolution.YieldReason = DetermineYieldReason(robotToYield!, involvedRobots!);

            return resolution;
        }

        /// <summary>
        /// 构建等待图(使用统一锁服务)
        /// @author zzy
        /// </summary>
        private Dictionary<Guid, Guid> BuildWaitGraph()
        {
            var waitGraph = new Dictionary<Guid, Guid>();

            foreach (var (robotId, state) in _robotStates)
            {
                // 优先使用带Code的路径信息
                if (state.PlannedPathWithCode.Count > 0 && !string.IsNullOrEmpty(state.MapCode))
                {
                    var nextNode = state.PlannedPathWithCode.FirstOrDefault();
                    if (nextNode == null || string.IsNullOrEmpty(nextNode.NodeCode)) continue;

                    // 使用统一锁服务检查节点是否被其他机器人锁定
                    var holder = _unifiedTrafficControlService.GetNodeLockHolderAsync(state.MapCode, nextNode.NodeCode).GetAwaiter().GetResult();
                    if (holder.HasValue && holder.Value != robotId)
                    {
                        waitGraph[robotId] = holder.Value;
                    }
                }
                // 回退到传统路径(如果没有带Code的路径)
                else if (state.PlannedPath.Count > 0)
                {
                    // 无法使用统一锁检查,跳过
                    _logger.LogDebug("机器人 {RobotId} 缺少 PlannedPathWithCode 或 MapCode,跳过死锁检测", robotId);
                }
            }

            return waitGraph;
        }

        /// <summary>
        /// DFS检测环
        /// @author zzy
        /// </summary>
        private bool DetectCycle(Guid current, Dictionary<Guid, Guid> graph,
            HashSet<Guid> visited, HashSet<Guid> recursionStack, List<Guid> cycle)
        {
            visited.Add(current);
            recursionStack.Add(current);
            cycle.Add(current);

            if (graph.TryGetValue(current, out var next))
            {
                if (!visited.Contains(next))
                {
                    if (DetectCycle(next, graph, visited, recursionStack, cycle))
                        return true;
                }
                else if (recursionStack.Contains(next))
                {
                    // 找到环,裁剪cycle只保留环部分
                    var cycleStart = cycle.IndexOf(next);
                    if (cycleStart >= 0)
                        cycle.RemoveRange(0, cycleStart);
                    return true;
                }
            }

            recursionStack.Remove(current);
            cycle.Remove(current);
            return false;
        }

        private string DetermineYieldReason(RobotRuntimeState yielder, List<RobotRuntimeState?> allRobots)
        {
            var others = allRobots.Where(r => r != null && r.RobotId != yielder.RobotId).ToList();
            if (others.Any(r => r!.Priority > yielder.Priority))
                return "任务优先级较低";
            if (!yielder.IsLoaded && others.Any(r => r!.IsLoaded))
                return "空载状态";
            if (others.Any(r => r!.BatteryLevel < yielder.BatteryLevel))
                return "电量较高";
            return "综合评估";
        }
    }


}