TailPatchApplier.cs
7.5 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
using System;
using System.Collections.Generic;
using System.Linq;
using Rcs.Application.Services.PathFind.Models;
using Rcs.Application.Services.PathFind.Realtime;
using Rcs.Domain.Entities;
using Rcs.Domain.Enums;
namespace Rcs.Infrastructure.PathFinding.Realtime;
/// <summary>
/// 在保留已发送分段的前提下,将尾段补丁应用到未发送缓存区域。
/// </summary>
public sealed class TailPatchApplier : ITailPatchApplier
{
/// <summary>
/// 在缓存中应用尾段补丁,并推进路径版本号。
/// </summary>
/// <param name="cache">目标路径缓存。</param>
/// <param name="patch">待应用补丁。</param>
/// <param name="failureReason">失败原因。</param>
/// <returns>应用是否成功。</returns>
public bool TryApplyPatch(VdaSegmentedPathCache cache, TailPatch patch, out string failureReason)
{
failureReason = string.Empty;
if (cache.PlanVersion != patch.ExpectedPlanVersion)
{
failureReason = $"plan version mismatch, expected={patch.ExpectedPlanVersion}, actual={cache.PlanVersion}";
return false;
}
if (patch.NewTail.Count == 0)
{
failureReason = "patch tail is empty";
return false;
}
if (patch.FromJunctionIndex < 0 || patch.FromJunctionIndex > cache.JunctionSegments.Count)
{
failureReason = $"invalid from junction index: {patch.FromJunctionIndex}";
return false;
}
var rebuilt = BuildPatchedJunctions(cache, patch, out failureReason);
if (rebuilt == null)
{
return false;
}
cache.JunctionSegments = rebuilt;
cache.CurrentJunctionIndex = patch.FromJunctionIndex;
cache.CurrentResourceIndex = patch.FromResourceIndex;
cache.ActivePatchId = patch.PatchId;
cache.LastDecisionCode = $"PatchApplied:{patch.StrategyCode}";
cache.RouteMode = RouteModeCodes.Rejoining;
cache.ConsecutiveConflictFailCount = 0;
cache.BlockedReason = null;
cache.PlanVersion += 1;
return true;
}
/// <summary>
/// 构建补丁应用后的路口分段列表。
/// </summary>
private static List<VdaJunctionSegmentCache>? BuildPatchedJunctions(
VdaSegmentedPathCache cache,
TailPatch patch,
out string failureReason)
{
failureReason = string.Empty;
var result = new List<VdaJunctionSegmentCache>();
for (var j = 0; j < patch.FromJunctionIndex && j < cache.JunctionSegments.Count; j++)
{
result.Add(CloneJunction(cache.JunctionSegments[j]));
}
var hasCurrentJunction = patch.FromJunctionIndex < cache.JunctionSegments.Count;
List<VdaSegmentCacheItem> keptResources = new();
if (hasCurrentJunction)
{
var current = cache.JunctionSegments[patch.FromJunctionIndex];
if (patch.FromResourceIndex < 0 || patch.FromResourceIndex > current.ResourceSegments.Count)
{
failureReason = $"invalid from resource index: {patch.FromResourceIndex}";
return null;
}
keptResources = current.ResourceSegments
.Take(patch.FromResourceIndex)
.Select(CloneResource)
.ToList();
}
var patchJunctions = ConvertPatchTailToCache(patch.NewTail, patch.StrategyCode);
if (patchJunctions.Count == 0)
{
failureReason = "converted patch junction list is empty";
return null;
}
if (keptResources.Count > 0)
{
var first = patchJunctions[0];
var merged = new VdaJunctionSegmentCache
{
ResourceSegments = new List<VdaSegmentCacheItem>()
};
merged.ResourceSegments.AddRange(keptResources);
merged.ResourceSegments.AddRange(first.ResourceSegments);
result.Add(merged);
result.AddRange(patchJunctions.Skip(1));
}
else
{
result.AddRange(patchJunctions);
}
return result;
}
/// <summary>
/// 将补丁尾段结构转换为缓存结构。
/// </summary>
private static List<VdaJunctionSegmentCache> ConvertPatchTailToCache(
List<List<List<PathSegmentWithCode>>> newTail,
string strategyCode)
{
var result = new List<VdaJunctionSegmentCache>();
var segmentOrigin = string.Equals(strategyCode, "TrimProgressAlign", StringComparison.Ordinal)
? "Merged"
: "Avoidance";
foreach (var junction in newTail)
{
var cacheJunction = new VdaJunctionSegmentCache
{
ResourceSegments = new List<VdaSegmentCacheItem>()
};
foreach (var resource in junction)
{
if (resource.Count == 0) continue;
cacheJunction.ResourceSegments.Add(new VdaSegmentCacheItem
{
IsSent = false,
SegmentOrigin = segmentOrigin,
Segments = resource.Select(CloneSegment).ToList()
});
}
if (cacheJunction.ResourceSegments.Count > 0)
{
result.Add(cacheJunction);
}
}
return result;
}
/// <summary>
/// 深拷贝路口分段。
/// </summary>
private static VdaJunctionSegmentCache CloneJunction(VdaJunctionSegmentCache junction)
{
return new VdaJunctionSegmentCache
{
ResourceSegments = junction.ResourceSegments.Select(CloneResource).ToList()
};
}
/// <summary>
/// 深拷贝资源分段。
/// </summary>
private static VdaSegmentCacheItem CloneResource(VdaSegmentCacheItem resource)
{
return new VdaSegmentCacheItem
{
IsSent = resource.IsSent,
SegmentOrigin = resource.SegmentOrigin,
Segments = resource.Segments.Select(CloneSegment).ToList(),
EndNodeNetActionContextIds = new List<string>(resource.EndNodeNetActionContextIds),
StartNodeNetActionContextIds = new List<string>(resource.StartNodeNetActionContextIds),
IsEndNodeNetActionExecuted = resource.IsEndNodeNetActionExecuted,
IsStartNodeNetActionExecuted = resource.IsStartNodeNetActionExecuted
};
}
/// <summary>
/// 深拷贝原子路径段。
/// </summary>
private static PathSegmentWithCode CloneSegment(PathSegmentWithCode segment)
{
return new PathSegmentWithCode
{
EdgeId = segment.EdgeId,
FromNodeId = segment.FromNodeId,
ToNodeId = segment.ToNodeId,
Angle = segment.Angle,
Length = segment.Length,
MaxSpeed = segment.MaxSpeed,
RequiredAngle = segment.RequiredAngle,
StartTheta = segment.StartTheta,
EndTheta = segment.EndTheta,
EdgeCode = segment.EdgeCode,
FromNodeCode = segment.FromNodeCode,
ToNodeCode = segment.ToNodeCode,
ForkAngleOffset = segment.ForkAngleOffset,
StartNodeActions = new List<StepAction>(segment.StartNodeActions),
StartNodeNetActionTypes = new Dictionary<Guid, ActionType>(segment.StartNodeNetActionTypes),
EndNodeActions = new List<StepAction>(segment.EndNodeActions),
EndNodeNetActionTypes = new Dictionary<Guid, ActionType>(segment.EndNodeNetActionTypes)
};
}
}