IUnifiedTrafficControlService.cs 13.9 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Rcs.Application.Services.PathFind.Models;

namespace Rcs.Application.Services
{
    /// <summary>
    /// 锁请求结果
    /// @author zzy
    /// </summary>
    public class LockRequestResult
    {
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool Success { get; set; }

        /// <summary>
        /// 失败原因
        /// </summary>
        public string? FailureReason { get; set; }

        /// <summary>
        /// 冲突的节点Code列表
        /// </summary>
        public List<string> ConflictingNodeCodes { get; set; } = new();

        /// <summary>
        /// 冲突的边Code列表
        /// </summary>
        public List<string> ConflictingEdgeCodes { get; set; } = new();

        /// <summary>
        /// 占用冲突资源的机器人ID
        /// </summary>
        public List<Guid> ConflictingRobotIds { get; set; } = new();

        /// <summary>
        /// 成功锁定的节点Code列表
        /// </summary>
        public List<string> LockedNodeCodes { get; set; } = new();

        /// <summary>
        /// 成功锁定的边Code列表
        /// </summary>
        public List<string> LockedEdgeCodes { get; set; } = new();
    }

    /// <summary>
    /// 锁定状态信息
    /// @author zzy
    /// </summary>
    public class LockStatusInfo
    {
        /// <summary>
        /// 资源Key(格式: MapCode:NodeCode 或 MapCode:EdgeCode)
        /// </summary>
        public string ResourceKey { get; set; } = string.Empty;

        /// <summary>
        /// 资源Code(NodeCode或EdgeCode)
        /// </summary>
        public string ResourceCode { get; set; } = string.Empty;

        /// <summary>
        /// 资源类型
        /// </summary>
        public LockResourceType ResourceType { get; set; }

        /// <summary>
        /// 是否被锁定
        /// </summary>
        public bool IsLocked { get; set; }

        /// <summary>
        /// 锁定该资源的机器人ID
        /// </summary>
        public Guid? LockedByRobotId { get; set; }

        /// <summary>
        /// 锁定时间
        /// </summary>
        public DateTime? LockedAt { get; set; }

        /// <summary>
        /// 锁过期时间
        /// </summary>
        public DateTime? ExpiresAt { get; set; }
    }

    /// <summary>
    /// 锁资源类型
    /// @author zzy
    /// </summary>
    public enum LockResourceType
    {
        /// <summary>
        /// 节点
        /// </summary>
        Node = 1,

        /// <summary>
        /// 边
        /// </summary>
        Edge = 2
    }

    /// <summary>
    /// 统一交通管制服务接口 - 基于 MapCode:Code 的跨地图锁管理
    /// 同一MapCode下的所有地图共享相同的NodeCode/EdgeCode,因此使用 MapCode:Code 作为锁的Key
    /// @author zzy
    /// </summary>
    public interface IUnifiedTrafficControlService
    {
        #region 基于Code的锁操作

        /// <summary>
        /// 尝试锁定节点(使用 MapCode:NodeCode 作为Key)
        /// </summary>
        /// <param name="mapCode">地图编码(同一物理环境的所有厂家地图使用相同的MapCode)</param>
        /// <param name="nodeCode">节点编码</param>
        /// <param name="robotId">机器人ID</param>
        /// <param name="ttlSeconds">锁的过期时间(秒)</param>
        /// <returns>是否成功</returns>
        Task<bool> TryAcquireNodeLockAsync(string mapCode, string nodeCode, Guid robotId, int ttlSeconds = 30);

        /// <summary>
        /// 尝试锁定边(使用 MapCode:EdgeCode 作为Key)
        /// </summary>
        Task<bool> TryAcquireEdgeLockAsync(string mapCode, string edgeCode, Guid robotId, int ttlSeconds = 30);

        /// <summary>
        /// 尝试锁定边(带方向信息,用于区分同向/逆向冲突)
        /// @author zzy
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="edgeCode">边编码</param>
        /// <param name="robotId">机器人ID</param>
        /// <param name="fromNodeCode">行驶方向起点节点Code</param>
        /// <param name="toNodeCode">行驶方向终点节点Code</param>
        /// <param name="ttlSeconds">锁的过期时间(秒)</param>
        /// <returns>是否成功</returns>
        Task<bool> TryAcquireEdgeLockWithDirectionAsync(string mapCode, string edgeCode, Guid robotId, string fromNodeCode, string toNodeCode, int ttlSeconds = 30);

