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();
}
}