Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSDriveWay.cpp
15 : /// @author Jakob Erdmann
16 : /// @date December 2021
17 : ///
18 : // A sequende of rail tracks (lanes) that may be used as a "set route" (Fahrstraße)
19 : /****************************************************************************/
20 : #include <config.h>
21 : #include <cassert>
22 : #include <utility>
23 :
24 : #include <utils/xml/SUMOSAXAttributes.h>
25 : #include <utils/common/StringUtils.h>
26 : #include <microsim/MSStop.h>
27 : #include <microsim/MSLane.h>
28 : #include <microsim/MSEdge.h>
29 : #include <microsim/MSLink.h>
30 : #include <microsim/MSNet.h>
31 : #include <microsim/MSVehicleControl.h>
32 : #include <microsim/MSJunctionLogic.h>
33 : #include "MSRailSignal.h"
34 : #include "MSDriveWay.h"
35 : #include "MSRailSignalControl.h"
36 :
37 : // typical block length in germany on main lines is 3-5km on branch lines up to 7km
38 : // special branches that are used by one train exclusively could also be up to 20km in length
39 : // minimum block size in germany is 37.5m (LZB)
40 : // larger countries (USA, Russia) might see blocks beyond 20km)
41 : #define MAX_BLOCK_LENGTH 20000
42 : #define MAX_SIGNAL_WARNINGS 10
43 :
44 : #define DRIVEWAY_SANITY_CHECK
45 : //#define SUBDRIVEWAY_WARN_NOCONFLICT
46 :
47 : //#define DEBUG_BUILD_DRIVEWAY
48 : //#define DEBUG_BUILD_SUBDRIVEWAY
49 : //#define DEBUG_ADD_FOES
50 : //#define DEBUG_BUILD_SIDINGS
51 : //#define DEBUG_DRIVEWAY_BUILDROUTE
52 : //#define DEBUG_CHECK_FLANKS
53 : //#define DEBUG_SIGNALSTATE_PRIORITY
54 : //#define DEBUG_SIGNALSTATE
55 : //#define DEBUG_FIND_PROTECTION
56 : //#define DEBUG_MOVEREMINDER
57 : //#define DEBUG_MATCH
58 :
59 : #define DEBUG_HELPER(obj) ((obj) != nullptr && (obj)->isSelected())
60 : //#define DEBUG_HELPER(obj) ((obj)->getID() == "")
61 : //#define DEBUG_HELPER(obj) (true)
62 :
63 : //#define DEBUG_COND_DW (dw->myNumericalID == 5)
64 : #define DEBUG_COND_DW (false)
65 :
66 : // ===========================================================================
67 : // static value definitions
68 : // ===========================================================================
69 : int MSDriveWay::myGlobalDriveWayIndex(0);
70 : int MSDriveWay::myNumWarnings(0);
71 : bool MSDriveWay::myWriteVehicles(false);
72 : std::map<const MSLink*, std::vector<MSDriveWay*> > MSDriveWay::mySwitchDriveWays;
73 : std::map<const MSEdge*, std::vector<MSDriveWay*> > MSDriveWay::myReversalDriveWays;
74 : std::map<const MSEdge*, std::vector<MSDriveWay*>, ComparatorNumericalIdLess> MSDriveWay::myDepartureDriveways;
75 : std::map<const MSJunction*, int> MSDriveWay::myDepartDrivewayIndex;
76 : std::map<const MSEdge*, std::vector<MSDriveWay*> > MSDriveWay::myDepartureDrivewaysEnds;
77 : std::map<const MSEdge*, std::vector<MSDriveWay*>, ComparatorNumericalIdLess> MSDriveWay::myEndingDriveways;
78 : std::map<ConstMSEdgeVector, MSDriveWay*> MSDriveWay::myDriveWayRouteLookup;
79 : std::map<std::string, MSDriveWay*> MSDriveWay::myDriveWayLookup;
80 :
81 : // ---------------------------------------------------------------------------
82 : // static initialisation methods
83 : // ---------------------------------------------------------------------------
84 : void
85 42753 : MSDriveWay::init() {
86 42753 : myWriteVehicles = OptionsCont::getOptions().isSet("railsignal-vehicle-output");
87 42753 : }
88 :
89 : // ===========================================================================
90 : // MSDriveWay method definitions
91 : // ===========================================================================
92 :
93 :
94 15549 : MSDriveWay::MSDriveWay(const MSLink* origin, const std::string& id, bool temporary) :
95 31098 : MSMoveReminder("DriveWay_" + (temporary ? "tmp" : id)),
96 : Named(id),
97 7590 : myNumericalID(temporary ? -1 : myGlobalDriveWayIndex++),
98 15549 : myOrigin(origin),
99 15549 : myMaxFlankLength(0),
100 15549 : myActive(nullptr),
101 15549 : myCoreSize(0),
102 15549 : myFoundSignal(false),
103 15549 : myFoundJump(false),
104 15549 : myTerminateRoute(false),
105 15549 : myAbortedBuild(false),
106 15549 : myBidiEnded(false),
107 54237 : myIsSubDriveway(false)
108 15549 : {}
109 :
110 :
111 23139 : MSDriveWay::~MSDriveWay() {
112 17218 : for (const MSDriveWay* sub : mySubDriveWays) {
113 1669 : delete sub;
114 : }
115 : mySubDriveWays.clear();
116 69786 : }
117 :
118 : void
119 40044 : MSDriveWay::cleanup() {
120 40044 : myGlobalDriveWayIndex = 0;
121 40044 : myNumWarnings = 0;
122 40044 : myWriteVehicles = false;
123 :
124 42521 : for (auto item : myDepartureDriveways) {
125 5057 : for (MSDriveWay* dw : item.second) {
126 2580 : delete dw;
127 : }
128 : }
129 : MSDriveWay::mySwitchDriveWays.clear();
130 : MSDriveWay::myReversalDriveWays.clear();
131 : MSDriveWay::myDepartureDriveways.clear();
132 : MSDriveWay::myDepartDrivewayIndex.clear();
133 : MSDriveWay::myDepartureDrivewaysEnds.clear();
134 : MSDriveWay::myEndingDriveways.clear();
135 40044 : }
136 :
137 : void
138 186 : MSDriveWay::clearState() {
139 211 : for (auto item : myEndingDriveways) {
140 50 : for (MSDriveWay* dw : item.second) {
141 : dw->myTrains.clear();
142 : }
143 : }
144 186 : }
145 :
146 :
147 : bool
148 14194 : MSDriveWay::notifyEnter(SUMOTrafficObject& veh, Notification reason, const MSLane* enteredLane) {
149 : UNUSED_PARAMETER(reason);
150 : UNUSED_PARAMETER(enteredLane);
151 : #ifdef DEBUG_MOVEREMINDER
152 : std::cout << SIMTIME << " notifyEnter " << getDescription() << " veh=" << veh.getID() << " lane=" << enteredLane->getID() << " reason=" << reason << "\n";
153 : #endif
154 14194 : if (veh.isVehicle() && enteredLane == myLane && (reason == NOTIFICATION_DEPARTED || reason == NOTIFICATION_JUNCTION || reason == NOTIFICATION_PARKING)) {
155 14148 : SUMOVehicle& sveh = dynamic_cast<SUMOVehicle&>(veh);
156 14148 : MSRouteIterator firstIt = std::find(sveh.getCurrentRouteEdge(), sveh.getRoute().end(), myLane->getNextNormal());
157 14142 : if (myTrains.count(&sveh) == 0 && match(firstIt, sveh.getRoute().end())) {
158 12254 : myTrains.insert(&sveh);
159 12254 : if (myOrigin != nullptr) {
160 7632 : MSRailSignalControl::getInstance().notifyApproach(myOrigin);
161 : }
162 31909 : for (const MSDriveWay* foe : myFoes) {
163 19655 : if (foe->myOrigin != nullptr) {
164 13517 : MSRailSignalControl::getInstance().notifyApproach(foe->myOrigin);
165 : }
166 : }
167 12254 : if (myWriteVehicles) {
168 946 : myVehicleEvents.push_back(VehicleEvent(SIMSTEP, true, veh.getID(), reason));
169 : }
170 12254 : return true;
171 : }
172 : }
173 : return false;
174 : }
175 :
176 :
177 : bool
178 51782 : MSDriveWay::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, Notification reason, const MSLane* enteredLane) {
179 : UNUSED_PARAMETER(reason);
180 : UNUSED_PARAMETER(enteredLane);
181 : #ifdef DEBUG_MOVEREMINDER
182 : std::cout << SIMTIME << " notifyLeave " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(enteredLane) << " reason=" << toString(reason) << "\n";
183 : #endif
184 51782 : if (veh.isVehicle()) {
185 : // leaving network with departure, teleport etc
186 51782 : if (reason != MSMoveReminder::NOTIFICATION_JUNCTION && reason != MSMoveReminder::NOTIFICATION_SEGMENT) {
187 3604 : myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
188 3604 : if (myWriteVehicles) {
189 228 : myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
190 : }
191 3604 : return false;
192 : } else {
193 : return true;
194 : }
195 : } else {
196 : return false;
197 : }
198 : }
199 :
200 :
201 : bool
202 52384 : MSDriveWay::notifyLeaveBack(SUMOTrafficObject& veh, Notification reason, const MSLane* leftLane) {
203 : UNUSED_PARAMETER(reason);
204 : UNUSED_PARAMETER(leftLane);
205 : #ifdef DEBUG_MOVEREMINDER
206 : std::cout << SIMTIME << " notifyLeaveBack " << getDescription() << " veh=" << veh.getID() << " lane=" << Named::getIDSecure(leftLane) << " reason=" << toString(reason) << "\n";
207 : #endif
208 52384 : if (veh.isVehicle()) {
209 52384 : if (leftLane == myForward.back() && veh.getBackLane() != leftLane->getBidiLane()) {
210 8768 : myTrains.erase(&dynamic_cast<SUMOVehicle&>(veh));
211 8768 : if (myWriteVehicles) {
212 718 : myVehicleEvents.push_back(VehicleEvent(SIMSTEP, false, veh.getID(), reason));
213 : }
214 8768 : return false;
215 : } else {
216 43616 : return true;
217 : }
218 : } else {
219 : return false;
220 : }
221 : }
222 :
223 :
224 : bool
225 401278 : MSDriveWay::reserve(const Approaching& closest, MSEdgeVector& occupied) {
226 401278 : if (foeDriveWayOccupied(true, closest.first, occupied)) {
227 : return false;
228 : }
229 389578 : for (MSLink* foeLink : myConflictLinks) {
230 112141 : if (hasLinkConflict(closest, foeLink)) {
231 : #ifdef DEBUG_SIGNALSTATE
232 : if (gDebugFlag4 || DEBUG_HELPER(closest.first)) {
233 : std::cout << getID() << " linkConflict with " << getTLLinkID(foeLink) << "\n";
234 : }
235 : #endif
236 : return false;
237 : }
238 : }
239 277437 : myActive = closest.first;
240 277437 : return true;
241 : }
242 :
243 :
244 : bool
245 112141 : MSDriveWay::hasLinkConflict(const Approaching& veh, const MSLink* foeLink) const {
246 : #ifdef DEBUG_SIGNALSTATE_PRIORITY
247 : if (gDebugFlag4) {
248 : std::cout << " checkLinkConflict foeLink=" << getTLLinkID(foeLink) << " ego=" << Named::getIDSecure(veh.first) << "\n";
249 : }
250 : #endif
251 112141 : if (foeLink->getApproaching().size() > 0) {
252 37446 : Approaching foe = foeLink->getClosest();
253 : #ifdef DEBUG_SIGNALSTATE_PRIORITY
254 : if (gDebugFlag4) {
255 : std::cout << " approaching foe=" << foe.first->getID() << "\n";
256 : }
257 : #endif
258 37446 : if (foe.first == veh.first) {
259 37446 : return false;
260 : }
261 : const MSTrafficLightLogic* foeTLL = foeLink->getTLLogic();
262 : assert(foeTLL != nullptr);
263 33154 : const MSRailSignal* constFoeRS = dynamic_cast<const MSRailSignal*>(foeTLL);
264 : MSRailSignal* foeRS = const_cast<MSRailSignal*>(constFoeRS);
265 33154 : if (foeRS != nullptr) {
266 33154 : const MSDriveWay& foeDriveWay = foeRS->retrieveDriveWayForVeh(foeLink->getTLIndex(), foe.first);
267 : MSEdgeVector occupied;
268 52416 : if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied) ||
269 31695 : !foeRS->constraintsAllow(foe.first) ||
270 24308 : !overlap(foeDriveWay) ||
271 45029 : !isFoeOrSubFoe(&foeDriveWay) ||
272 9736 : canUseSiding(veh.first, &foeDriveWay).first) {
273 : #ifdef DEBUG_SIGNALSTATE_PRIORITY
274 : if (gDebugFlag4) {
275 : if (foeDriveWay.foeDriveWayOccupied(false, foe.first, occupied)) {
276 : std::cout << " foe blocked\n";
277 : } else if (!foeRS->constraintsAllow(foe.first)) {
278 : std::cout << " foe constrained\n";
279 : } else if (!overlap(foeDriveWay)) {
280 : std::cout << " no overlap\n";
281 : } else if (!isFoeOrSubFoe(&foeDriveWay)) {
282 : std::cout << " foeDW=" << foeDriveWay.getID() << " is not a foe to " << getID() << "\n";
283 : } else if (canUseSiding(veh.first, &foeDriveWay).first) {
284 : std::cout << " use siding\n";
285 : }
286 : }
287 : #endif
288 23494 : return false;
289 : }
290 : #ifdef DEBUG_SIGNALSTATE_PRIORITY
291 : if (gDebugFlag4) {
292 : std::cout
293 : << " aSB=" << veh.second.arrivalSpeedBraking << " foeASB=" << foe.second.arrivalSpeedBraking
294 : << " aT=" << veh.second.arrivalTime << " foeAT=" << foe.second.arrivalTime
295 : << " aS=" << veh.first->getSpeed() << " foeS=" << foe.first->getSpeed()
296 : << " aD=" << veh.second.dist << " foeD=" << foe.second.dist
297 : << " aW=" << veh.first->getWaitingTime() << " foeW=" << foe.first->getWaitingTime()
298 : << " aN=" << veh.first->getNumericalID() << " foeN=" << foe.first->getNumericalID()
299 : << "\n";
300 : }
301 : #endif
302 9660 : const bool yield = mustYield(veh, foe);
303 9660 : if (MSRailSignal::storeVehicles()) {
304 540 : MSRailSignal::rivalVehicles().push_back(foe.first);
305 540 : if (yield) {
306 315 : MSRailSignal::priorityVehicles().push_back(foe.first);
307 : }
308 : }
309 9660 : return yield;
310 33154 : }
311 : }
312 : return false;
313 : }
314 :
315 :
316 : bool
317 20737 : MSDriveWay::isFoeOrSubFoe(const MSDriveWay* foe) const {
318 20737 : if (std::find(myFoes.begin(), myFoes.end(), foe) != myFoes.end()) {
319 : return true;
320 : }
321 14614 : for (const MSDriveWay* sub : foe->mySubDriveWays) {
322 8862 : if (isFoeOrSubFoe(sub)) {
323 : return true;
324 : }
325 : }
326 : return false;
327 : }
328 :
329 :
330 : bool
331 9660 : MSDriveWay::mustYield(const Approaching& veh, const Approaching& foe) {
332 9660 : if (foe.second.arrivalSpeedBraking == veh.second.arrivalSpeedBraking) {
333 5382 : if (foe.second.arrivalTime == veh.second.arrivalTime) {
334 536 : if (foe.first->getSpeed() == veh.first->getSpeed()) {
335 518 : if (foe.second.dist == veh.second.dist) {
336 486 : if (foe.first->getWaitingTime() == veh.first->getWaitingTime()) {
337 468 : return foe.first->getNumericalID() < veh.first->getNumericalID();
338 : } else {
339 18 : return foe.first->getWaitingTime() > veh.first->getWaitingTime();
340 : }
341 : } else {
342 32 : return foe.second.dist < veh.second.dist;
343 : }
344 : } else {
345 18 : return foe.first->getSpeed() > veh.first->getSpeed();
346 : }
347 : } else {
348 4846 : return foe.second.arrivalTime < veh.second.arrivalTime;
349 : }
350 : } else {
351 4278 : return foe.second.arrivalSpeedBraking > veh.second.arrivalSpeedBraking;
352 : }
353 : }
354 :
355 :
356 : bool
357 15879 : MSDriveWay::conflictLaneOccupied(bool store, const SUMOVehicle* ego) const {
358 203007 : for (const MSLane* lane : myConflictLanes) {
359 : if (!lane->isEmpty()) {
360 4410 : std::string joinVehicle = "";
361 4410 : if (ego != nullptr && !MSGlobals::gUseMesoSim) {
362 0 : const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
363 0 : if (stop != nullptr) {
364 0 : joinVehicle = stop->join;
365 : }
366 : }
367 : #ifdef DEBUG_SIGNALSTATE
368 : if (gDebugFlag4) {
369 : std::cout << SIMTIME << " conflictLane " << lane->getID() << " occupied ego=" << Named::getIDSecure(ego) << " vehNumber=" << lane->getVehicleNumber() << "\n";
370 : if (joinVehicle != "") {
371 : std::cout << " joinVehicle=" << joinVehicle << " occupant=" << toString(lane->getVehiclesSecure()) << "\n";
372 : lane->releaseVehicles();
373 : }
374 : }
375 : #endif
376 4410 : if (lane->getVehicleNumberWithPartials() == 1) {
377 4410 : MSVehicle* foe = lane->getLastAnyVehicle();
378 4410 : if (joinVehicle != "") {
379 0 : if (foe->getID() == joinVehicle && foe->isStopped()) {
380 : #ifdef DEBUG_SIGNALSTATE
381 : if (gDebugFlag4) {
382 : std::cout << " ignore join-target '" << joinVehicle << "\n";
383 : }
384 : #endif
385 0 : continue;
386 : }
387 : }
388 4410 : if (ego != nullptr) {
389 0 : if (foe == ego && std::find(myForward.begin(), myForward.end(), lane) == myForward.end()) {
390 : #ifdef DEBUG_SIGNALSTATE
391 : if (gDebugFlag4) {
392 : std::cout << " ignore ego as oncoming '" << ego->getID() << "\n";
393 : }
394 : #endif
395 0 : continue;
396 : }
397 0 : if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
398 : #ifdef DEBUG_SIGNALSTATE
399 : if (gDebugFlag4) {
400 : std::cout << " ignore " << foe->getID() << " for which ego is join-target\n";
401 : }
402 : #endif
403 0 : continue;
404 : }
405 : }
406 : }
407 4410 : if (MSRailSignal::storeVehicles() && store) {
408 4410 : MSRailSignal::blockingVehicles().push_back(lane->getLastAnyVehicle());
409 : }
410 : return true;
411 : }
412 : }
413 11469 : return false;
414 : }
415 :
416 :
417 : bool
418 78178 : MSDriveWay::foeDriveWayApproached() const {
419 229154 : for (const MSDriveWay* foeDW : myFoes) {
420 181441 : if (foeDW->myOrigin != nullptr && foeDW->myOrigin->getApproaching().size() > 0) {
421 : #ifdef DEBUG_SIGNALSTATE
422 : if (gDebugFlag4) {
423 : std::cout << SIMTIME << " foeLink=" << foeDW->myOrigin->getDescription() << " approachedBy=" << foeDW->myOrigin->getApproaching().begin()->first->getID() << "\n";
424 : }
425 : #endif
426 : return true;
427 : }
428 : }
429 : return false;
430 : }
431 :
432 :
433 : bool
434 827614 : MSDriveWay::foeDriveWayOccupied(bool store, const SUMOVehicle* ego, MSEdgeVector& occupied) const {
435 1904121 : for (const MSDriveWay* foeDW : myFoes) {
436 1530562 : if (!foeDW->myTrains.empty()) {
437 : #ifdef DEBUG_SIGNALSTATE
438 : if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
439 : std::cout << SIMTIME << " " << getID() << " foeDriveWay " << foeDW->getID() << " occupied ego=" << Named::getIDSecure(ego) << " foeVeh=" << toString(foeDW->myTrains) << "\n";
440 : }
441 : #endif
442 472661 : if (foeDW->myTrains.size() == 1) {
443 472463 : SUMOVehicle* foe = *foeDW->myTrains.begin();
444 472463 : if (foe == ego) {
445 : #ifdef DEBUG_SIGNALSTATE
446 : if (gDebugFlag4 || DEBUG_HELPER(ego)) {
447 : std::cout << " ignore ego as foe '" << Named::getIDSecure(ego) << "\n";
448 : }
449 : #endif
450 18706 : continue;
451 : }
452 456253 : if (hasJoin(ego, foe)) {
453 36 : continue;
454 : }
455 : }
456 456415 : std::pair<bool, const MSDriveWay*> useSiding = canUseSiding(ego, foeDW);
457 : #ifdef DEBUG_SIGNALSTATE
458 : if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
459 : auto it = mySidings.find(foeDW);
460 : int numSidings = 0;
461 : if (it != mySidings.end()) {
462 : numSidings = it->second.size();
463 : }
464 : std::cout << " useSiding=" << useSiding.first << " sidingFoe=" << Named::getIDSecure(useSiding.second) << " numSidings=" << numSidings << "\n";
465 : }
466 : #endif
467 456415 : if (useSiding.first) {
468 2460 : continue;
469 : } else {
470 453955 : if (MSRailSignal::storeVehicles() && store) {
471 736 : for (SUMOVehicle* foe : foeDW->myTrains) {
472 368 : MSRailSignal::blockingVehicles().push_back(foe);
473 : }
474 368 : MSRailSignal::blockingDriveWays().push_back(foeDW);
475 : }
476 908340 : for (const SUMOVehicle* foe : foeDW->myTrains) {
477 454385 : occupied.push_back(const_cast<MSEdge*>(foe->getEdge()));
478 454385 : MSEdge* bidi = const_cast<MSEdge*>(foe->getEdge()->getBidiEdge());
479 454385 : if (bidi != nullptr) {
480 328581 : occupied.push_back(bidi);
481 : }
482 : /// @todo: if foe occupies more than one edge we should add all of them to the occupied vector
483 : }
484 164716 : if (ego != nullptr && MSGlobals::gTimeToTeleportRSDeadlock > 0
485 582622 : && (ego->getWaitingTime() > ego->getVehicleType().getCarFollowModel().getStartupDelay() || !ego->isOnRoad())) {
486 : // if there is an occupied siding, it becomes part of the waitRelation
487 56324 : SUMOVehicle* foe = *(useSiding.second == nullptr ? foeDW : useSiding.second)->myTrains.begin();
488 56324 : const MSRailSignal* rs = myOrigin != nullptr ? dynamic_cast<const MSRailSignal*>(myOrigin->getTLLogic()) : nullptr;
489 56324 : MSRailSignalControl::getInstance().addWaitRelation(ego, rs, foe);
490 : }
491 453955 : return true;
492 : }
493 1057901 : } else if (foeDW != this && isDepartDriveway() && !foeDW->isDepartDriveway()) {
494 8690 : if (foeDW->myOrigin->getApproaching().size() > 0) {
495 1363 : Approaching foeA = foeDW->myOrigin->getClosest();
496 1363 : const SUMOVehicle* foe = foeA.first;
497 1363 : if (foeA.second.dist < foe->getBrakeGap(true)) {
498 131 : MSRouteIterator firstIt = std::find(foe->getCurrentRouteEdge(), foe->getRoute().end(), foeDW->myRoute.front());
499 131 : if (firstIt != foe->getRoute().end()) {
500 131 : if (foeDW->match(firstIt, foe->getRoute().end())) {
501 100 : bool useSiding = canUseSiding(ego, foeDW).first;
502 : #ifdef DEBUG_SIGNALSTATE
503 : if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
504 : std::cout << SIMTIME << " " << getID() << " blocked by " << foeDW->getID() << " (approached by " << foe->getID() << ") useSiding=" << useSiding << "\n";
505 : }
506 : #endif
507 100 : if (useSiding) {
508 : //std::cout << SIMTIME << " " << getID() << " ego=" << ego->getID() << " foeDW=" << foeDW->getID() << " myFoes=" << toString(myFoes) << "\n";
509 0 : continue;
510 : } else {
511 100 : return true;
512 : }
513 : }
514 : }
515 : }
516 : }
517 : }
518 : }
519 374387 : for (const std::set<const MSDriveWay*>& dlFoes : myDeadlocks) {
520 : bool allOccupied = true;
521 7748 : for (const MSDriveWay* dlFoe : dlFoes) {
522 5938 : if (dlFoe->myTrains.empty()) {
523 : allOccupied = false;
524 : //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck clear " << dlFoe->getID() << "\n";
525 : break;
526 : }
527 : }
528 2638 : if (allOccupied) {
529 : #ifdef DEBUG_SIGNALSTATE
530 : if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
531 : std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " deadlockCheck " << joinNamedToString(dlFoes, " ") << "\n";
532 : }
533 : #endif
534 6502 : for (const MSDriveWay* dlFoe : dlFoes) {
535 4692 : MSRailSignal::blockingDriveWays().push_back(dlFoe);
536 : }
537 : return true;
538 : }
539 : }
540 : return false;
541 : }
542 :
543 :
544 : bool
545 456253 : MSDriveWay::hasJoin(const SUMOVehicle* ego, const SUMOVehicle* foe) {
546 456253 : if (ego != nullptr && !MSGlobals::gUseMesoSim) {
547 166096 : std::string joinVehicle = "";
548 166096 : const SUMOVehicleParameter::Stop* stop = ego->getNextStopParameter();
549 166096 : if (stop != nullptr) {
550 85637 : joinVehicle = stop->join;
551 : }
552 166096 : if (joinVehicle == "" && !ego->hasDeparted() && ego->getStops().size() > 1) {
553 : // check one more stop
554 4277 : auto it = ego->getStops().begin();
555 : std::advance(it, 1);
556 4277 : joinVehicle = it->pars.join;
557 : }
558 166096 : if (joinVehicle != "") {
559 : #ifdef DEBUG_SIGNALSTATE
560 : if (gDebugFlag4 || DEBUG_COND_DW) {
561 : std::cout << " joinVehicle=" << joinVehicle << "\n";
562 : }
563 : #endif
564 345 : if (foe->getID() == joinVehicle && foe->isStopped()) {
565 : #ifdef DEBUG_SIGNALSTATE
566 : if (gDebugFlag4 || DEBUG_COND_DW) {
567 : std::cout << " ignore join-target '" << joinVehicle << "\n";
568 : }
569 : #endif
570 : return true;
571 : }
572 : }
573 :
574 166072 : if (foe->isStopped() && foe->getNextStopParameter()->join == ego->getID()) {
575 : #ifdef DEBUG_SIGNALSTATE
576 : if (gDebugFlag4 || DEBUG_COND_DW) {
577 : std::cout << " ignore " << foe->getID() << " for which ego is join-target\n";
578 : }
579 : #endif
580 : return true;
581 : }
582 : }
583 : return false;
584 : }
585 :
586 :
587 : std::pair<bool, const MSDriveWay*>
588 468583 : MSDriveWay::canUseSiding(const SUMOVehicle* ego, const MSDriveWay* foe, bool recurse) const {
589 : auto it = mySidings.find(foe);
590 468583 : if (it != mySidings.end()) {
591 7957 : for (auto siding : it->second) {
592 : // assume siding is usuable when computing state for unapproached signal (ego == nullptr)
593 7783 : if (ego == nullptr || siding.length >= ego->getLength()) {
594 : // if the siding is already "reserved" by another vehicle we cannot use it here
595 5807 : const MSEdge* sidingEnd = myRoute[siding.end];
596 11596 : for (MSDriveWay* sidingApproach : myEndingDriveways[sidingEnd]) {
597 8866 : if (!sidingApproach->myTrains.empty()) {
598 : // possibly the foe vehicle can use the other part of the siding
599 3271 : if (recurse) {
600 : const SUMOVehicle* foeVeh = nullptr;
601 2332 : if (!foe->myTrains.empty()) {
602 2324 : foeVeh = *foe->myTrains.begin();
603 8 : } else if (foe->myOrigin != nullptr && foe->myOrigin->getApproaching().size() > 0) {
604 8 : foeVeh = foe->myOrigin->getClosest().first;
605 : }
606 2332 : if (foeVeh == nullptr) {
607 0 : WRITE_WARNINGF("Invalid call to canUseSiding dw=% foe=% ego=% time=%", getID(), foe->getID(), Named::getIDSecure(ego), time2string(SIMSTEP));
608 0 : continue;
609 : }
610 2332 : if (foe->canUseSiding(foeVeh, this, false).first) {
611 194 : continue;
612 : }
613 : }
614 : // possibly the foe vehicle
615 : // @todo: in principle it might still be possible to continue if vehicle that approaches the siding can safely leave the situation
616 : #ifdef DEBUG_SIGNALSTATE
617 : if (gDebugFlag4 || DEBUG_COND_DW || DEBUG_HELPER(ego)) {
618 : std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
619 : << " foeVeh=" << toString(foe->myTrains)
620 : << " sidingEnd=" << sidingEnd->getID() << " sidingApproach=" << sidingApproach->getID() << " approaching=" << toString(sidingApproach->myTrains) << "\n";
621 : }
622 : #endif
623 3077 : return std::make_pair(false, sidingApproach);
624 : }
625 : }
626 : //std::cout << SIMTIME << " " << getID() << " ego=" << Named::getIDSecure(ego) << " foe=" << foe->getID()
627 : // << " foeVeh=" << toString(foe->myTrains)
628 : // << " sidingEnd=" << sidingEnd->getID() << "usable\n";
629 2730 : return std::make_pair(true, nullptr);
630 : }
631 : }
632 : }
633 462776 : return std::make_pair(false, nullptr);
634 : }
635 :
636 : bool
637 15426 : MSDriveWay::overlap(const MSDriveWay& other) const {
638 34630 : for (int i = 0; i < myCoreSize; i++) {
639 140618 : for (int j = 0; j < other.myCoreSize; j++) {
640 121414 : const MSEdge* edge = myRoute[i];
641 121414 : const MSEdge* edge2 = other.myRoute[j];
642 : if (edge->getToJunction() == edge2->getToJunction()
643 121414 : || edge->getToJunction() == edge2->getFromJunction()) {
644 : // XXX might be rail_crossing with parallel tracks
645 : return true;
646 : }
647 : }
648 : }
649 : return false;
650 : }
651 :
652 :
653 : bool
654 18964 : MSDriveWay::flankConflict(const MSDriveWay& other) const {
655 48042 : for (const MSLane* lane : myForward) {
656 173610 : for (const MSLane* lane2 : other.myForward) {
657 140021 : if (lane == lane2) {
658 : return true;
659 : }
660 : }
661 278376 : for (const MSLane* lane2 : other.myBidi) {
662 247134 : if (lane == lane2) {
663 4399 : if (bidiBlockedBy(other)) {
664 : // it's only a deadlock if both trains block symmetrically
665 : return true;
666 : }
667 : }
668 : }
669 253542 : for (const MSLane* lane2 : other.myBidiExtended) {
670 224464 : if (lane == lane2) {
671 7591 : if (bidiBlockedBy(other)) {
672 : // it's only a deadlock if both trains block symmetrically
673 : return true;
674 : }
675 : }
676 : }
677 : }
678 : return false;
679 : }
680 :
681 :
682 : bool
683 12075 : MSDriveWay::crossingConflict(const MSDriveWay& other) const {
684 34636 : for (const MSLane* lane : myForward) {
685 112451 : for (const MSLane* lane2 : other.myForward) {
686 89890 : if (lane->isNormal() && lane2->isNormal() && lane->getEdge().getToJunction() == lane2->getEdge().getToJunction()) {
687 : return true;
688 : }
689 : }
690 : }
691 : return false;
692 : }
693 :
694 :
695 : bool
696 12262 : MSDriveWay::bidiBlockedBy(const MSDriveWay& other) const {
697 85615 : for (const MSLane* lane : myBidi) {
698 302208 : for (const MSLane* lane2 : other.myForward) {
699 228855 : if (lane == lane2) {
700 : return true;
701 : }
702 : }
703 : }
704 42136 : for (const MSLane* lane : myBidiExtended) {
705 145868 : for (const MSLane* lane2 : other.myForward) {
706 113237 : if (lane == lane2) {
707 1826 : if (overlap(other)) {
708 : return true;
709 : }
710 : }
711 : }
712 : }
713 : return false;
714 : }
715 :
716 :
717 : bool
718 6556 : MSDriveWay::bidiBlockedByEnd(const MSDriveWay& other) const {
719 6556 : const MSLane* end = other.myForward.back();
720 45939 : for (const MSLane* lane : myBidi) {
721 41879 : if (lane == end) {
722 : return true;
723 : }
724 : }
725 17591 : for (const MSLane* lane : myBidiExtended) {
726 14698 : if (lane == end) {
727 1167 : if (overlap(other)) {
728 : return true;
729 : }
730 : }
731 : }
732 : return false;
733 : }
734 :
735 : bool
736 1003 : MSDriveWay::forwardRouteConflict(std::set<const MSEdge*> forward, const MSDriveWay& other, bool secondCheck) {
737 : int i = 0;
738 4088 : for (const MSEdge* edge2 : other.myRoute) {
739 4009 : if (i == other.myCoreSize) {
740 : return false;
741 : }
742 4009 : i++;
743 4009 : if (edge2 == myForward.front()->getNextNormal() && !secondCheck) {
744 : // foe should not pass from behind through our own forward section
745 : return false;
746 : }
747 : if (forward.count(edge2->getBidiEdge()) != 0) {
748 : return true;
749 : }
750 : }
751 : return false;
752 : }
753 :
754 : void
755 5783 : MSDriveWay::writeBlocks(OutputDevice& od) const {
756 10018 : od.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
757 5783 : od.writeAttr(SUMO_ATTR_ID, myID);
758 5783 : od.writeAttr(SUMO_ATTR_VEHICLE, myFirstVehicle);
759 5783 : od.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
760 5783 : if (myCoreSize != (int)myRoute.size()) {
761 0 : od.writeAttr("core", myCoreSize);
762 : }
763 5783 : od.openTag("forward");
764 5783 : od.writeAttr(SUMO_ATTR_LANES, toString(myForward));
765 5783 : od.closeTag();
766 5783 : if (!myIsSubDriveway) {
767 4235 : od.openTag("bidi");
768 8470 : od.writeAttr(SUMO_ATTR_LANES, toString(myBidi));
769 4235 : if (myBidiExtended.size() > 0) {
770 1027 : od.lf();
771 1027 : od << " ";
772 2054 : od.writeAttr("deadlockCheck", toString(myBidiExtended));
773 : }
774 4235 : od.closeTag();
775 4235 : od.openTag("flank");
776 4235 : od.writeAttr(SUMO_ATTR_LANES, toString(myFlank));
777 4235 : od.closeTag();
778 :
779 8470 : od.openTag("conflictLinks");
780 :
781 : std::vector<std::string> signals;
782 7149 : for (MSLink* link : myConflictLinks) {
783 5828 : signals.push_back(getTLLinkID(link));
784 : }
785 8470 : od.writeAttr("signals", joinToStringSorting(signals, " "));
786 8470 : od.closeTag();
787 :
788 : std::vector<std::string> foes;
789 15348 : for (MSDriveWay* dw : myFoes) {
790 11113 : foes.push_back(dw->myID);
791 : }
792 4235 : if (foes.size() > 0) {
793 4211 : od.openTag("foes");
794 8422 : od.writeAttr("driveWays", joinToStringSorting(foes, " "));
795 8422 : od.closeTag();
796 : }
797 4610 : for (auto item : mySidings) {
798 375 : od.openTag("sidings");
799 750 : od.writeAttr("foe", item.first->getID());
800 903 : for (auto siding : item.second) {
801 528 : od.openTag("siding");
802 528 : od.writeAttr("start", myRoute[siding.start]->getID());
803 528 : od.writeAttr("end", myRoute[siding.end]->getID());
804 528 : od.writeAttr("length", siding.length);
805 1056 : od.closeTag();
806 : }
807 750 : od.closeTag();
808 : }
809 4316 : for (auto item : myDeadlocks) {
810 81 : od.openTag("deadlock");
811 162 : od.writeAttr("foes", joinNamedToStringSorting(item, " "));
812 162 : od.closeTag();
813 : }
814 4235 : }
815 11566 : od.closeTag(); // driveWay
816 :
817 7331 : for (const MSDriveWay* sub : mySubDriveWays) {
818 1548 : sub->writeBlocks(od);
819 : }
820 : #ifdef DRIVEWAY_SANITY_CHECK
821 5783 : std::set<MSDriveWay*> uFoes(myFoes.begin(), myFoes.end());
822 5783 : if (uFoes.size() != myFoes.size()) {
823 0 : WRITE_WARNINGF("Duplicate foes in driveway '%'", getID());
824 :
825 : }
826 : #endif
827 5783 : }
828 :
829 :
830 : void
831 550 : MSDriveWay::writeBlockVehicles(OutputDevice& od) const {
832 974 : od.openTag(myIsSubDriveway ? "subDriveWay" : "driveWay");
833 550 : od.writeAttr(SUMO_ATTR_ID, myID);
834 1496 : for (const VehicleEvent& ve : myVehicleEvents) {
835 1419 : od.openTag(ve.isEntry ? "entry" : "exit");
836 946 : od.writeAttr(SUMO_ATTR_ID, ve.id);
837 946 : od.writeAttr(SUMO_ATTR_TIME, time2string(ve.time));
838 946 : od.writeAttr("reason", ve.reason);
839 1892 : od.closeTag(); // event
840 : }
841 1100 : od.closeTag(); // driveWay
842 :
843 676 : for (const MSDriveWay* sub : mySubDriveWays) {
844 126 : sub->writeBlockVehicles(od);
845 : }
846 550 : }
847 :
848 :
849 : void
850 5921 : MSDriveWay::buildRoute(const MSLink* origin, double length,
851 : MSRouteIterator next, MSRouteIterator end,
852 : LaneVisitedMap& visited,
853 : std::set<MSLink*>& flankSwitches) {
854 : bool seekForwardSignal = true;
855 : bool seekBidiSwitch = true;
856 : bool foundUnsafeSwitch = false;
857 5921 : MSLane* toLane = origin ? origin->getViaLaneOrLane() : (*next)->getLanes()[0];
858 11842 : const std::string warnID = origin ? "rail signal " + getClickableTLLinkID(origin) : "insertion lane '" + toLane->getID() + "'";
859 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
860 : gDebugFlag4 = DEBUG_COND_DW;
861 : if (gDebugFlag4) std::cout << "buildRoute origin=" << warnID << " vehRoute=" << toString(ConstMSEdgeVector(next, end))
862 : << " visited=" << formatVisitedMap(visited) << "\n";
863 : #endif
864 38779 : while ((seekForwardSignal || seekBidiSwitch)) {
865 36894 : if (length > MAX_BLOCK_LENGTH) {
866 56 : if (myNumWarnings < MAX_SIGNAL_WARNINGS) {
867 224 : WRITE_WARNING("Block after " + warnID +
868 : " exceeds maximum length (stopped searching after edge '" + toLane->getEdge().getID() + "' (length=" + toString(length) + "m).");
869 : }
870 56 : myNumWarnings++;
871 56 : myAbortedBuild = true;
872 : // length exceeded
873 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
874 : if (gDebugFlag4) {
875 : std::cout << " abort: length=" << length << "\n";
876 : }
877 : #endif
878 4036 : return;
879 : }
880 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
881 : if (gDebugFlag4) {
882 : std::cout << " toLane=" << toLane->getID() << " visited=" << formatVisitedMap(visited) << "\n";
883 : }
884 : #endif
885 36838 : const MSEdge* current = &toLane->getEdge();
886 36838 : if (current->isNormal()) {
887 24109 : myRoute.push_back(current);
888 24109 : if (next != end) {
889 : next++;
890 : }
891 : }
892 36838 : appendMapIndex(visited, toLane);
893 36838 : length += toLane->getLength();
894 36838 : MSLane* bidi = toLane->getBidiLane();
895 36838 : if (seekForwardSignal) {
896 17840 : if (!foundUnsafeSwitch) {
897 17840 : myForward.push_back(toLane);
898 17840 : if (myForward.size() == 1) {
899 5921 : myLane = toLane;
900 5921 : toLane->addMoveReminder(this);
901 : }
902 : }
903 18998 : } else if (bidi == nullptr) {
904 746 : if (toLane->isInternal() && toLane->getIncomingLanes().front().viaLink->isTurnaround()) {
905 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
906 : if (gDebugFlag4) {
907 : std::cout << " continue bidiSearch beyond turnaround\n";
908 : }
909 : #endif
910 : } else {
911 : seekBidiSwitch = false;
912 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
913 : if (gDebugFlag4) {
914 : std::cout << " noBidi, abort search for bidiSwitch\n";
915 : }
916 : #endif
917 : }
918 : }
919 36838 : if (bidi != nullptr) {
920 29535 : if (foundUnsafeSwitch) {
921 7904 : myBidiExtended.push_back(bidi);
922 : } else {
923 21631 : myBidi.push_back(bidi);
924 : }
925 29535 : if (!seekForwardSignal) {
926 : // look for switch that could protect from oncoming vehicles
927 37861 : for (const auto& ili : bidi->getIncomingLanes()) {
928 19609 : if (ili.viaLink->getDirection() == LinkDirection::TURN) {
929 1420 : continue;
930 : }
931 39341 : for (const MSLink* const link : ili.lane->getLinkCont()) {
932 21152 : if (link->getDirection() == LinkDirection::TURN) {
933 929 : continue;
934 : }
935 20223 : if (link->getViaLaneOrLane() != bidi) {
936 2034 : myCoreSize = (int)myRoute.size();
937 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
938 : if (gDebugFlag4) {
939 : const MSEdge* const bidiNext = bidi->getNextNormal();
940 : std::cout << " found unsafe switch " << ili.viaLink->getDescription() << " (used=" << bidiNext->getID() << ")\n";
941 : }
942 : #endif
943 : // trains along our route beyond this switch might create deadlock
944 : foundUnsafeSwitch = true;
945 : // the switch itself must still be guarded to ensure safety
946 4182 : for (const auto& ili2 : bidi->getIncomingLanes()) {
947 2148 : if (ili2.viaLink->getDirection() != LinkDirection::TURN) {
948 2086 : flankSwitches.insert(ili.viaLink);
949 : }
950 : }
951 : }
952 : }
953 : }
954 : }
955 : }
956 : const std::vector<MSLink*>& links = toLane->getLinkCont();
957 36838 : toLane = nullptr;
958 40012 : for (const MSLink* const link : links) {
959 34711 : if ((next != end && &link->getLane()->getEdge() == *next)
960 68982 : && isRailway(link->getViaLaneOrLane()->getPermissions())) {
961 54198 : toLane = link->getViaLaneOrLane();
962 32858 : if (link->getTLLogic() != nullptr) {
963 6895 : if (link == origin) {
964 0 : WRITE_WARNINGF(TL("Found circular block after % (% edges, length %)"), warnID, toString(myRoute.size()), toString(length));
965 : //std::cout << getClickableTLLinkID(origin) << " circularBlock2=" << toString(myRoute) << "\n";
966 0 : myAbortedBuild = true;
967 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
968 : if (gDebugFlag4) {
969 : std::cout << " abort: found circle\n";
970 : }
971 : #endif
972 0 : return;
973 : }
974 : seekForwardSignal = false;
975 6895 : myFoundSignal = true;
976 6895 : seekBidiSwitch = bidi != nullptr;
977 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
978 : if (gDebugFlag4) {
979 : std::cout << " found forwardSignal " << link->getTLLogic()->getID() << " seekBidiSwitch=" << seekBidiSwitch << "\n";
980 : }
981 : #endif
982 : }
983 : //if (links.size() > 1 && !foundUnsafeSwitch) {
984 32858 : if (isSwitch(link)) {
985 : // switch on driveway
986 : //std::cout << "mySwitchDriveWays " << getID() << " link=" << link->getDescription() << "\n";
987 18839 : mySwitchDriveWays[link].push_back(this);
988 : }
989 32858 : if (link->getLane()->getBidiLane() != nullptr && &link->getLane()->getEdge() == current->getBidiEdge()) {
990 : // reversal on driveway
991 580 : myReversalDriveWays[current].push_back(this);
992 580 : myReversals.push_back(current);
993 : }
994 : break;
995 : }
996 : }
997 36838 : if (toLane == nullptr) {
998 3980 : if (next != end) {
999 : // no connection found, jump to next route edge
1000 : toLane = (*next)->getLanes()[0];
1001 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
1002 : if (gDebugFlag4) {
1003 : std::cout << " abort: turn-around or jump\n";
1004 : }
1005 : #endif
1006 128 : myFoundJump = true;
1007 128 : return;
1008 : } else {
1009 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
1010 : if (gDebugFlag4) {
1011 : std::cout << " abort: no next lane available\n";
1012 : }
1013 : #endif
1014 3852 : myTerminateRoute = true;
1015 3852 : return;
1016 : }
1017 : }
1018 : }
1019 1885 : myBidiEnded = !seekBidiSwitch;
1020 : #ifdef DEBUG_DRIVEWAY_BUILDROUTE
1021 : if (gDebugFlag4) {
1022 : std::cout << " normalEnd myBidiEnded=" << myBidiEnded << "\n";
1023 : }
1024 : #endif
1025 : }
1026 :
1027 :
1028 : bool
1029 39770 : MSDriveWay::isSwitch(const MSLink* link) {
1030 76743 : for (const MSLink* other : link->getLaneBefore()->getNormalPredecessorLane()->getLinkCont()) {
1031 44328 : if (other->getLane() != link->getLane() && !other->isTurnaround()) {
1032 : return true;
1033 : }
1034 : }
1035 53558 : for (auto ili : link->getLane()->getIncomingLanes()) {
1036 37926 : if (ili.viaLink != link && !ili.viaLink->isTurnaround()) {
1037 : return true;
1038 : }
1039 : }
1040 15632 : const MSLane* bidi = link->getLane()->getBidiLane();
1041 15632 : if (bidi != nullptr) {
1042 26631 : for (const MSLink* other : bidi->getLinkCont()) {
1043 14169 : if (other->getLane() != link->getLaneBefore()->getNormalPredecessorLane()->getBidiLane() && !other->isTurnaround()) {
1044 : return true;
1045 : }
1046 : }
1047 : }
1048 : return false;
1049 : }
1050 :
1051 :
1052 : void
1053 23684 : MSDriveWay::checkFlanks(const MSLink* originLink, const std::vector<const MSLane*>& lanes, const LaneVisitedMap& visited, bool allFoes, std::set<MSLink*>& flankSwitches) const {
1054 : #ifdef DEBUG_CHECK_FLANKS
1055 : std::cout << " checkFlanks lanes=" << toString(lanes) << " allFoes=" << allFoes << "\n";
1056 : #endif
1057 13364 : const MSLink* reverseOriginLink = originLink != nullptr && originLink->getLane()->getBidiLane() != nullptr && originLink->getLaneBefore()->getBidiLane() != nullptr
1058 32180 : ? originLink->getLane()->getBidiLane()->getLinkTo(originLink->getLaneBefore()->getBidiLane())
1059 : : nullptr;
1060 : //std::cout << " originLink=" << originLink->getDescription() << "\n";
1061 8496 : if (reverseOriginLink != nullptr) {
1062 8496 : reverseOriginLink = reverseOriginLink->getCorrespondingExitLink();
1063 : //std::cout << " reverseOriginLink=" << reverseOriginLink->getDescription() << "\n";
1064 : }
1065 73252 : for (int i = 0; i < (int)lanes.size(); i++) {
1066 49568 : const MSLane* lane = lanes[i];
1067 49568 : const MSLane* prev = i > 0 ? lanes[i - 1] : nullptr;
1068 49568 : const MSLane* next = i + 1 < (int)lanes.size() ? lanes[i + 1] : nullptr;
1069 49568 : if (lane->isInternal()) {
1070 15791 : continue;
1071 : }
1072 71111 : for (auto ili : lane->getIncomingLanes()) {
1073 44714 : if (ili.viaLink == originLink
1074 35589 : || ili.viaLink == reverseOriginLink
1075 33455 : || ili.viaLink->getDirection() == LinkDirection::TURN
1076 67288 : || ili.viaLink->getDirection() == LinkDirection::TURN_LEFTHAND) {
1077 7380 : continue;
1078 : }
1079 29954 : if (ili.lane != prev && ili.lane != next) {
1080 : #ifdef DEBUG_CHECK_FLANKS
1081 : std::cout << " add flankSwitch junction=" << ili.viaLink->getJunction()->getID() << " index=" << ili.viaLink->getIndex() << " iLane=" << ili.lane->getID() << " prev=" << Named::getIDSecure(prev) << " targetLane=" << lane->getID() << " next=" << Named::getIDSecure(next) << "\n";
1082 : #endif
1083 : flankSwitches.insert(ili.viaLink);
1084 22514 : } else if (allFoes) {
1085 : // link is part of the driveway, find foes that cross the driveway without entering
1086 7875 : checkCrossingFlanks(ili.viaLink, visited, flankSwitches);
1087 : }
1088 : }
1089 : }
1090 23684 : }
1091 :
1092 :
1093 : void
1094 7875 : MSDriveWay::checkCrossingFlanks(MSLink* dwLink, const LaneVisitedMap& visited, std::set<MSLink*>& flankSwitches) const {
1095 : #ifdef DEBUG_CHECK_FLANKS
1096 : std::cout << " checkCrossingFlanks dwLink=" << dwLink->getDescription() << " visited=" << formatVisitedMap(visited) << "\n";
1097 : #endif
1098 : const MSJunction* junction = dwLink->getJunction();
1099 7875 : if (junction == nullptr) {
1100 : return; // unregulated junction;
1101 : }
1102 7801 : const MSJunctionLogic* logic = junction->getLogic();
1103 7801 : if (logic == nullptr) {
1104 : return; // unregulated junction;
1105 : }
1106 41248 : for (const MSEdge* in : junction->getIncoming()) {
1107 33507 : if (in->isInternal()) {
1108 17082 : continue;
1109 : }
1110 32870 : for (MSLane* inLane : in->getLanes()) {
1111 16445 : const MSLane* inBidi = inLane->getBidiLane();
1112 24072 : if (isRailway(inLane->getPermissions()) && visited.count(inLane) == 0 && (inBidi == nullptr || visited.count(inBidi) == 0)) {
1113 6000 : for (MSLink* link : inLane->getLinkCont()) {
1114 3127 : if (link->getIndex() >= 0 && logic->getFoesFor(dwLink->getIndex()).test(link->getIndex())
1115 8128 : && visited.count(link->getLane()) == 0) {
1116 : #ifdef DEBUG_CHECK_FLANKS
1117 : std::cout << " add crossing flankSwitch junction=" << junction->getID() << " index=" << link->getIndex() << "\n";
1118 : #endif
1119 427 : if (link->getViaLane() == nullptr) {
1120 : flankSwitches.insert(link);
1121 : } else {
1122 : flankSwitches.insert(link->getViaLane()->getLinkCont().front());
1123 : }
1124 : }
1125 : }
1126 : }
1127 : }
1128 : }
1129 : }
1130 :
1131 : void
1132 9836 : MSDriveWay::findFlankProtection(MSLink* link, MSLink* origLink, std::vector<const MSLane*>& flank) {
1133 : #ifdef DEBUG_CHECK_FLANKS
1134 : std::cout << " findFlankProtection link=" << link->getDescription() << " origLink=" << origLink->getDescription() << "\n";
1135 : #endif
1136 9836 : if (link->getCorrespondingEntryLink()->getTLLogic() != nullptr) {
1137 2149 : MSLink* entry = const_cast<MSLink*>(link->getCorrespondingEntryLink());
1138 : // guarded by signal
1139 : #ifdef DEBUG_CHECK_FLANKS
1140 : std::cout << " flank guarded by " << entry->getTLLogic()->getID() << "\n";
1141 : #endif
1142 : // @note, technically it's enough to collect links from foe driveways
1143 : // but this also adds "unused" conflict links which may aid comprehension
1144 2149 : myConflictLinks.push_back(entry);
1145 2149 : addFoes(entry);
1146 : } else {
1147 : const MSLane* lane = link->getLaneBefore();
1148 : std::vector<MSLink*> predLinks;
1149 15323 : for (auto ili : lane->getIncomingLanes()) {
1150 7636 : if (!ili.viaLink->isTurnaround()) {
1151 7382 : predLinks.push_back(ili.viaLink);
1152 : }
1153 : }
1154 7687 : if (predLinks.size() > 1) {
1155 : // this is a switch
1156 : #ifdef DEBUG_ADD_FOES
1157 : std::cout << " predecessors of " << link->getDescription() << " isSwitch\n";
1158 : #endif
1159 705 : for (MSLink* pred : predLinks) {
1160 470 : addSwitchFoes(pred);
1161 : }
1162 7452 : } else if (predLinks.size() == 1) {
1163 6912 : if (isSwitch(link)) {
1164 5924 : addSwitchFoes(link);
1165 : } else {
1166 : // continue upstream via single predecessor
1167 988 : findFlankProtection(predLinks.front(), origLink, flank);
1168 : }
1169 : }
1170 7687 : }
1171 9836 : }
1172 :
1173 :
1174 : void
1175 6394 : MSDriveWay::addSwitchFoes(MSLink* link) {
1176 : auto it = mySwitchDriveWays.find(link);
1177 6394 : if (it != mySwitchDriveWays.end()) {
1178 : #ifdef DEBUG_ADD_FOES
1179 : std::cout << " driveway " << myID << " addSwitchFoes for link " << link->getDescription() << "\n";
1180 : #endif
1181 7309 : for (MSDriveWay* foe : it->second) {
1182 5042 : if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
1183 : #ifdef DEBUG_ADD_FOES
1184 : std::cout << " foe=" << foe->myID
1185 : << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this)
1186 : << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
1187 : #endif
1188 2683 : myFoes.push_back(foe);
1189 : } else {
1190 : #ifdef DEBUG_ADD_FOES
1191 : std::cout << " cand=" << foe->myID << "\n";
1192 : #endif
1193 : }
1194 : }
1195 : }
1196 6394 : }
1197 :
1198 :
1199 : MSDriveWay*
1200 5921 : MSDriveWay::buildDriveWay(const std::string& id, const MSLink* link, MSRouteIterator first, MSRouteIterator end) {
1201 : // collect lanes and links that are relevant for setting this signal for the current driveWay
1202 : // For each driveway we collect
1203 : // - conflictLanes (signal must be red if any conflict lane is occupied)
1204 : // - conflictLinks (signal must be red if any conflict link is approached by a vehicle
1205 : // - that cannot break in time (arrivalSpeedBraking > 0)
1206 : // - approached by a vehicle with higher switching priority (see #3941)
1207 : // These objects are construct in steps:
1208 : //
1209 : // forwardBlock
1210 : // - search forward recursive from outgoing lane until controlled railSignal link found
1211 : // -> add all found lanes to conflictLanes
1212 : //
1213 : // bidiBlock (if any forwardBlock edge has bidi edge)
1214 : // - search bidi backward recursive until first switch
1215 : // - from switch search backward recursive all other incoming until controlled rail signal link
1216 : // -> add final links to conflictLinks
1217 : //
1218 : // flanks
1219 : // - search backward recursive from flanking switches
1220 : // until controlled railSignal link or protecting switch is found
1221 : // -> add all found lanes to conflictLanes
1222 : // -> add final links to conflictLinks
1223 5921 : MSDriveWay* dw = new MSDriveWay(link, id);
1224 : LaneVisitedMap visited;
1225 : std::vector<const MSLane*> before;
1226 5921 : MSLane* fromBidi = nullptr;
1227 5921 : if (link != nullptr) {
1228 3341 : appendMapIndex(visited, link->getLaneBefore());
1229 3341 : fromBidi = link->getLaneBefore()->getBidiLane();
1230 : }
1231 : std::set<MSLink*> flankSwitches; // list of switches that threaten the driveway and for which protection must be found
1232 :
1233 5921 : if (fromBidi != nullptr) {
1234 2193 : before.push_back(fromBidi);
1235 : }
1236 5921 : dw->buildRoute(link, 0., first, end, visited, flankSwitches);
1237 5921 : dw->myCoreSize = (int)dw->myRoute.size();
1238 5921 : dw->checkFlanks(link, dw->myForward, visited, true, flankSwitches);
1239 5921 : dw->checkFlanks(link, dw->myBidi, visited, false, flankSwitches);
1240 5921 : dw->checkFlanks(link, before, visited, true, flankSwitches);
1241 13392 : for (MSLink* fsLink : flankSwitches) {
1242 : #ifdef DEBUG_ADD_FOES
1243 : if (DEBUG_COND_DW) {
1244 : std::cout << " fsLink=" << fsLink->getDescription() << "\n";
1245 : }
1246 : #endif
1247 7471 : dw->findFlankProtection(fsLink, fsLink, dw->myFlank);
1248 : }
1249 : std::set<MSLink*> flankSwitchesBidiExtended;
1250 5921 : dw->checkFlanks(link, dw->myBidiExtended, visited, false, flankSwitchesBidiExtended);
1251 7298 : for (MSLink* link : flankSwitchesBidiExtended) {
1252 : #ifdef DEBUG_ADD_FOES
1253 : if (DEBUG_COND_DW) {
1254 : std::cout << " fsLinkExtended=" << link->getDescription() << "\n";
1255 : }
1256 : #endif
1257 1377 : dw->findFlankProtection(link, link, dw->myBidiExtended);
1258 : }
1259 5921 : MSRailSignal* rs = link ? const_cast<MSRailSignal*>(static_cast<const MSRailSignal*>(link->getTLLogic())) : nullptr;
1260 9262 : const bool movingBlock = (rs && rs->isMovingBlock()) || (!rs && OptionsCont::getOptions().getBool("railsignal-moving-block"));
1261 : #ifdef DEBUG_BUILD_DRIVEWAY
1262 : if (DEBUG_COND_DW) {
1263 : std::cout << SIMTIME << " buildDriveWay " << dw->myID << " link=" << (link == nullptr ? "NULL" : link->getDescription())
1264 : << "\n route=" << toString(dw->myRoute)
1265 : << "\n forward=" << toString(dw->myForward)
1266 : << "\n bidi=" << toString(dw->myBidi)
1267 : << "\n bidiEx=" << toString(dw->myBidiExtended)
1268 : << "\n flank=" << toString(dw->myFlank)
1269 : << "\n flankSwitch=" << MSRailSignal::describeLinks(std::vector<MSLink*>(flankSwitches.begin(), flankSwitches.end()))
1270 : << "\n coreSize=" << dw->myCoreSize
1271 : << "\n";
1272 : }
1273 : #endif
1274 5921 : if (!rs || !rs->isMovingBlock()) {
1275 5896 : dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myForward.begin(), dw->myForward.end());
1276 : }
1277 5921 : dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myBidi.begin(), dw->myBidi.end());
1278 5921 : dw->myConflictLanes.insert(dw->myConflictLanes.end(), dw->myFlank.begin(), dw->myFlank.end());
1279 5921 : dw->addBidiFoes(rs, false);
1280 5921 : dw->addBidiFoes(rs, true);
1281 : // add driveways that start on the same signal / lane
1282 5921 : dw->addParallelFoes(link, *first);
1283 : // add driveways that reverse along this driveways route
1284 5921 : dw->addReversalFoes();
1285 : // make foes unique and symmetrical
1286 5921 : std::set<MSDriveWay*, ComparatorNumericalIdLess> uniqueFoes(dw->myFoes.begin(), dw->myFoes.end());
1287 5921 : std::set<MSLink*> uniqueCLink(dw->myConflictLinks.begin(), dw->myConflictLinks.end());
1288 : dw->myFoes.clear();
1289 5921 : const MSEdge* lastEdge = &dw->myForward.back()->getEdge();
1290 10021 : for (MSDriveWay* foe : uniqueFoes) {
1291 4100 : const MSEdge* foeLastEdge = &foe->myForward.back()->getEdge();
1292 4100 : const bool sameLast = foeLastEdge == lastEdge;
1293 4100 : if (sameLast && !movingBlock) {
1294 1056 : dw->myFoes.push_back(foe);
1295 1056 : if (foe != dw) {
1296 1056 : foe->myFoes.push_back(dw);
1297 : }
1298 : } else {
1299 3044 : if (foe->bidiBlockedByEnd(*dw)) {
1300 : #ifdef DEBUG_ADD_FOES
1301 : if (DEBUG_COND_DW) {
1302 : std::cout << " setting " << dw->getID() << " as foe of " << foe->getID() << "\n";
1303 : }
1304 : #endif
1305 1634 : foe->myFoes.push_back(dw);
1306 1634 : foe->addSidings(dw);
1307 : } else {
1308 1410 : dw->buildSubFoe(foe, movingBlock);
1309 : }
1310 3044 : if (dw->bidiBlockedByEnd(*foe)) {
1311 : #ifdef DEBUG_ADD_FOES
1312 : if (DEBUG_COND_DW) {
1313 : std::cout << " addFoeCheckSiding " << foe->getID() << "\n";
1314 : }
1315 : #endif
1316 1757 : dw->myFoes.push_back(foe);
1317 1757 : dw->addSidings(foe);
1318 : } else {
1319 1287 : foe->buildSubFoe(dw, movingBlock);
1320 : }
1321 : }
1322 4100 : if (link) {
1323 2892 : foe->addConflictLink(link);
1324 : }
1325 : // ignore links that have the same start junction
1326 4100 : if (foe->myRoute.front()->getFromJunction() != dw->myRoute.front()->getFromJunction()) {
1327 6250 : for (auto ili : foe->myForward.front()->getIncomingLanes()) {
1328 2852 : if (ili.viaLink->getTLLogic() != nullptr) {
1329 : // ignore links that originate on myBidi
1330 2532 : const MSLane* origin = ili.viaLink->getLaneBefore();
1331 2532 : if (std::find(dw->myBidi.begin(), dw->myBidi.end(), origin) == dw->myBidi.end()) {
1332 : uniqueCLink.insert(ili.viaLink);
1333 : }
1334 : }
1335 : }
1336 : }
1337 : }
1338 : dw->myConflictLinks.clear();
1339 5921 : dw->myConflictLinks.insert(dw->myConflictLinks.begin(), uniqueCLink.begin(), uniqueCLink.end());
1340 5921 : myEndingDriveways[lastEdge].push_back(dw);
1341 5921 : if (!movingBlock) {
1342 : // every driveway is it's own foe (also all driveways that depart in the same block)
1343 12871 : for (MSDriveWay* sameEnd : myEndingDriveways[lastEdge]) {
1344 : if (uniqueFoes.count(sameEnd) == 0) {
1345 5961 : dw->myFoes.push_back(sameEnd);
1346 5961 : if (sameEnd != dw) {
1347 107 : sameEnd->myFoes.push_back(dw);
1348 : }
1349 : }
1350 : }
1351 : }
1352 : #ifdef DEBUG_BUILD_DRIVEWAY
1353 : if (DEBUG_COND_DW) {
1354 : std::cout << dw->myID << " finalFoes " << toString(dw->myFoes) << "\n";
1355 : }
1356 : #endif
1357 5921 : return dw;
1358 5921 : }
1359 :
1360 : std::string
1361 2914 : MSDriveWay::getTLLinkID(const MSLink* link) {
1362 5828 : return link->getTLLogic()->getID() + "_" + toString(link->getTLIndex());
1363 : }
1364 :
1365 : std::string
1366 0 : MSDriveWay::getJunctionLinkID(const MSLink* link) {
1367 0 : return link->getJunction()->getID() + "_" + toString(link->getIndex());
1368 : }
1369 :
1370 : std::string
1371 3363 : MSDriveWay::getClickableTLLinkID(const MSLink* link) {
1372 10089 : return "junction '" + link->getTLLogic()->getID() + "', link " + toString(link->getTLIndex());
1373 : }
1374 :
1375 : std::string
1376 0 : MSDriveWay::formatVisitedMap(const LaneVisitedMap& visited) {
1377 : UNUSED_PARAMETER(visited);
1378 : /*
1379 : std::vector<const MSLane*> lanes(visited.size(), nullptr);
1380 : for (auto item : visited) {
1381 : lanes[item.second] = item.first;
1382 : }
1383 : for (auto it = lanes.begin(); it != lanes.end();) {
1384 : if (*it == nullptr) {
1385 : it = lanes.erase(it);
1386 : } else {
1387 : it++;
1388 : }
1389 : }
1390 : return toString(lanes);
1391 : */
1392 0 : return "dummy";
1393 : }
1394 :
1395 :
1396 : void
1397 40179 : MSDriveWay::appendMapIndex(LaneVisitedMap& map, const MSLane* lane) {
1398 : // avoid undefined behavior from evaluation order
1399 40179 : const int tmp = (int)map.size();
1400 40179 : map[lane] = tmp;
1401 40179 : }
1402 :
1403 : bool
1404 552863 : MSDriveWay::match(MSRouteIterator firstIt, MSRouteIterator endIt) const {
1405 : // @todo optimize: it is sufficient to check for specific edges (after each switch)
1406 552863 : auto itRoute = firstIt;
1407 : auto itDwRoute = myRoute.begin();
1408 : bool match = true;
1409 3426854 : while (itRoute != endIt && itDwRoute != myRoute.end()) {
1410 2893177 : if (*itRoute != *itDwRoute) {
1411 : match = false;
1412 : #ifdef DEBUG_MATCH
1413 : std::cout << " check dw=" << getID() << " match failed at vehEdge=" << (*itRoute)->getID() << " dwEdge=" << (*itDwRoute)->getID() << "\n";
1414 : #endif
1415 : break;
1416 : }
1417 : itRoute++;
1418 : itDwRoute++;
1419 : }
1420 : // if the vehicle arrives before the end of this driveway,
1421 : // we'd rather build a new driveway to avoid superfluous restrictions
1422 533677 : if (match && itDwRoute == myRoute.end()
1423 1078447 : && (itRoute == endIt || myAbortedBuild || myBidiEnded || myFoundJump || myIsSubDriveway)) {
1424 : //std::cout << " using dw=" << "\n";
1425 522569 : if (itRoute != endIt) {
1426 : // check whether the current route requires an extended driveway
1427 296272 : const MSEdge* next = *itRoute;
1428 296272 : const MSEdge* prev = myRoute.back();
1429 561 : if (myFoundJump && prev->getBidiEdge() != next && prev->getBidiEdge() != nullptr
1430 296477 : && prev->isConnectedTo(*next, (SUMOVehicleClass)(SVC_RAIL_CLASSES & prev->getPermissions()))) {
1431 : #ifdef DEBUG_MATCH
1432 : std::cout << " check dw=" << getID() << " prev=" << prev->getID() << " next=" << next->getID() << "\n";
1433 : #endif
1434 : return false;
1435 : }
1436 296263 : if (!myFoundJump && prev->getBidiEdge() == next && prev == &myForward.back()->getEdge()) {
1437 : assert(myIsSubDriveway);
1438 : // must not leave driveway via reversal
1439 : #ifdef DEBUG_MATCH
1440 : std::cout << getID() << " back=" << myForward.back()->getID() << " noMatch route " << toString(ConstMSEdgeVector(firstIt, endIt)) << "\n";
1441 : #endif
1442 : return false;
1443 : }
1444 : }
1445 522545 : return true;
1446 : }
1447 : return false;
1448 : }
1449 :
1450 : void
1451 8806 : MSDriveWay::addFoes(const MSLink* link) {
1452 : #ifdef DEBUG_ADD_FOES
1453 : std::cout << "driveway " << myID << " addFoes for link " << link->getDescription() << "\n";
1454 : #endif
1455 8806 : const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(link->getTLLogic());
1456 8806 : if (rs != nullptr) {
1457 11067 : for (MSDriveWay* foe : rs->retrieveDriveWays(link->getTLIndex())) {
1458 : #ifdef DEBUG_ADD_FOES
1459 : std::cout << " cand foe=" << foe->myID << " fc1=" << flankConflict(*foe) << " fc2=" << foe->flankConflict(*this) << " cc1=" << crossingConflict(*foe) << " cc2=" << foe->crossingConflict(*this) << "\n";
1460 : #endif
1461 2264 : if (foe != this && (flankConflict(*foe) || foe->flankConflict(*this) || crossingConflict(*foe) || foe->crossingConflict(*this))) {
1462 : #ifdef DEBUG_ADD_FOES
1463 : std::cout << " foe=" << foe->myID << "\n";
1464 : #endif
1465 1923 : myFoes.push_back(foe);
1466 : }
1467 8803 : }
1468 : }
1469 8806 : }
1470 :
1471 :
1472 : void
1473 11842 : MSDriveWay::addBidiFoes(const MSRailSignal* ownSignal, bool extended) {
1474 : #ifdef DEBUG_ADD_FOES
1475 : std::cout << "driveway " << myID << " addBidiFoes extended=" << extended << "\n";
1476 : #endif
1477 11842 : const std::vector<const MSLane*>& bidiLanes = extended ? myBidiExtended : myBidi;
1478 41377 : for (const MSLane* bidi : bidiLanes) {
1479 61502 : for (auto ili : bidi->getIncomingLanes()) {
1480 31967 : const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(ili.viaLink->getTLLogic());
1481 31967 : if (rs != nullptr && rs != ownSignal &&
1482 31967 : std::find(bidiLanes.begin(), bidiLanes.end(), ili.lane) != bidiLanes.end()) {
1483 3316 : addFoes(ili.viaLink);
1484 : }
1485 : }
1486 29535 : const MSEdge* bidiEdge = &bidi->getEdge();
1487 : if (myDepartureDriveways.count(bidiEdge) != 0) {
1488 1936 : for (MSDriveWay* foe : myDepartureDriveways[bidiEdge]) {
1489 965 : if (flankConflict(*foe)) {
1490 : #ifdef DEBUG_ADD_FOES
1491 : std::cout << " foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << "\n";
1492 : #endif
1493 726 : myFoes.push_back(foe);
1494 : } else {
1495 : #ifdef DEBUG_ADD_FOES
1496 : std::cout << " cand foe " << foe->getID() << " departs on bidi=" << bidiEdge->getID() << " rejected\n";
1497 : #endif
1498 : }
1499 : }
1500 : }
1501 : if (myDepartureDrivewaysEnds.count(bidiEdge) != 0) {
1502 1742 : for (MSDriveWay* foe : myDepartureDrivewaysEnds[bidiEdge]) {
1503 912 : if (flankConflict(*foe)) {
1504 : #ifdef DEBUG_ADD_FOES
1505 : std::cout << " foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << "\n";
1506 : #endif
1507 705 : myFoes.push_back(foe);
1508 : } else {
1509 : #ifdef DEBUG_ADD_FOES
1510 : std::cout << " cand foe " << foe->getID() << " ends on bidi=" << bidiEdge->getID() << " rejected\n";
1511 : #endif
1512 : }
1513 : }
1514 : }
1515 : }
1516 11842 : }
1517 :
1518 :
1519 : void
1520 5921 : MSDriveWay::addParallelFoes(const MSLink* link, const MSEdge* first) {
1521 : #ifdef DEBUG_ADD_FOES
1522 : std::cout << "driveway " << myID << " addParallelFoes\n";
1523 : #endif
1524 5921 : if (link) {
1525 3341 : addFoes(link);
1526 : } else {
1527 : auto it = myDepartureDriveways.find(first);
1528 2580 : if (it != myDepartureDriveways.end()) {
1529 2689 : for (MSDriveWay* foe : it->second) {
1530 : #ifdef DEBUG_ADD_FOES
1531 : std::cout << " foe " << foe->getID() << " departs on first=" << first->getID() << "\n";
1532 : #endif
1533 109 : myFoes.push_back(foe);
1534 : }
1535 : }
1536 : }
1537 5921 : }
1538 :
1539 :
1540 : void
1541 5921 : MSDriveWay::addReversalFoes() {
1542 : #ifdef DEBUG_ADD_FOES
1543 : std::cout << "driveway " << myID << " addReversalFoes\n";
1544 : #endif
1545 : std::set<const MSEdge*> forward;
1546 23761 : for (const MSLane* lane : myForward) {
1547 17840 : if (lane->isNormal()) {
1548 12423 : forward.insert(&lane->getEdge());
1549 : }
1550 : }
1551 : int i = 0;
1552 30030 : for (const MSEdge* e : myRoute) {
1553 12433 : if (forward.count(e) != 0) {
1554 : // reversals in our own forward section must be ignored
1555 : continue;
1556 : }
1557 11676 : if (i == myCoreSize) {
1558 : break;
1559 : }
1560 11676 : i++;
1561 : auto it = myReversalDriveWays.find(e);
1562 11676 : if (it != myReversalDriveWays.end()) {
1563 1285 : for (MSDriveWay* foe : it->second) {
1564 : // check whether the foe reverses into our own forward section
1565 : // (it might reverse again or disappear via arrival)
1566 : #ifdef DEBUG_ADD_FOES
1567 : //std::cout << " candidate foe " << foe->getID() << " reverses on edge=" << e->getID() << " forward=" << joinNamedToString(forward, " ") << " foeRoute=" << toString(foe->myRoute) << "\n";
1568 : #endif
1569 1720 : if (forwardRouteConflict(forward, *foe)) {
1570 : std::set<const MSEdge*> foeForward;
1571 955 : for (const MSLane* lane : foe->myForward) {
1572 812 : if (lane->isNormal()) {
1573 517 : foeForward.insert(&lane->getEdge());
1574 517 : if (lane->getBidiLane() != nullptr) {
1575 517 : foeForward.insert(lane->getEdge().getBidiEdge());
1576 : }
1577 : }
1578 : }
1579 : #ifdef DEBUG_ADD_FOES
1580 : std::cout << " reversal cand=" << foe->getID() << " foeForward " << toString(foeForward) << "\n";
1581 : #endif
1582 286 : if (foe->forwardRouteConflict(foeForward, *this, true)) {
1583 : #ifdef DEBUG_ADD_FOES
1584 : std::cout << " foe " << foe->getID() << " reverses on edge=" << e->getID() << "\n";
1585 : #endif
1586 114 : myFoes.push_back(foe);
1587 : }
1588 : }
1589 : }
1590 : }
1591 : }
1592 5921 : }
1593 :
1594 :
1595 : bool
1596 2697 : MSDriveWay::buildSubFoe(MSDriveWay* foe, bool movingBlock) {
1597 : // Subdriveways (Teilfahrstraße) model the resolution of a driving conflict
1598 : // before a vehicle has left the driveway. This is possible when the driveway diverges from the foe
1599 : // driveway at an earlier point (switch or crossing).
1600 : //
1601 : // We already know that the last edge of this driveway doesn't impact the foe (unless the driveway ends within the block).
1602 : // Remove further edges from the end of the driveway (myForward) until the point of conflict is found.
1603 : //
1604 : // For movingBlock the logic is changed:
1605 : // We remove the conflict-free part as before but then keep removing the the conflict part until the
1606 : // another non-conconflit part is found
1607 2697 : if (myForward.size() < foe->myForward.size() &&
1608 2697 : myForward == std::vector<const MSLane*>(foe->myForward.begin(), foe->myForward.begin() + myForward.size())) {
1609 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1610 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " is subpart of foe=" << foe->getID() << "\n";
1611 : #endif
1612 16 : foe->myFoes.push_back(this);
1613 16 : return true;
1614 : }
1615 2681 : int subLast = (int)myForward.size() - 2;
1616 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1617 : if (subLast < 0) {
1618 : std::cout << " " << getID() << " cannot build subDriveWay for foe " << foe->getID() << " because myForward has only a single lane\n";
1619 : }
1620 : #endif
1621 : bool foundConflict = false;
1622 8467 : while (subLast >= 0) {
1623 7959 : const MSLane* lane = myForward[subLast];
1624 7959 : MSDriveWay tmp(myOrigin, "tmp", true);
1625 7959 : tmp.myForward.push_back(lane);
1626 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1627 : std::cout << " subLast=" << subLast << " lane=" << lane->getID() << " fc=" << tmp.flankConflict(*foe) << " cc=" << tmp.crossingConflict(*foe)
1628 : << " bc=" << (std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end()) << "\n";
1629 : #endif
1630 7959 : const bool bidiConflict = std::find(foe->myBidi.begin(), foe->myBidi.end(), lane) != foe->myBidi.end();
1631 7959 : if (tmp.flankConflict(*foe) || tmp.crossingConflict(*foe) || bidiConflict) {
1632 : foundConflict = true;
1633 2187 : if (!movingBlock || bidiConflict) {
1634 : break;
1635 : }
1636 5772 : } else if (foundConflict) {
1637 : break;
1638 : }
1639 5786 : subLast--;
1640 7959 : }
1641 2681 : if (subLast < 0) {
1642 508 : if (foe->myTerminateRoute) {
1643 468 : if (bidiBlockedByEnd(*foe) && bidiBlockedBy(*this) && foe->forwardEndOnRoute(this)) {
1644 62 : foe->myFoes.push_back(this);
1645 : // foe will get the sidings
1646 62 : addSidings(foe, true);
1647 : }
1648 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1649 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " terminates\n";
1650 : #endif
1651 40 : } else if (myTerminateRoute && myBidi.size() <= myForward.size()) {
1652 5 : foe->myFoes.push_back(this);
1653 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1654 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " terminates, foe=" << foe->getID() << "\n";
1655 : #endif
1656 5 : return true;
1657 : } else if (foe->myReversals.size() % 2 == 1) {
1658 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1659 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " has " << foe->myReversals.size() << " reversals\n";
1660 : #endif
1661 : } else {
1662 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1663 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " failed\n";
1664 : #endif
1665 : #ifdef SUBDRIVEWAY_WARN_NOCONFLICT
1666 : WRITE_WARNINGF("No point of conflict found between driveway '%' and driveway '%' when creating sub-driveway", getID(), foe->getID());
1667 : #endif
1668 : }
1669 503 : return false;
1670 : }
1671 2173 : int subSize = subLast + 1;
1672 2773 : for (MSDriveWay* cand : mySubDriveWays) {
1673 1092 : if ((int)cand->myForward.size() == subSize) {
1674 : // can re-use existing sub-driveway
1675 492 : foe->myFoes.push_back(cand);
1676 492 : cand->myFoes.push_back(foe);
1677 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1678 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " useExisting=" << cand->getID() << "\n";
1679 : #endif
1680 492 : return true;
1681 : }
1682 : }
1683 1681 : std::vector<const MSLane*> forward(myForward.begin(), myForward.begin() + subSize);
1684 : std::vector<const MSEdge*> route;
1685 7866 : for (const MSLane* lane : forward) {
1686 6185 : if (lane->isNormal()) {
1687 3646 : route.push_back(&lane->getEdge());
1688 : }
1689 : }
1690 1681 : if (myRoute.size() > route.size()) {
1691 : // route continues. make sure the subDriveway does not end with a reversal
1692 1681 : const MSEdge* lastNormal = route.back();
1693 1681 : const MSEdge* nextNormal = myRoute[route.size()];
1694 1681 : if (lastNormal->getBidiEdge() == nextNormal) {
1695 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1696 : std::cout << SIMTIME << " abort subFoe dw=" << getID() << " foe=" << foe->getID()
1697 : << " lastNormal=" << lastNormal->getID() << " nextNormal=" << nextNormal->getID() << " endWithReversal\n";
1698 : #endif
1699 : return false;
1700 : }
1701 : }
1702 3338 : MSDriveWay* sub = new MSDriveWay(myOrigin, getID() + "." + toString(mySubDriveWays.size()));
1703 1669 : sub->myLane = myLane;
1704 1669 : sub->myIsSubDriveway = true;
1705 1669 : sub->myForward = forward;
1706 1669 : sub->myRoute = route;
1707 1669 : sub->myCoreSize = (int)sub->myRoute.size();
1708 1669 : myLane->addMoveReminder(sub);
1709 :
1710 : // copy trains that are currently on this driveway (and associated entry events)
1711 1728 : for (SUMOVehicle* veh : myTrains) {
1712 59 : if (std::find(sub->myRoute.begin(), sub->myRoute.end(), veh->getEdge()) != sub->myRoute.end()) {
1713 : sub->myTrains.insert(veh);
1714 46 : dynamic_cast<MSBaseVehicle*>(veh)->addReminder(sub);
1715 46 : for (const VehicleEvent& ve : myVehicleEvents) {
1716 0 : if (ve.id == veh->getID()) {
1717 0 : sub->myVehicleEvents.push_back(ve);
1718 : }
1719 : }
1720 : }
1721 : }
1722 :
1723 1669 : foe->myFoes.push_back(sub);
1724 1669 : sub->myFoes.push_back(foe);
1725 1669 : mySubDriveWays.push_back(sub);
1726 : #ifdef DEBUG_BUILD_SUBDRIVEWAY
1727 : std::cout << SIMTIME << " buildSubFoe dw=" << getID() << " foe=" << foe->getID() << " sub=" << sub->getID() << " route=" << toString(sub->myRoute) << "\n";
1728 : #endif
1729 : return true;
1730 1681 : }
1731 :
1732 : void
1733 3453 : MSDriveWay::addSidings(MSDriveWay* foe, bool addToFoe) {
1734 3453 : const MSEdge* foeEndBidi = foe->myForward.back()->getEdge().getBidiEdge();
1735 : int forwardNormals = 0;
1736 15800 : for (auto lane : foe->myForward) {
1737 12347 : if (lane->isNormal()) {
1738 8255 : forwardNormals++;
1739 : }
1740 : }
1741 3453 : if (forwardNormals == (int)foe->myRoute.size()) {
1742 : #ifdef DEBUG_BUILD_SIDINGS
1743 : std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() << " aborted\n";
1744 : #endif
1745 724 : return;
1746 : }
1747 : auto foeSearchBeg = foe->myRoute.begin() + forwardNormals;
1748 : auto foeSearchEnd = foe->myRoute.end();
1749 2729 : if (foeEndBidi == nullptr) {
1750 0 : throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " noBidi\n");
1751 : }
1752 : int i;
1753 : std::vector<int> start;
1754 : std::vector<double> length;
1755 16128 : for (i = 0; i < (int)myRoute.size(); i++) {
1756 16128 : if (myRoute[i] == foeEndBidi) {
1757 : break;
1758 : }
1759 : }
1760 2729 : if (i == (int)myRoute.size()) {
1761 0 : throw ProcessError("checkSiding " + getID() + " foe=" + foe->getID() + " foeEndBidi=" + foeEndBidi->getID() + " not on route\n");
1762 : }
1763 2729 : const MSEdge* next = myRoute[i];
1764 : #ifdef DEBUG_BUILD_SIDINGS
1765 : std::cout << "checkSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " next=" << next->getID() << " forwardNormals=" << forwardNormals << " frSize=" << foe->myRoute.size() << " foeSearchBeg=" << (*foeSearchBeg)->getID() << "\n";
1766 : #endif
1767 2729 : i--;
1768 16128 : for (; i >= 0; i--) {
1769 13399 : const MSEdge* cur = myRoute[i];
1770 13399 : if (hasRS(cur, next)) {
1771 2680 : if (std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge()) == foeSearchEnd) {
1772 642 : start.push_back(i);
1773 642 : length.push_back(0);
1774 : }
1775 : }
1776 13399 : if (!start.empty()) {
1777 1806 : auto itFind = std::find(foeSearchBeg, foeSearchEnd, cur->getBidiEdge());
1778 1806 : if (itFind != foeSearchEnd) {
1779 : #ifdef DEBUG_BUILD_SIDINGS
1780 : std::cout << "endSiding " << getID() << " foe=" << foe->getID() << " i=" << i << " curBidi=" << Named::getIDSecure(cur->getBidiEdge()) << " length=" << toString(length) << "\n";
1781 : #endif
1782 401 : const int firstIndex = i + 1;
1783 401 : if (addToFoe) {
1784 34 : auto& foeSidings = foe->mySidings[this];
1785 : // indices must be mapped onto foe route;
1786 34 : const MSEdge* first = myRoute[firstIndex];
1787 34 : auto itFirst = std::find(foe->myRoute.begin(), foe->myRoute.end(), first);
1788 34 : if (itFirst != foe->myRoute.end()) {
1789 87 : for (int j = 0; j < (int)length.size(); j++) {
1790 53 : const MSEdge* last = myRoute[start[j]];
1791 53 : auto itLast = std::find(itFirst, foe->myRoute.end(), last);
1792 53 : if (itLast != foe->myRoute.end()) {
1793 53 : foeSidings.insert(foeSidings.begin(), Siding((int)(itFirst - foe->myRoute.begin()), (int)(itLast - foe->myRoute.begin()), length[j]));
1794 : }
1795 : }
1796 : }
1797 : } else {
1798 367 : auto& foeSidings = mySidings[foe];
1799 846 : for (int j = 0; j < (int)length.size(); j++) {
1800 479 : foeSidings.insert(foeSidings.begin(), Siding(firstIndex, start[j], length[j]));
1801 : }
1802 : }
1803 : start.clear();
1804 : length.clear();
1805 401 : foeSearchBeg = itFind;
1806 : } else {
1807 3154 : for (int j = 0; j < (int)length.size(); j++) {
1808 1749 : length[j] += cur->getLength();
1809 : }
1810 : }
1811 : }
1812 : next = cur;
1813 : }
1814 2729 : }
1815 :
1816 :
1817 : bool
1818 13399 : MSDriveWay::hasRS(const MSEdge* cur, const MSEdge* next) {
1819 13399 : if (cur->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
1820 : // check if there is a controlled link between cur and next
1821 10572 : for (auto lane : cur->getLanes()) {
1822 10951 : for (const MSLink* link : lane->getLinkCont()) {
1823 7005 : if (&link->getLane()->getEdge() == next && link->getTLLogic() != nullptr) {
1824 : return true;
1825 : }
1826 : }
1827 : }
1828 : }
1829 : return false;
1830 : }
1831 :
1832 :
1833 : bool
1834 72 : MSDriveWay::forwardEndOnRoute(const MSDriveWay* foe) const {
1835 72 : const MSEdge* foeForwardEnd = &foe->myForward.back()->getNormalPredecessorLane()->getEdge();
1836 72 : return std::find(myRoute.begin(), myRoute.end(), foeForwardEnd) != myRoute.end();
1837 : }
1838 :
1839 : void
1840 2892 : MSDriveWay::addConflictLink(const MSLink* link) {
1841 2892 : if (link->getTLLogic() != nullptr) {
1842 : // ignore links that originate on myBidi
1843 : // and also links from the same junction as my own link
1844 2892 : const MSLane* origin = link->getLaneBefore();
1845 2892 : if (std::find(myBidi.begin(), myBidi.end(), origin) == myBidi.end()) {
1846 2042 : if (link->getJunction() != myRoute.front()->getFromJunction()) {
1847 1639 : if (std::find(myConflictLinks.begin(), myConflictLinks.end(), link) == myConflictLinks.end()) {
1848 1196 : myConflictLinks.push_back(const_cast<MSLink*>(link));
1849 : }
1850 : }
1851 : }
1852 : }
1853 2892 : }
1854 :
1855 : void
1856 260 : MSDriveWay::addDWDeadlock(const std::vector<const MSDriveWay*>& deadlockFoes) {
1857 : std::set<const MSDriveWay*> filtered;
1858 1355 : for (const MSDriveWay* foe : deadlockFoes) {
1859 1095 : if (std::find(myFoes.begin(), myFoes.end(), foe) == myFoes.end()) {
1860 : filtered.insert(foe);
1861 : }
1862 : }
1863 260 : if (std::find(myDeadlocks.begin(), myDeadlocks.end(), filtered) == myDeadlocks.end()) {
1864 81 : myDeadlocks.push_back(filtered);
1865 : //std::cout << getID() << " deadlockFoes=" << toString(deadlockFoes) << "\n";
1866 : }
1867 260 : }
1868 :
1869 : const MSDriveWay*
1870 46316 : MSDriveWay::getDepartureDriveway(const SUMOVehicle* veh) {
1871 46316 : const MSEdge* edge = veh->getEdge();
1872 46316 : if (edge->getFromJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
1873 3636 : for (const MSLane* lane : edge->getLanes()) {
1874 3978 : for (auto ili : lane->getIncomingLanes()) {
1875 2434 : const MSLink* entry = ili.viaLink->getCorrespondingEntryLink();
1876 2434 : const MSRailSignal* rs = dynamic_cast<const MSRailSignal*>(entry->getTLLogic());
1877 1732 : if (rs != nullptr) {
1878 1732 : const MSDriveWay* dw = &const_cast<MSRailSignal*>(rs)->retrieveDriveWayForVeh(entry->getTLIndex(), veh);
1879 1732 : if (&dw->myForward.front()->getEdge() == edge) {
1880 : return dw;
1881 : }
1882 : }
1883 : }
1884 : }
1885 : }
1886 47374 : for (MSDriveWay* dw : myDepartureDriveways[edge]) {
1887 44794 : if (dw->match(veh->getCurrentRouteEdge(), veh->getRoute().end())) {
1888 : return dw;
1889 : }
1890 : }
1891 5160 : const std::string id = edge->getFromJunction()->getID() + ".d" + toString(myDepartDrivewayIndex[edge->getFromJunction()]++);
1892 2580 : MSDriveWay* dw = buildDriveWay(id, nullptr, veh->getCurrentRouteEdge(), veh->getRoute().end());
1893 2580 : myDepartureDriveways[edge].push_back(dw);
1894 2580 : myDepartureDrivewaysEnds[&dw->myForward.back()->getEdge()].push_back(dw);
1895 : dw->setVehicle(veh->getID());
1896 : return dw;
1897 : }
1898 :
1899 :
1900 : void
1901 1058 : MSDriveWay::writeDepatureBlocks(OutputDevice& od, bool writeVehicles) {
1902 2714 : for (auto item : myDepartureDriveways) {
1903 1656 : const MSEdge* edge = item.first;
1904 1656 : if (item.second.size() > 0) {
1905 3312 : od.openTag("departJunction");
1906 : od.writeAttr(SUMO_ATTR_ID, edge->getFromJunction()->getID());
1907 3413 : for (const MSDriveWay* dw : item.second) {
1908 1757 : if (writeVehicles) {
1909 137 : dw->writeBlockVehicles(od);
1910 : } else {
1911 1620 : dw->writeBlocks(od);
1912 : }
1913 : }
1914 3312 : od.closeTag(); // departJunction
1915 : }
1916 : }
1917 1058 : }
1918 :
1919 : void
1920 424 : MSDriveWay::saveState(OutputDevice& out) {
1921 : // all driveways are in myEndingDriveways which makes it convenient
1922 462 : for (auto item : myEndingDriveways) {
1923 80 : for (MSDriveWay* dw : item.second) {
1924 42 : dw->_saveState(out);
1925 51 : for (MSDriveWay* sub : dw->mySubDriveWays) {
1926 9 : sub->_saveState(out);
1927 : }
1928 : }
1929 : }
1930 424 : }
1931 :
1932 : void
1933 51 : MSDriveWay::_saveState(OutputDevice& out) const {
1934 51 : if (!myTrains.empty() || haveSubTrains()) {
1935 37 : out.openTag(myIsSubDriveway ? SUMO_TAG_SUBDRIVEWAY : SUMO_TAG_DRIVEWAY);
1936 : out.writeAttr(SUMO_ATTR_ID, getID());
1937 40 : out.writeAttr(SUMO_ATTR_EDGES, toString(myRoute));
1938 20 : if (!myTrains.empty()) {
1939 40 : out.writeAttr(SUMO_ATTR_VEHICLES, toString(myTrains));
1940 : }
1941 40 : out.closeTag();
1942 : }
1943 51 : }
1944 :
1945 :
1946 : bool
1947 31 : MSDriveWay::haveSubTrains() const {
1948 36 : for (MSDriveWay* sub : mySubDriveWays) {
1949 5 : if (!sub->myTrains.empty()) {
1950 : return true;
1951 : }
1952 : }
1953 : return false;
1954 : }
1955 :
1956 : void
1957 29 : MSDriveWay::loadState(const SUMOSAXAttributes& attrs, int tag) {
1958 29 : if ((int)myDriveWayRouteLookup.size() < myGlobalDriveWayIndex) {
1959 69 : for (auto item : myEndingDriveways) {
1960 110 : for (MSDriveWay* dw : item.second) {
1961 56 : myDriveWayRouteLookup[dw->myRoute] = dw;
1962 : }
1963 : }
1964 : }
1965 29 : MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
1966 : bool ok;
1967 29 : const std::string id = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
1968 29 : const std::string edges = attrs.get<std::string>(SUMO_ATTR_EDGES, id.c_str(), ok);
1969 : ConstMSEdgeVector route;
1970 29 : if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
1971 29 : MSEdge::parseEdgesList(edges, route, id);
1972 : }
1973 : MSDriveWay* dw = nullptr;
1974 29 : if (tag == SUMO_TAG_DRIVEWAY) {
1975 : auto it = myDriveWayRouteLookup.find(route);
1976 26 : if (it == myDriveWayRouteLookup.end()) {
1977 : //WRITE_WARNING(TLF("Unknown driveWay '%' with route '%'", id, edges));
1978 : //return;
1979 0 : throw ProcessError(TLF("Unknown driveWay '%' with route '%'", id, edges));
1980 : }
1981 26 : dw = it->second;
1982 26 : myDriveWayLookup[id] = dw;
1983 : } else {
1984 3 : std::string parentID = id.substr(0, id.rfind('.'));
1985 : auto it = myDriveWayLookup.find(parentID);
1986 3 : if (it == myDriveWayLookup.end()) {
1987 : //WRITE_WARNING(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
1988 : //return;
1989 0 : throw ProcessError(TLF("Unknown parent driveway '%' for subDriveWay '%'", parentID, id));
1990 : }
1991 3 : MSDriveWay* parent = it->second;
1992 3 : for (MSDriveWay* sub : parent->mySubDriveWays) {
1993 1 : if (sub->myRoute == route) {
1994 : dw = sub;
1995 : break;
1996 : }
1997 : }
1998 3 : if (dw == nullptr) {
1999 : // missing subdriveways can be ignored. They may have been created
2000 : // as foes for driveways that are not relevant at state loading time
2001 : return;
2002 : }
2003 : }
2004 54 : const std::string vehicles = attrs.getOpt<std::string>(SUMO_ATTR_VEHICLES, id.c_str(), ok, "");
2005 81 : for (const std::string& vehID : StringTokenizer(vehicles).getVector()) {
2006 27 : MSBaseVehicle* veh = dynamic_cast<MSBaseVehicle*>(c.getVehicle(vehID));
2007 27 : if (veh == nullptr) {
2008 0 : throw ProcessError(TLF("Unknown vehicle '%' in driveway '%'", vehID, id));
2009 : }
2010 27 : dw->myTrains.insert(veh);
2011 27 : veh->addReminder(dw);
2012 27 : }
2013 29 : }
2014 :
2015 :
2016 : /****************************************************************************/
|