        /// <summary>
        /// 释放节点锁
        /// </summary>
        Task<bool> ReleaseNodeLockAsync(string mapCode, string nodeCode, Guid robotId);

        /// <summary>
        /// 释放边锁
        /// </summary>
        Task<bool> ReleaseEdgeLockAsync(string mapCode, string edgeCode, Guid robotId);

        /// <summary>
        /// 批量锁定路径(节点+边)
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="nodeCodes">节点编码列表</param>
        /// <param name="edgeCodes">边编码列表</param>
        /// <param name="robotId">机器人ID</param>
        /// <param name="ttlSeconds">锁的过期时间</param>
        /// <returns>锁请求结果</returns>
        Task<LockRequestResult> TryAcquirePathLocksAsync(
            string mapCode,
            IEnumerable<string> nodeCodes,
            IEnumerable<string> edgeCodes,
            Guid robotId,
            int ttlSeconds = 30);

        /// <summary>
        /// 批量释放路径锁
        /// </summary>
        Task<int> ReleasePathLocksAsync(
            string mapCode,
            IEnumerable<string> nodeCodes,
            IEnumerable<string> edgeCodes,
            Guid robotId);

        #endregion

        #region 锁状态查询

        /// <summary>
        /// 检查节点是否被锁定
        /// </summary>
        Task<LockStatusInfo> GetNodeLockStatusAsync(string mapCode, string nodeCode);

        /// <summary>
        /// 检查边是否被锁定
        /// </summary>
        Task<LockStatusInfo> GetEdgeLockStatusAsync(string mapCode, string edgeCode);

        /// <summary>
        /// 获取节点锁持有者(机器人ID)
        /// </summary>
        Task<Guid?> GetNodeLockHolderAsync(string mapCode, string nodeCode);

        /// <summary>
        /// 获取边锁持有者
        /// </summary>
        Task<Guid?> GetEdgeLockHolderAsync(string mapCode, string edgeCode);

        /// <summary>
        /// 获取机器人当前锁定的所有资源
        /// </summary>
        Task<(List<string> NodeKeys, List<string> EdgeKeys)> GetRobotLockedResourcesAsync(Guid robotId);

        #endregion

        #region 全局锁管理

        /// <summary>
        /// 释放机器人的所有锁
        /// </summary>
        Task<int> ReleaseAllRobotLocksAsync(Guid robotId);

        /// <summary>
        /// 续期机器人的所有锁
        /// </summary>
        Task<int> RenewAllRobotLocksAsync(Guid robotId, int ttlSeconds = 30);

        /// <summary>
        /// 获取指定MapCode下所有锁定状态(用于可视化)
        /// </summary>
        Task<List<LockStatusInfo>> GetAllLockStatusAsync(string mapCode);

        #endregion

        #region 冲突检测

        /// <summary>
        /// 检测路径是否与其他机器人冲突
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="nodeCodes">计划路径的节点编码列表</param>
        /// <param name="edgeCodes">计划路径的边编码列表</param>
        /// <param name="robotId">请求机器人ID</param>
        /// <param name="edgeDirectionMap">边方向映射(Key: EdgeCode, Value: (FromNodeCode, ToNodeCode)),用于排除同方向冲突</param>
        /// <returns>冲突检测结果</returns>
        Task<PathConflictResult> CheckPathConflictAsync(
            string mapCode,
            IEnumerable<string> nodeCodes,
            IEnumerable<string> edgeCodes,
            Guid robotId,
            Dictionary<string, (string FromNodeCode, string ToNodeCode)>? edgeDirectionMap = null);

        /// <summary>
        /// 尝试锁定下一段路径,并判断是否存在逆向占用
        /// 用于车辆快到当前段终点时,判断并发送下一段路径指令的场景
        /// @author zzy
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="segments">路径段列表(包含节点和边编码信息)</param>
        /// <param name="robotId">机器人ID</param>
        /// <param name="ttlSeconds">锁的过期时间(秒)</param>
        /// <returns>下一段路径锁定结果</returns>
        Task<NextSegmentLockResult> TryAcquireNextSegmentLockAsync(
            string mapCode,
            IEnumerable<PathSegmentWithCode> segments,
            Guid robotId,
            int ttlSeconds = 30);

        #endregion

        #region 逆向边管理(双向互斥检测)

        /// <summary>
        /// 注册逆向边关系(用于双向互斥检测)
        /// 锁定边时会自动检查逆向边是否被其他机器人占用
        /// @author zzy
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="edgeCode">边编码</param>
        /// <param name="reverseEdgeCode">逆向边编码(如果为null或空,表示单行道)</param>
        void RegisterReverseEdge(string mapCode, string edgeCode, string? reverseEdgeCode);

        /// <summary>
        /// 批量注册逆向边关系
        /// @author zzy
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="edgeReverseMapping">边编码到逆向边编码的映射</param>
        void RegisterReverseEdges(string mapCode, Dictionary<string, string?> edgeReverseMapping);

        /// <summary>
        /// 清除指定地图的逆向边关系缓存
        /// @author zzy
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        void ClearReverseEdgeCache(string mapCode);

        #endregion

        #region 初始化与合并映射集成

        /// <summary>
        /// 从合并映射初始化逆向边关系
        /// 应在 MapMappingService.InitializeAllMappingsAsync() 后调用
        /// @author zzy
        /// </summary>
        /// <param name="mergedTopologies">合并拓扑字典(Key: MapCode)</param>
        Task InitializeFromMergedMappingsAsync(Dictionary<string, MergedMapTopology> mergedTopologies);

        /// <summary>
        /// 刷新指定MapCode的逆向边关系(当地图更新后调用)
        /// @author zzy
        /// </summary>
        /// <param name="mapCode">地图编码</param>
        /// <param name="mergedTopology">更新后的合并拓扑</param>
        Task RefreshReverseEdgesAsync(string mapCode, MergedMapTopology mergedTopology);

        #endregion
    }

    /// <summary>
    /// 路径冲突检测结果
    /// @author zzy
    /// </summary>
    public class PathConflictResult
    {
        /// <summary>
        /// 是否存在冲突
        /// </summary>
        public bool HasConflict { get; set; }

        /// <summary>
        /// 冲突详情列表
        /// </summary>
        public List<PathConflictDetail> Conflicts { get; set; } = new();

        /// <summary>
        /// 第一个冲突点在路径中的索引
        /// </summary>
        public int? FirstConflictIndex { get; set; }

        /// <summary>
        /// 同方向边冲突列表(被排除的非冲突项)
        /// @author zzy
        /// </summary>
        public List<PathConflictDetail> SameDirectionConflicts { get; set; } = new();
    }

    /// <summary>
    /// 路径冲突详情
    /// @author zzy
    /// </summary>
    public class PathConflictDetail
    {
        /// <summary>
        /// 冲突的资源Key(MapCode:Code)
        /// </summary>
        public string ResourceKey { get; set; } = string.Empty;

        /// <summary>
        /// 冲突的节点编码(如果是节点冲突)
        /// </summary>
        public string? NodeCode { get; set; }

        /// <summary>
        /// 冲突的边编码(如果是边冲突)
        /// </summary>
        public string? EdgeCode { get; set; }

        /// <summary>
        /// 冲突类型
        /// </summary>
        public ConflictType ConflictType { get; set; }

        /// <summary>
        /// 冲突的机器人ID
        /// </summary>
        public Guid ConflictingRobotId { get; set; }

        /// <summary>
        /// 在路径中的索引位置
        /// </summary>
        public int PathIndex { get; set; }
    }

    /// <summary>
    /// 冲突类型
    /// @author zzy
    /// </summary>
    public enum ConflictType
    {
        /// <summary>
        /// 节点被占用
        /// </summary>
        NodeOccupied = 1,

        /// <summary>
        /// 边被占用
        /// </summary>
        EdgeOccupied = 2,

        /// <summary>
        /// 对向冲突(双向边上相向而行)
        /// </summary>
        HeadOnConflict = 3,

        /// <summary>
        /// 交叉冲突
        /// </summary>
        CrossingConflict = 4
    }

    /// <summary>
    /// 下一段路径锁定结果
    /// @author zzy
    /// </summary>
    public class NextSegmentLockResult
    {
        /// <summary>
        /// 是否成功
        /// </summary>
        public bool Success { get; set; }

        /// <summary>
        /// 是否存在逆向冲突
        /// </summary>
        public bool HasReverseConflict { get; set; }

        /// <summary>
        /// 失败原因
        /// </summary>
        public string? FailureReason { get; set; }

        /// <summary>
        /// 锁定的节点Code列表
        /// </summary>
        public List<string> LockedNodeCodes { get; set; } = new();

        /// <summary>
        /// 锁定的边Code列表
        /// </summary>
        public List<string> LockedEdgeCodes { get; set; } = new();
    }
}