Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-2026 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 MSTriggeredRerouter.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Mirco Sturari
19 : /// @author Mirko Barthauer
20 : /// @date Mon, 25 July 2005
21 : ///
22 : // Reroutes vehicles passing an edge
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include <string>
27 : #include <algorithm>
28 : #ifdef HAVE_FOX
29 : #include <utils/common/ScopedLocker.h>
30 : #endif
31 : #include <utils/options/OptionsCont.h>
32 : #include <utils/common/MsgHandler.h>
33 : #include <utils/common/Command.h>
34 : #include <utils/xml/SUMOXMLDefinitions.h>
35 : #include <utils/common/UtilExceptions.h>
36 : #include <utils/common/ToString.h>
37 : #include <utils/common/StringUtils.h>
38 : #include <utils/xml/SUMOSAXHandler.h>
39 : #include <utils/router/DijkstraRouter.h>
40 : #include <utils/common/RandHelper.h>
41 : #include <utils/common/WrappingCommand.h>
42 : #include <microsim/MSEdgeWeightsStorage.h>
43 : #include <microsim/MSLane.h>
44 : #include <microsim/MSLink.h>
45 : #include <microsim/MSVehicle.h>
46 : #include <microsim/MSBaseVehicle.h>
47 : #include <microsim/MSRoute.h>
48 : #include <microsim/MSEdge.h>
49 : #include <microsim/MSEventControl.h>
50 : #include <microsim/MSNet.h>
51 : #include <microsim/MSVehicleControl.h>
52 : #include <microsim/MSGlobals.h>
53 : #include <microsim/MSParkingArea.h>
54 : #include <microsim/MSStop.h>
55 : #include <microsim/traffic_lights/MSRailSignal.h>
56 : #include <microsim/traffic_lights/MSRailSignalConstraint.h>
57 : #include <microsim/transportables/MSPerson.h>
58 : #include <microsim/devices/MSDevice_Routing.h>
59 : #include <microsim/devices/MSRoutingEngine.h>
60 : #include "MSTriggeredRerouter.h"
61 :
62 : #include <mesosim/MELoop.h>
63 : #include <mesosim/MESegment.h>
64 :
65 : //#define DEBUG_REROUTER
66 : //#define DEBUG_OVERTAKING
67 : #define DEBUGCOND(veh) (veh.isSelected())
68 : //#define DEBUGCOND(veh) (true)
69 : //#define DEBUGCOND(veh) (veh.getID() == "")
70 :
71 : /// assume that a faster train has more priority and a slower train doesn't matter
72 : #define DEFAULT_PRIO_OVERTAKER 1
73 : #define DEFAULT_PRIO_OVERTAKEN 0.001
74 :
75 : // ===========================================================================
76 : // static member definition
77 : // ===========================================================================
78 : MSEdge MSTriggeredRerouter::mySpecialDest_keepDestination("MSTriggeredRerouter_keepDestination", -1, SumoXMLEdgeFunc::UNKNOWN, "", "", "", -1, 0);
79 : MSEdge MSTriggeredRerouter::mySpecialDest_terminateRoute("MSTriggeredRerouter_terminateRoute", -1, SumoXMLEdgeFunc::UNKNOWN, "", "", "", -1, 0);
80 : const double MSTriggeredRerouter::DEFAULT_MAXDELAY(7200);
81 : std::map<std::string, MSTriggeredRerouter*> MSTriggeredRerouter::myInstances;
82 :
83 :
84 : // ===========================================================================
85 : // method definitions
86 : // ===========================================================================
87 4201 : MSTriggeredRerouter::MSTriggeredRerouter(const std::string& id,
88 : const MSEdgeVector& edges, double prob, bool off, bool optional,
89 4201 : SUMOTime timeThreshold, const std::string& vTypes, const Position& pos, const double radius) :
90 : Named(id),
91 : MSMoveReminder(id),
92 : MSStoppingPlaceRerouter("parking"),
93 4201 : myEdges(edges),
94 4201 : myProbability(prob),
95 4201 : myUserProbability(prob),
96 4201 : myAmInUserMode(false),
97 4201 : myAmOptional(optional),
98 4201 : myPosition(pos),
99 4201 : myRadius(radius),
100 4201 : myTimeThreshold(timeThreshold),
101 12603 : myHaveParkProbs(false) {
102 4201 : myInstances[id] = this;
103 : // build actors
104 9728 : for (const MSEdge* const e : edges) {
105 5527 : if (MSGlobals::gUseMesoSim) {
106 802 : MSGlobals::gMesoNet->getSegmentForEdge(*e)->addDetector(this);
107 : }
108 11746 : for (MSLane* const lane : e->getLanes()) {
109 6219 : lane->addMoveReminder(this);
110 : }
111 : }
112 4201 : if (off) {
113 1 : setUserMode(true);
114 1 : setUserUsageProbability(0);
115 : }
116 8402 : const std::vector<std::string> vt = StringTokenizer(vTypes).getVector();
117 : myVehicleTypes.insert(vt.begin(), vt.end());
118 : if (myPosition == Position::INVALID) {
119 3739 : myPosition = edges.front()->getLanes()[0]->getShape()[0];
120 : }
121 4201 : }
122 :
123 :
124 7616 : MSTriggeredRerouter::~MSTriggeredRerouter() {
125 : myInstances.erase(getID());
126 11813 : }
127 :
128 :
129 : // ------------ loading begin
130 : void
131 20166 : MSTriggeredRerouter::myStartElement(int element,
132 : const SUMOSAXAttributes& attrs) {
133 20166 : if (element == SUMO_TAG_INTERVAL) {
134 4058 : bool ok = true;
135 4058 : myParsedRerouteInterval = RerouteInterval();
136 4058 : myParsedRerouteInterval.begin = attrs.getOptSUMOTimeReporting(SUMO_ATTR_BEGIN, nullptr, ok, -1);
137 4058 : myParsedRerouteInterval.end = attrs.getOptSUMOTimeReporting(SUMO_ATTR_END, nullptr, ok, SUMOTime_MAX);
138 4058 : if (myParsedRerouteInterval.begin >= myParsedRerouteInterval.end) {
139 18 : throw ProcessError(TLF("rerouter '%': interval end % is not after begin %.", getID(),
140 : time2string(myParsedRerouteInterval.end),
141 18 : time2string(myParsedRerouteInterval.begin)));
142 : }
143 : }
144 20160 : if (element == SUMO_TAG_DEST_PROB_REROUTE) {
145 : // by giving probabilities of new destinations
146 : // get the destination edge
147 268 : std::string dest = attrs.getStringSecure(SUMO_ATTR_ID, "");
148 268 : if (dest == "") {
149 0 : throw ProcessError(TLF("rerouter '%': destProbReroute has no destination edge id.", getID()));
150 : }
151 268 : MSEdge* to = MSEdge::dictionary(dest);
152 268 : if (to == nullptr) {
153 111 : if (dest == "keepDestination") {
154 : to = &mySpecialDest_keepDestination;
155 83 : } else if (dest == "terminateRoute") {
156 : to = &mySpecialDest_terminateRoute;
157 : } else {
158 0 : throw ProcessError(TLF("rerouter '%': Destination edge '%' is not known.", getID(), dest));
159 : }
160 : }
161 : // get the probability to reroute
162 268 : bool ok = true;
163 268 : double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, getID().c_str(), ok, 1.);
164 268 : if (!ok) {
165 0 : throw ProcessError();
166 : }
167 268 : if (prob < 0) {
168 0 : throw ProcessError(TLF("rerouter '%': Attribute 'probability' for destination '%' is negative (must not).", getID(), dest));
169 : }
170 : // add
171 268 : myParsedRerouteInterval.edgeProbs.add(to, prob);
172 : }
173 :
174 20160 : if (element == SUMO_TAG_CLOSING_REROUTE) {
175 : // by closing edge
176 1131 : const std::string& closed_id = attrs.getStringSecure(SUMO_ATTR_ID, "");
177 1131 : MSEdge* const closedEdge = MSEdge::dictionary(closed_id);
178 1131 : if (closedEdge == nullptr) {
179 0 : throw ProcessError(TLF("rerouter '%': Edge '%' to close is not known.", getID(), closed_id));
180 : }
181 : bool ok;
182 1131 : SVCPermissions permissions = SVC_UNSPECIFIED;
183 1131 : if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
184 755 : const std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, getID().c_str(), ok, "", false);
185 755 : const std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, getID().c_str(), ok, "");
186 755 : permissions = parseVehicleClasses(allow, disallow);
187 : }
188 1131 : const SUMOTime until = attrs.getOptSUMOTimeReporting(SUMO_ATTR_UNTIL, nullptr, ok, TIME2STEPS(-1));
189 1131 : myParsedRerouteInterval.closed[closedEdge] = std::make_pair(permissions, STEPS2TIME(until));
190 : }
191 :
192 20160 : if (element == SUMO_TAG_CLOSING_LANE_REROUTE) {
193 : // by closing lane
194 141 : std::string closed_id = attrs.getStringSecure(SUMO_ATTR_ID, "");
195 141 : MSLane* closedLane = MSLane::dictionary(closed_id);
196 141 : if (closedLane == nullptr) {
197 0 : throw ProcessError(TLF("rerouter '%': Lane '%' to close is not known.", getID(), closed_id));
198 : }
199 : SVCPermissions permissions = SVC_AUTHORITY;
200 141 : if (attrs.hasAttribute(SUMO_ATTR_ALLOW) || attrs.hasAttribute(SUMO_ATTR_DISALLOW)) {
201 : bool ok;
202 68 : const std::string allow = attrs.getOpt<std::string>(SUMO_ATTR_ALLOW, getID().c_str(), ok, "", false);
203 68 : const std::string disallow = attrs.getOpt<std::string>(SUMO_ATTR_DISALLOW, getID().c_str(), ok, "");
204 68 : permissions = parseVehicleClasses(allow, disallow);
205 : }
206 141 : myParsedRerouteInterval.closedLanes[closedLane] = permissions;
207 : }
208 :
209 20160 : if (element == SUMO_TAG_ROUTE_PROB_REROUTE) {
210 : // by explicit rerouting using routes
211 : // check if route exists
212 774 : std::string routeStr = attrs.getStringSecure(SUMO_ATTR_ID, "");
213 774 : if (routeStr == "") {
214 0 : throw ProcessError(TLF("rerouter '%': routeProbReroute has no alternative route id.", getID()));
215 : }
216 774 : ConstMSRoutePtr route = MSRoute::dictionary(routeStr);
217 774 : if (route == nullptr) {
218 0 : throw ProcessError(TLF("rerouter '%': Alternative route '%' does not exist.", getID(), routeStr));
219 : }
220 :
221 : // get the probability to reroute
222 774 : bool ok = true;
223 : double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, getID().c_str(), ok, 1.);
224 774 : if (!ok) {
225 0 : throw ProcessError();
226 : }
227 774 : if (prob < 0) {
228 0 : throw ProcessError(TLF("rerouter '%': Attribute 'probability' for alternative route '%' is negative (must not).", getID(), routeStr));
229 : }
230 : // add
231 1548 : myParsedRerouteInterval.routeProbs.add(route, prob);
232 : }
233 :
234 20160 : if (element == SUMO_TAG_PARKING_AREA_REROUTE) {
235 13608 : std::string parkingarea = attrs.getStringSecure(SUMO_ATTR_ID, "");
236 13608 : if (parkingarea == "") {
237 0 : throw ProcessError(TLF("rerouter '%': parkingAreaReroute requires a parkingArea id.", getID()));
238 : }
239 13608 : MSParkingArea* pa = static_cast<MSParkingArea*>(MSNet::getInstance()->getStoppingPlace(parkingarea, SUMO_TAG_PARKING_AREA));
240 13608 : if (pa == nullptr) {
241 0 : throw ProcessError(TLF("rerouter '%': parkingArea '%' is not known.", getID(), parkingarea));
242 : }
243 : // get the probability to reroute
244 13608 : bool ok = true;
245 13608 : const double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, getID().c_str(), ok, 1.);
246 13608 : if (!ok) {
247 0 : throw ProcessError();
248 : }
249 13608 : if (prob < 0) {
250 0 : throw ProcessError(TLF("rerouter '%': Attribute 'probability' for parkingArea '%' is negative (must not).", getID(), parkingarea));
251 : }
252 13608 : const bool visible = attrs.getOpt<bool>(SUMO_ATTR_VISIBLE, getID().c_str(), ok, false);
253 : // add
254 13608 : myParsedRerouteInterval.parkProbs.add(std::make_pair(pa, visible), prob);
255 13608 : myHaveParkProbs = true;
256 : }
257 :
258 20160 : if (element == SUMO_TAG_VIA_PROB_REROUTE) {
259 : // by giving probabilities of vias
260 37 : std::string viaID = attrs.getStringSecure(SUMO_ATTR_ID, "");
261 37 : if (viaID == "") {
262 0 : throw ProcessError(TLF("rerouter '%': No via edge id given.", getID()));
263 : }
264 37 : MSEdge* const via = MSEdge::dictionary(viaID);
265 37 : if (via == nullptr) {
266 0 : throw ProcessError(TLF("rerouter '%': Via Edge '%' is not known.", getID(), viaID));
267 : }
268 : // get the probability to reroute
269 37 : bool ok = true;
270 37 : double prob = attrs.getOpt<double>(SUMO_ATTR_PROB, getID().c_str(), ok, 1.);
271 37 : if (!ok) {
272 0 : throw ProcessError();
273 : }
274 37 : if (prob < 0) {
275 0 : throw ProcessError(TLF("rerouter '%': Attribute 'probability' for via '%' is negative (must not).", getID(), viaID));
276 : }
277 : // add
278 37 : myParsedRerouteInterval.edgeProbs.add(via, prob);
279 37 : myParsedRerouteInterval.isVia = true;
280 : }
281 20160 : if (element == SUMO_TAG_OVERTAKING_REROUTE) {
282 : // for letting a slow train use a siding to be overtaken by a fast train
283 : OvertakeLocation oloc;
284 99 : bool ok = true;
285 396 : for (const std::string& edgeID : attrs.get<std::vector<std::string> >(SUMO_ATTR_MAIN, getID().c_str(), ok)) {
286 297 : MSEdge* edge = MSEdge::dictionary(edgeID);
287 297 : if (edge == nullptr) {
288 0 : throw InvalidArgument(TLF("The main edge '%' to use within rerouter '%' is not known.", edgeID, getID()));
289 : }
290 297 : oloc.main.push_back(edge);
291 297 : oloc.cMain.push_back(edge);
292 99 : }
293 396 : for (const std::string& edgeID : attrs.get<std::vector<std::string> >(SUMO_ATTR_SIDING, getID().c_str(), ok)) {
294 297 : MSEdge* edge = MSEdge::dictionary(edgeID);
295 297 : if (edge == nullptr) {
296 0 : throw InvalidArgument(TLF("The siding edge '%' to use within rerouter '%' is not known.", edgeID, getID()));
297 : }
298 297 : oloc.siding.push_back(edge);
299 297 : oloc.cSiding.push_back(edge);
300 99 : }
301 99 : oloc.sidingExit = findSignal(oloc.cSiding.begin(), oloc.cSiding.end());
302 99 : if (oloc.sidingExit == nullptr) {
303 0 : throw InvalidArgument(TLF("The siding within rerouter '%' does not have a rail signal.", getID()));
304 : }
305 198 : for (auto it = oloc.cSiding.begin(); it != oloc.cSiding.end(); it++) {
306 198 : oloc.sidingLength += (*it)->getLength();
307 198 : if ((*it)->getToJunction()->getID() == oloc.sidingExit->getID()) {
308 : break;
309 : }
310 : }
311 99 : oloc.minSaving = attrs.getOpt<double>(SUMO_ATTR_MINSAVING, getID().c_str(), ok, 300);
312 99 : const bool hasAlternatives = myParsedRerouteInterval.overtakeLocations.size() > 0;
313 99 : oloc.defer = attrs.getOpt<bool>(SUMO_ATTR_DEFER, getID().c_str(), ok, hasAlternatives);
314 99 : myParsedRerouteInterval.overtakeLocations.push_back(oloc);
315 99 : }
316 20160 : if (element == SUMO_TAG_STATION_REROUTE) {
317 : // for letting a train switch it's stopping place in case of conflict
318 45 : const std::string stopID = attrs.getStringSecure(SUMO_ATTR_ID, "");
319 45 : if (stopID == "") {
320 0 : throw ProcessError(TLF("rerouter '%': stationReroute requires a stopping place id.", getID()));
321 : }
322 45 : MSStoppingPlace* stop = MSNet::getInstance()->getStoppingPlace(stopID);
323 45 : if (stop == nullptr) {
324 0 : throw ProcessError(TLF("rerouter '%': stopping place '%' is not known.", getID(), stopID));
325 : }
326 45 : myParsedRerouteInterval.stopAlternatives.push_back(std::make_pair(stop, true));
327 : }
328 20160 : }
329 :
330 :
331 : void
332 24355 : MSTriggeredRerouter::myEndElement(int element) {
333 24355 : if (element == SUMO_TAG_INTERVAL) {
334 : // precompute permissionsAllowAll
335 : bool allowAll = true;
336 4424 : for (const auto& entry : myParsedRerouteInterval.closed) {
337 942 : allowAll = allowAll && entry.second.first == SVC_UNSPECIFIED;
338 : if (!allowAll) {
339 : break;
340 : }
341 : }
342 4052 : myParsedRerouteInterval.permissionsAllowAll = allowAll;
343 :
344 17657 : for (auto paVi : myParsedRerouteInterval.parkProbs.getVals()) {
345 13605 : dynamic_cast<MSParkingArea*>(paVi.first)->setNumAlternatives((int)myParsedRerouteInterval.parkProbs.getVals().size() - 1);
346 : }
347 4052 : if (myParsedRerouteInterval.closedLanes.size() > 0) {
348 : // collect edges that are affect by a closed lane
349 : std::set<MSEdge*> affected;
350 226 : for (std::pair<MSLane*, SVCPermissions> settings : myParsedRerouteInterval.closedLanes) {
351 135 : affected.insert(&settings.first->getEdge());
352 : }
353 91 : myParsedRerouteInterval.closedLanesAffected.insert(myParsedRerouteInterval.closedLanesAffected.begin(), affected.begin(), affected.end());
354 : }
355 4052 : const SUMOTime closingBegin = myParsedRerouteInterval.begin;
356 4052 : const SUMOTime simBegin = string2time(OptionsCont::getOptions().getString("begin"));
357 4052 : if (closingBegin < simBegin && myParsedRerouteInterval.end > simBegin) {
358 : // interval started before simulation begin but is still active at
359 : // the start of the simulation
360 415 : myParsedRerouteInterval.begin = simBegin;
361 : }
362 4052 : myIntervals.push_back(myParsedRerouteInterval);
363 4052 : myIntervals.back().id = (long long int)&myIntervals.back();
364 4052 : if (!(myParsedRerouteInterval.closed.empty() && myParsedRerouteInterval.closedLanes.empty())) {
365 1954 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(
366 977 : new WrappingCommand<MSTriggeredRerouter>(this, &MSTriggeredRerouter::setPermissions), myParsedRerouteInterval.begin);
367 : }
368 : }
369 24355 : }
370 :
371 :
372 : // ------------ loading end
373 :
374 :
375 : SUMOTime
376 1135 : MSTriggeredRerouter::setPermissions(const SUMOTime currentTime) {
377 : bool updateVehicles = false;
378 2313 : for (const RerouteInterval& i : myIntervals) {
379 1178 : if (i.begin == currentTime && !(i.closed.empty() && i.closedLanes.empty()) /*&& i.permissions != SVCAll*/) {
380 2057 : for (const auto& settings : i.closed) {
381 1095 : if (settings.second.first != SVC_UNSPECIFIED) {
382 1499 : for (MSLane* lane : settings.first->getLanes()) {
383 : //std::cout << SIMTIME << " closing: intervalID=" << i.id << " lane=" << lane->getID() << " prevPerm=" << getVehicleClassNames(lane->getPermissions()) << " new=" << getVehicleClassNames(settings.second.first) << "\n";
384 754 : lane->setPermissions(settings.second.first, i.id);
385 : }
386 745 : settings.first->rebuildAllowedLanes();
387 : updateVehicles = true;
388 : }
389 : }
390 1111 : for (std::pair<MSLane*, SVCPermissions> settings : i.closedLanes) {
391 149 : settings.first->setPermissions(settings.second, i.id);
392 149 : settings.first->getEdge().rebuildAllowedLanes();
393 : updateVehicles = true;
394 : }
395 1924 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(
396 962 : new WrappingCommand<MSTriggeredRerouter>(this, &MSTriggeredRerouter::setPermissions), i.end);
397 : }
398 1178 : if (i.end == currentTime && !(i.closed.empty() && i.closedLanes.empty()) /*&& i.permissions != SVCAll*/) {
399 394 : for (auto settings : i.closed) {
400 193 : if (settings.second.first != SVC_UNSPECIFIED) {
401 336 : for (MSLane* lane : settings.first->getLanes()) {
402 168 : lane->resetPermissions(i.id);
403 : //std::cout << SIMTIME << " opening: intervalID=" << i.id << " lane=" << lane->getID() << " restore prevPerm=" << getVehicleClassNames(lane->getPermissions()) << "\n";
404 : }
405 168 : settings.first->rebuildAllowedLanes();
406 : updateVehicles = true;
407 : }
408 : }
409 314 : for (std::pair<MSLane*, SVCPermissions> settings : i.closedLanes) {
410 113 : settings.first->resetPermissions(i.id);
411 113 : settings.first->getEdge().rebuildAllowedLanes();
412 : updateVehicles = true;
413 : }
414 : }
415 : }
416 1135 : if (updateVehicles) {
417 : // only vehicles on the affected lanes had their bestlanes updated so far
418 1638 : for (MSEdge* e : myEdges) {
419 : // also updates vehicles
420 822 : e->rebuildAllowedTargets();
421 : }
422 : }
423 1135 : return 0;
424 : }
425 :
426 :
427 : const MSTriggeredRerouter::RerouteInterval*
428 1202742 : MSTriggeredRerouter::getCurrentReroute(SUMOTime time, SUMOTrafficObject& obj) const {
429 1714561 : for (const RerouteInterval& ri : myIntervals) {
430 1216823 : if (ri.begin <= time && ri.end > time) {
431 : if (
432 : // destProbReroute
433 719514 : ri.edgeProbs.getOverallProb() > 0 ||
434 : // routeProbReroute
435 177002 : ri.routeProbs.getOverallProb() > 0 ||
436 : // parkingZoneReroute
437 790604 : ri.parkProbs.getOverallProb() > 0 ||
438 : // stationReroute
439 : ri.stopAlternatives.size() > 0) {
440 655051 : return &ri;
441 : }
442 67769 : if (!ri.closed.empty() || !ri.closedLanesAffected.empty() || !ri.overtakeLocations.empty()) {
443 67769 : const std::set<SUMOTrafficObject::NumericalID>& edgeIndices = obj.getUpcomingEdgeIDs();
444 135538 : if (affected(edgeIndices, ri.getClosedEdges())
445 67769 : || affected(edgeIndices, ri.closedLanesAffected)) {
446 49809 : return &ri;
447 : }
448 17960 : for (const OvertakeLocation& oloc : ri.overtakeLocations) {
449 144 : if (affected(edgeIndices, oloc.main)) {
450 : return &ri;
451 : }
452 : }
453 :
454 : }
455 : }
456 : }
457 : return nullptr;
458 : }
459 :
460 :
461 : const MSTriggeredRerouter::RerouteInterval*
462 318592 : MSTriggeredRerouter::getCurrentReroute(SUMOTime time) const {
463 318610 : for (const RerouteInterval& ri : myIntervals) {
464 318592 : if (ri.begin <= time && ri.end > time) {
465 318574 : if (ri.edgeProbs.getOverallProb() != 0 || ri.routeProbs.getOverallProb() != 0 || ri.parkProbs.getOverallProb() != 0
466 318592 : || !ri.closed.empty() || !ri.closedLanesAffected.empty() || !ri.overtakeLocations.empty()) {
467 : return &ri;
468 : }
469 : }
470 : }
471 : return nullptr;
472 : }
473 :
474 :
475 : bool
476 677990 : MSTriggeredRerouter::notifyEnter(SUMOTrafficObject& tObject, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
477 677990 : if (myAmOptional || myRadius != std::numeric_limits<double>::max()) {
478 : return true;
479 : }
480 677850 : return triggerRouting(tObject, reason);
481 : }
482 :
483 :
484 : bool
485 524316 : MSTriggeredRerouter::notifyMove(SUMOTrafficObject& veh, double /*oldPos*/,
486 : double /*newPos*/, double /*newSpeed*/) {
487 524316 : return triggerRouting(veh, NOTIFICATION_JUNCTION);
488 : }
489 :
490 :
491 : bool
492 19045 : MSTriggeredRerouter::notifyLeave(SUMOTrafficObject& /*veh*/, double /*lastPos*/,
493 : MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
494 19045 : return reason == NOTIFICATION_LANE_CHANGE;
495 : }
496 :
497 :
498 : bool
499 1202826 : MSTriggeredRerouter::triggerRouting(SUMOTrafficObject& tObject, MSMoveReminder::Notification reason) {
500 1202826 : if (!applies(tObject)) {
501 : return false;
502 : }
503 1202742 : if (myRadius != std::numeric_limits<double>::max() && tObject.getPosition().distanceTo(myPosition) > myRadius) {
504 0 : return true;
505 : }
506 : // check whether the vehicle shall be rerouted
507 1202742 : const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
508 1202742 : const MSTriggeredRerouter::RerouteInterval* const rerouteDef = getCurrentReroute(now, tObject);
509 1202742 : if (rerouteDef == nullptr) {
510 : return true; // an active interval could appear later
511 : }
512 705004 : const double prob = myAmInUserMode ? myUserProbability : myProbability;
513 705004 : if (prob < 1 && RandHelper::rand(tObject.getRNG()) > prob) {
514 : return false; // XXX another interval could appear later but we would have to track whether the current interval was already tried
515 : }
516 704715 : if (myTimeThreshold > 0 && MAX2(tObject.getWaitingTime(), tObject.getWaitingTime(true)) < myTimeThreshold) {
517 : return true; // waiting time may be reached later
518 : }
519 698278 : if (reason == NOTIFICATION_LANE_CHANGE) {
520 : return false;
521 : }
522 : // if we have a closingLaneReroute, only vehicles with a rerouting device can profit from rerouting (otherwise, edge weights will not reflect local jamming)
523 680950 : const bool hasReroutingDevice = tObject.getDevice(typeid(MSDevice_Routing)) != nullptr;
524 680950 : if (rerouteDef->closedLanes.size() > 0 && !hasReroutingDevice) {
525 : return true; // an active interval could appear later
526 : }
527 640956 : const MSEdge* lastEdge = tObject.getRerouteDestination();
528 : #ifdef DEBUG_REROUTER
529 : if (DEBUGCOND(tObject)) {
530 : std::cout << SIMTIME << " veh=" << tObject.getID() << " check rerouter " << getID() << " lane=" << Named::getIDSecure(tObject.getLane()) << " edge=" << tObject.getEdge()->getID() << " finalEdge=" << lastEdge->getID() /*<< " arrivalPos=" << tObject.getArrivalPos()*/ << "\n";
531 : }
532 : #endif
533 :
534 640956 : if (rerouteDef->parkProbs.getOverallProb() > 0) {
535 : #ifdef HAVE_FOX
536 103871 : ScopedLocker<> lock(myNotificationMutex, MSGlobals::gNumSimThreads > 1);
537 : #endif
538 103871 : if (!tObject.isVehicle()) {
539 : return false;
540 : }
541 : SUMOVehicle& veh = static_cast<SUMOVehicle&>(tObject);
542 103806 : bool newDestination = false;
543 : ConstMSEdgeVector newRoute;
544 103806 : MSParkingArea* oldParkingArea = veh.getNextParkingArea();
545 103806 : MSParkingArea* newParkingArea = rerouteParkingArea(rerouteDef, veh, newDestination, newRoute);
546 103806 : if (newParkingArea != nullptr) {
547 : // adapt plans of any riders
548 29018 : for (MSTransportable* p : veh.getPersons()) {
549 40 : p->rerouteParkingArea(oldParkingArea, newParkingArea);
550 : }
551 :
552 28978 : if (newDestination && veh.getParameter().arrivalPosProcedure != ArrivalPosDefinition::DEFAULT) {
553 : // update arrival parameters
554 50 : SUMOVehicleParameter* newParameter = new SUMOVehicleParameter();
555 50 : *newParameter = veh.getParameter();
556 50 : newParameter->arrivalPosProcedure = ArrivalPosDefinition::GIVEN;
557 50 : newParameter->arrivalPos = newParkingArea->getEndLanePosition();
558 50 : veh.replaceParameter(newParameter);
559 : }
560 :
561 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = hasReroutingDevice
562 12912 : ? MSRoutingEngine::getRouterTT(veh.getRNGIndex(), veh.getVClass(), rerouteDef->getClosed())
563 57956 : : MSNet::getInstance()->getRouterTT(veh.getRNGIndex(), rerouteDef->getClosed());
564 28978 : const double routeCost = router.recomputeCosts(newRoute, &veh, MSNet::getInstance()->getCurrentTimeStep());
565 28978 : ConstMSEdgeVector prevEdges(veh.getCurrentRouteEdge(), veh.getRoute().end());
566 28978 : resetClosedEdges(hasReroutingDevice, veh);
567 28978 : const double previousCost = router.recomputeCosts(prevEdges, &veh, MSNet::getInstance()->getCurrentTimeStep());
568 28978 : const double savings = previousCost - routeCost;
569 : //if (getID() == "ego") std::cout << SIMTIME << " pCost=" << previousCost << " cost=" << routeCost
570 : // << " prevEdges=" << toString(prevEdges)
571 : // << " newEdges=" << toString(edges)
572 : // << "\n";
573 :
574 : std::string errorMsg;
575 28978 : if (veh.replaceParkingArea(newParkingArea, errorMsg)) {
576 57956 : veh.replaceRouteEdges(newRoute, routeCost, savings, getID() + ":" + toString(SUMO_TAG_PARKING_AREA_REROUTE), false, false, false);
577 28978 : if (oldParkingArea->isReservable()) {
578 15 : oldParkingArea->removeSpaceReservation(&veh);
579 : }
580 28978 : if (newParkingArea->isReservable()) {
581 15 : newParkingArea->addSpaceReservation(&veh);
582 : }
583 : } else {
584 0 : WRITE_WARNING("Vehicle '" + veh.getID() + "' at rerouter '" + getID()
585 : + "' could not reroute to new parkingArea '" + newParkingArea->getID()
586 : + "' reason=" + errorMsg + ", time=" + time2string(MSNet::getInstance()->getCurrentTimeStep()) + ".");
587 : }
588 28978 : } else {
589 74828 : if (oldParkingArea && oldParkingArea->isReservable()) {
590 15 : oldParkingArea->addSpaceReservation(&veh);
591 : }
592 : }
593 : return false;
594 103806 : }
595 537085 : if (rerouteDef->overtakeLocations.size() > 0) {
596 144 : if (!tObject.isVehicle()) {
597 : return false;
598 : }
599 : SUMOVehicle& veh = static_cast<SUMOVehicle&>(tObject);
600 144 : const ConstMSEdgeVector& oldEdges = veh.getRoute().getEdges();
601 : double bestSavings = -std::numeric_limits<double>::max();
602 : double netSaving;
603 : int bestIndex = -1;
604 : MSRouteIterator bestMainStart = oldEdges.end();
605 : std::pair<const SUMOVehicle*, MSRailSignal*> best_overtaker_signal(nullptr, nullptr);
606 : int index = -1;
607 : // sort locations by descending distance to vehicle
608 : std::vector<std::pair<int, int> > sortedLocs;
609 330 : for (const OvertakeLocation& oloc : rerouteDef->overtakeLocations) {
610 186 : index++;
611 186 : if (veh.getLength() > oloc.sidingLength) {
612 3 : continue;
613 : }
614 183 : auto mainStart = std::find(veh.getCurrentRouteEdge(), oldEdges.end(), oloc.main.front());
615 183 : if (mainStart == oldEdges.end()
616 : // exit main within
617 366 : || ConstMSEdgeVector(mainStart, mainStart + oloc.main.size()) != oloc.cMain
618 : // stop in main
619 366 : || (veh.hasStops() && veh.getNextStop().edge < (mainStart + oloc.main.size()))) {
620 : //std::cout << SIMTIME << " veh=" << veh.getID() << " wrong route or stop\n";
621 0 : continue;
622 : }
623 : // negated iterator distance for descending order
624 183 : sortedLocs.push_back(std::make_pair(-(int)(mainStart - veh.getCurrentRouteEdge()), index));
625 : }
626 144 : std::sort(sortedLocs.begin(), sortedLocs.end());
627 327 : for (const auto& item : sortedLocs) {
628 183 : index = item.second;
629 183 : const OvertakeLocation& oloc = rerouteDef->overtakeLocations[index];
630 183 : auto mainStart = veh.getCurrentRouteEdge() - item.first; // subtracting negative difference
631 183 : std::pair<const SUMOVehicle*, MSRailSignal*> overtaker_signal = overtakingTrain(veh, mainStart, oloc, netSaving);
632 183 : if (overtaker_signal.first != nullptr && netSaving > bestSavings) {
633 : bestSavings = netSaving;
634 : bestIndex = index;
635 : best_overtaker_signal = overtaker_signal;
636 : bestMainStart = mainStart;
637 : #ifdef DEBUG_OVERTAKING
638 : std::cout << " newBest index=" << bestIndex << " saving=" << bestSavings << "\n";
639 : #endif
640 : }
641 : }
642 144 : if (bestIndex >= 0) {
643 41 : const OvertakeLocation& oloc = rerouteDef->overtakeLocations[bestIndex];
644 41 : if (oloc.defer) {
645 5 : return false;
646 : }
647 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = hasReroutingDevice
648 0 : ? MSRoutingEngine::getRouterTT(veh.getRNGIndex(), veh.getVClass(), rerouteDef->getClosed())
649 72 : : MSNet::getInstance()->getRouterTT(veh.getRNGIndex(), rerouteDef->getClosed());
650 36 : ConstMSEdgeVector newEdges(veh.getCurrentRouteEdge(), bestMainStart);
651 36 : newEdges.insert(newEdges.end(), oloc.siding.begin(), oloc.siding.end());
652 36 : newEdges.insert(newEdges.end(), bestMainStart + oloc.main.size(), oldEdges.end());
653 36 : const double routeCost = router.recomputeCosts(newEdges, &veh, MSNet::getInstance()->getCurrentTimeStep());
654 36 : const double savings = (router.recomputeCosts(oloc.cMain, &veh, MSNet::getInstance()->getCurrentTimeStep())
655 36 : - router.recomputeCosts(oloc.cSiding, &veh, MSNet::getInstance()->getCurrentTimeStep()));
656 72 : const std::string info = getID() + ":" + toString(SUMO_TAG_OVERTAKING_REROUTE) + ":" + best_overtaker_signal.first->getID();
657 36 : veh.replaceRouteEdges(newEdges, routeCost, savings, info, false, false, false);
658 36 : oloc.sidingExit->addConstraint(veh.getID(), new MSRailSignalConstraint_Predecessor(
659 36 : MSRailSignalConstraint::PREDECESSOR, best_overtaker_signal.second, best_overtaker_signal.first->getID(), 100, true));
660 36 : resetClosedEdges(hasReroutingDevice, veh);
661 36 : }
662 139 : return false;
663 144 : }
664 536941 : if (rerouteDef->stopAlternatives.size() > 0) {
665 : // somewhat similar to parkProbs but taking into account public transport schedule
666 15 : if (!tObject.isVehicle()) {
667 : return false;
668 : }
669 15 : checkStopSwitch(static_cast<MSBaseVehicle&>(tObject), rerouteDef);
670 : }
671 : // get rerouting params
672 536941 : ConstMSRoutePtr newRoute = rerouteDef->routeProbs.getOverallProb() > 0 ? rerouteDef->routeProbs.get() : nullptr;
673 : // we will use the route if given rather than calling our own dijsktra...
674 536941 : if (newRoute != nullptr) {
675 : #ifdef DEBUG_REROUTER
676 : if (DEBUGCOND(tObject)) {
677 : std::cout << " replacedRoute from routeDist " << newRoute->getID() << "\n";
678 : }
679 : #endif
680 530891 : tObject.replaceRoute(newRoute, getID());
681 530891 : return false; // XXX another interval could appear later but we would have to track whether the currenty interval was already used
682 : }
683 : const MSEdge* newEdge = lastEdge;
684 : // ok, try using a new destination
685 : double newArrivalPos = -1;
686 6050 : const MSEdgeVector closedEdges = rerouteDef->getClosedEdges();
687 6050 : const bool destUnreachable = std::find(closedEdges.begin(), closedEdges.end(), lastEdge) != closedEdges.end();
688 : bool keepDestination = false;
689 : // if we have a closingReroute, only assign new destinations to vehicles which cannot reach their original destination
690 : // if we have a closingLaneReroute, no new destinations should be assigned
691 6050 : if (closedEdges.empty() || destUnreachable || rerouteDef->isVia) {
692 4176 : newEdge = rerouteDef->edgeProbs.getOverallProb() > 0 ? rerouteDef->edgeProbs.get() : lastEdge;
693 : assert(newEdge != nullptr);
694 4176 : if (newEdge == &mySpecialDest_terminateRoute) {
695 : keepDestination = true;
696 55 : newEdge = tObject.getEdge();
697 55 : newArrivalPos = tObject.getPositionOnLane(); // instant arrival
698 4121 : } else if (newEdge == &mySpecialDest_keepDestination || newEdge == lastEdge) {
699 1577 : if (destUnreachable && rerouteDef->permissionsAllowAll) {
700 : // if permissions aren't set vehicles will simply drive through
701 : // the closing unless terminated. If the permissions are specified, assume that the user wants
702 : // vehicles to stand and wait until the closing ends
703 63 : WRITE_WARNINGF(TL("Cannot keep destination edge '%' for vehicle '%' due to closed edges. Terminating route."), lastEdge->getID(), tObject.getID());
704 21 : newEdge = tObject.getEdge();
705 : } else {
706 : newEdge = lastEdge;
707 : }
708 : }
709 : }
710 : ConstMSEdgeVector edges;
711 : std::vector<MSTransportableRouter::TripItem> items;
712 : // we have a new destination, let's replace the route (if it is affected)
713 6050 : MSEdgeVector closed = rerouteDef->getClosedEdges();
714 6050 : Prohibitions prohibited = rerouteDef->getClosed();
715 7924 : if (rerouteDef->closed.empty() || destUnreachable || rerouteDef->isVia || affected(tObject.getUpcomingEdgeIDs(), closed)) {
716 5973 : if (tObject.isVehicle()) {
717 : SUMOVehicle& veh = static_cast<SUMOVehicle&>(tObject);
718 4503 : ConstMSEdgeVector prevEdges = veh.getRoute().getEdges();
719 4503 : const bool canChangeDest = rerouteDef->edgeProbs.getOverallProb() > 0;
720 : MSVehicleRouter& router = hasReroutingDevice
721 4503 : ? MSRoutingEngine::getRouterTT(veh.getRNGIndex(), veh.getVClass(), prohibited)
722 1811 : : MSNet::getInstance()->getRouterTT(veh.getRNGIndex(), prohibited);
723 : bool ok = false;
724 : try {
725 4503 : ok = veh.reroute(now, getID(), router, false, false, canChangeDest, newEdge);
726 17 : } catch (ProcessError&) {}
727 4503 : if (!ok && !keepDestination && canChangeDest) {
728 : // destination unreachable due to closed intermediate edges. pick among alternative targets
729 96 : RandomDistributor<MSEdge*> edgeProbs2 = rerouteDef->edgeProbs;
730 96 : edgeProbs2.remove(const_cast<MSEdge*>(newEdge));
731 180 : while (!ok && edgeProbs2.getVals().size() > 0) {
732 124 : newEdge = edgeProbs2.get();
733 124 : edgeProbs2.remove(const_cast<MSEdge*>(newEdge));
734 124 : if (newEdge == &mySpecialDest_terminateRoute) {
735 56 : newEdge = veh.getEdge();
736 56 : newArrivalPos = veh.getPositionOnLane(); // instant arrival
737 70 : while (veh.hasStops()) {
738 14 : veh.abortNextStop();
739 : }
740 : }
741 124 : if (newEdge == &mySpecialDest_keepDestination && !rerouteDef->permissionsAllowAll) {
742 : newEdge = lastEdge;
743 : break;
744 : }
745 : try {
746 84 : ok = veh.reroute(now, getID(), router, false, false, true, newEdge);
747 0 : } catch (ProcessError&) {}
748 : }
749 : }
750 4503 : resetClosedEdges(hasReroutingDevice, tObject);
751 4503 : if (ok) {
752 : // since the old route was closed, savings would be infinite. This isn't useful
753 4076 : const double previousCost = router.recomputeCosts(prevEdges, &veh, MSNet::getInstance()->getCurrentTimeStep());
754 4076 : const double savings = previousCost - veh.getRoute().getCosts();
755 4076 : const_cast<MSRoute&>(veh.getRoute()).setSavings(savings);
756 : }
757 4503 : if (!rerouteDef->isVia) {
758 : #ifdef DEBUG_REROUTER
759 : if (DEBUGCOND(tObject)) std::cout << " rerouting: newDest=" << newEdge->getID()
760 : << " newEdges=" << toString(edges)
761 : << " newArrivalPos=" << newArrivalPos << " numClosed=" << rerouteDef->closed.size()
762 : << " destUnreachable=" << destUnreachable << " containsClosed=" << veh.getRoute().containsAnyOf(rerouteDef->getClosedEdges()) << "\n";
763 : #endif
764 4503 : if (ok && newArrivalPos != -1) {
765 : // must be called here because replaceRouteEdges may also set the arrivalPos
766 100 : veh.setArrivalPos(newArrivalPos);
767 : }
768 :
769 : }
770 4503 : } else {
771 : // person rerouting here
772 : MSTransportableRouter& router = hasReroutingDevice
773 1470 : ? MSRoutingEngine::getIntermodalRouterTT(tObject.getRNGIndex(), prohibited)
774 1470 : : MSNet::getInstance()->getIntermodalRouter(tObject.getRNGIndex(), 0, prohibited);
775 4410 : const bool success = router.compute(tObject.getEdge(), newEdge, tObject.getPositionOnLane(), "",
776 1470 : rerouteDef->isVia ? newEdge->getLength() / 2. : tObject.getParameter().arrivalPos, "",
777 1470 : tObject.getMaxSpeed(), nullptr, tObject.getVTypeParameter(), 0, now, items);
778 1470 : if (!rerouteDef->isVia) {
779 700 : if (success) {
780 1400 : for (const MSTransportableRouter::TripItem& it : items) {
781 700 : if (!it.edges.empty() && !edges.empty() && edges.back() == it.edges.front()) {
782 : edges.pop_back();
783 : }
784 700 : edges.insert(edges.end(), std::make_move_iterator(it.edges.begin()), std::make_move_iterator(it.edges.end()));
785 700 : if (!edges.empty()) {
786 700 : static_cast<MSPerson&>(tObject).replaceWalk(edges, tObject.getPositionOnLane(), 0, 1);
787 : }
788 : }
789 : } else {
790 : // maybe the pedestrian model still finds a way (JuPedSim)
791 0 : static_cast<MSPerson&>(tObject).replaceWalk({tObject.getEdge(), newEdge}, tObject.getPositionOnLane(), 0, 1);
792 : }
793 : }
794 : }
795 5973 : if (!prohibited.empty()) {
796 1882 : resetClosedEdges(hasReroutingDevice, tObject);
797 : }
798 : }
799 : // it was only a via so calculate the remaining part
800 6050 : if (rerouteDef->isVia) {
801 770 : if (tObject.isVehicle()) {
802 : SUMOVehicle& veh = static_cast<SUMOVehicle&>(tObject);
803 0 : if (!edges.empty()) {
804 : edges.pop_back();
805 : }
806 : MSVehicleRouter& router = hasReroutingDevice
807 0 : ? MSRoutingEngine::getRouterTT(veh.getRNGIndex(), veh.getVClass(), prohibited)
808 0 : : MSNet::getInstance()->getRouterTT(veh.getRNGIndex(), prohibited);
809 0 : router.compute(newEdge, lastEdge, &veh, now, edges);
810 0 : const double routeCost = router.recomputeCosts(edges, &veh, now);
811 : hasReroutingDevice
812 0 : ? MSRoutingEngine::getRouterTT(veh.getRNGIndex(), veh.getVClass())
813 0 : : MSNet::getInstance()->getRouterTT(veh.getRNGIndex()); // reset closed edges
814 0 : const bool useNewRoute = veh.replaceRouteEdges(edges, routeCost, 0, getID());
815 : #ifdef DEBUG_REROUTER
816 : if (DEBUGCOND(tObject)) std::cout << " rerouting: newDest=" << newEdge->getID()
817 : << " newEdges=" << toString(edges)
818 : << " useNewRoute=" << useNewRoute << " newArrivalPos=" << newArrivalPos << " numClosed=" << rerouteDef->closed.size()
819 : << " destUnreachable=" << destUnreachable << " containsClosed=" << veh.getRoute().containsAnyOf(rerouteDef->getClosedEdges()) << "\n";
820 : #endif
821 0 : if (useNewRoute && newArrivalPos != -1) {
822 : // must be called here because replaceRouteEdges may also set the arrivalPos
823 0 : veh.setArrivalPos(newArrivalPos);
824 : }
825 : } else {
826 : // person rerouting here
827 770 : bool success = !items.empty();
828 770 : if (success) {
829 : MSTransportableRouter& router = hasReroutingDevice
830 770 : ? MSRoutingEngine::getIntermodalRouterTT(tObject.getRNGIndex(), prohibited)
831 770 : : MSNet::getInstance()->getIntermodalRouter(tObject.getRNGIndex(), 0, prohibited);
832 2310 : success = router.compute(newEdge, lastEdge, newEdge->getLength() / 2., "",
833 770 : tObject.getParameter().arrivalPos, "",
834 770 : tObject.getMaxSpeed(), nullptr, tObject.getVTypeParameter(), 0, now, items);
835 : }
836 770 : if (success) {
837 2310 : for (const MSTransportableRouter::TripItem& it : items) {
838 1540 : if (!it.edges.empty() && !edges.empty() && edges.back() == it.edges.front()) {
839 : edges.pop_back();
840 : }
841 1540 : edges.insert(edges.end(), std::make_move_iterator(it.edges.begin()), std::make_move_iterator(it.edges.end()));
842 : }
843 770 : if (!edges.empty()) {
844 770 : static_cast<MSPerson&>(tObject).replaceWalk(edges, tObject.getPositionOnLane(), 0, 1);
845 : }
846 : } else {
847 : // maybe the pedestrian model still finds a way (JuPedSim)
848 0 : static_cast<MSPerson&>(tObject).replaceWalk({tObject.getEdge(), newEdge, lastEdge}, tObject.getPositionOnLane(), 0, 1);
849 : }
850 : }
851 770 : if (!prohibited.empty()) {
852 0 : resetClosedEdges(hasReroutingDevice, tObject);
853 : }
854 : }
855 : return false; // XXX another interval could appear later but we would have to track whether the currenty interval was already used
856 6050 : }
857 :
858 :
859 : void
860 1 : MSTriggeredRerouter::setUserMode(bool val) {
861 1 : myAmInUserMode = val;
862 1 : }
863 :
864 :
865 : void
866 1 : MSTriggeredRerouter::setUserUsageProbability(double prob) {
867 1 : myUserProbability = prob;
868 1 : }
869 :
870 :
871 : bool
872 0 : MSTriggeredRerouter::inUserMode() const {
873 0 : return myAmInUserMode;
874 : }
875 :
876 :
877 : double
878 5815 : MSTriggeredRerouter::getProbability() const {
879 5815 : return myAmInUserMode ? myUserProbability : myProbability;
880 : }
881 :
882 :
883 : double
884 0 : MSTriggeredRerouter::getUserProbability() const {
885 0 : return myUserProbability;
886 : }
887 :
888 :
889 : double
890 97253 : MSTriggeredRerouter::getStoppingPlaceOccupancy(MSStoppingPlace* sp, const SUMOVehicle* veh) {
891 : return (sp->getElement() == SUMO_TAG_PARKING_AREA
892 97253 : ? (double)dynamic_cast<MSParkingArea*>(sp)->getOccupancyIncludingRemoteReservations(veh)
893 42 : : (double)sp->getStoppedVehicles().size());
894 : }
895 :
896 :
897 : double
898 93033 : MSTriggeredRerouter::getLastStepStoppingPlaceOccupancy(MSStoppingPlace* sp, const SUMOVehicle* veh) {
899 : return (sp->getElement() == SUMO_TAG_PARKING_AREA
900 93033 : ? (double)dynamic_cast<MSParkingArea*>(sp)->getLastStepOccupancyIncludingRemoteReservations(veh)
901 42 : : (double)sp->getStoppedVehicles().size());
902 : }
903 :
904 :
905 : double
906 276843 : MSTriggeredRerouter::getStoppingPlaceCapacity(MSStoppingPlace* sp) {
907 : if (myBlockedStoppingPlaces.count(sp) == 0) {
908 : return (double)(sp->getElement() == SUMO_TAG_PARKING_AREA
909 276827 : ? dynamic_cast<MSParkingArea*>(sp)->getCapacity()
910 : // assume only one vehicle at a time (for stationReroute)
911 : : 1.);
912 : } else {
913 : return 0.;
914 : }
915 : }
916 :
917 :
918 : void
919 89984 : MSTriggeredRerouter::rememberBlockedStoppingPlace(SUMOVehicle& veh, const MSStoppingPlace* parkingArea, bool blocked) {
920 89984 : veh.rememberBlockedParkingArea(parkingArea, blocked);
921 89984 : }
922 :
923 :
924 : void
925 132770 : MSTriggeredRerouter::rememberStoppingPlaceScore(SUMOVehicle& veh, MSStoppingPlace* parkingArea, const std::string& score) {
926 132770 : veh.rememberParkingAreaScore(parkingArea, score);
927 132770 : }
928 :
929 :
930 : void
931 29834 : MSTriggeredRerouter::resetStoppingPlaceScores(SUMOVehicle& veh) {
932 29834 : veh.resetParkingAreaScores();
933 29834 : }
934 :
935 :
936 : SUMOTime
937 72076 : MSTriggeredRerouter::sawBlockedStoppingPlace(SUMOVehicle& veh, MSStoppingPlace* parkingArea, bool local) {
938 72076 : return veh.sawBlockedParkingArea(parkingArea, local);
939 : }
940 :
941 :
942 : int
943 69270 : MSTriggeredRerouter::getNumberStoppingPlaceReroutes(SUMOVehicle& veh) {
944 69270 : return veh.getNumberParkingReroutes();
945 : }
946 :
947 :
948 : void
949 29834 : MSTriggeredRerouter::setNumberStoppingPlaceReroutes(SUMOVehicle& veh, int value) {
950 29834 : veh.setNumberParkingReroutes(value);
951 29834 : }
952 :
953 :
954 : MSParkingArea*
955 103806 : MSTriggeredRerouter::rerouteParkingArea(const MSTriggeredRerouter::RerouteInterval* rerouteDef,
956 : SUMOVehicle& veh, bool& newDestination, ConstMSEdgeVector& newRoute) {
957 103806 : MSStoppingPlace* destStoppingPlace = veh.getNextParkingArea();
958 103806 : if (destStoppingPlace == nullptr) {
959 : // not driving towards the right type of stop
960 : return nullptr;
961 : }
962 : std::vector<StoppingPlaceVisible> parks;
963 295248 : for (auto cand : rerouteDef->parkProbs.getVals()) {
964 202133 : if (cand.first->accepts(&veh)) {
965 201453 : parks.push_back(cand);
966 : }
967 : }
968 : StoppingPlaceParamMap_t addInput = {};
969 186230 : return dynamic_cast<MSParkingArea*>(rerouteStoppingPlace(destStoppingPlace, parks, rerouteDef->parkProbs.getProbs(), veh, newDestination, newRoute, addInput, rerouteDef->getClosed()));
970 93115 : }
971 :
972 :
973 : std::pair<const SUMOVehicle*, MSRailSignal*>
974 183 : MSTriggeredRerouter::overtakingTrain(const SUMOVehicle& veh,
975 : ConstMSEdgeVector::const_iterator mainStart,
976 : const OvertakeLocation& oloc,
977 : double& netSaving) {
978 183 : const ConstMSEdgeVector& route = veh.getRoute().getEdges();
979 : const MSEdgeVector& main = oloc.main;
980 183 : const double vMax = veh.getMaxSpeed();
981 366 : const double prio = veh.getFloatParam(toString(SUMO_TAG_OVERTAKING_REROUTE) + ".prio", false, DEFAULT_PRIO_OVERTAKEN, false);
982 183 : MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
983 437 : for (MSVehicleControl::constVehIt it_veh = c.loadedVehBegin(); it_veh != c.loadedVehEnd(); ++it_veh) {
984 307 : const MSBaseVehicle* veh2 = dynamic_cast<const MSBaseVehicle*>((*it_veh).second);
985 307 : if (veh2->isOnRoad() && veh2->getMaxSpeed() > vMax) {
986 93 : const double arrivalDelay = veh2->getStopArrivalDelay();
987 182 : const double delay = MAX2(veh2->getStopDelay(), arrivalDelay == INVALID_DOUBLE ? 0 : arrivalDelay);
988 279 : if (delay > veh2->getFloatParam(toString(SUMO_TAG_OVERTAKING_REROUTE) + ".maxDelay", false, DEFAULT_MAXDELAY, false)) {
989 : continue;
990 : }
991 89 : const ConstMSEdgeVector& route2 = veh2->getRoute().getEdges();
992 : auto itOnMain2 = route2.end();
993 : int mainIndex = 0;
994 110 : for (const MSEdge* m : main) {
995 105 : itOnMain2 = std::find(veh2->getCurrentRouteEdge(), route2.end(), m);
996 105 : if (itOnMain2 != route2.end()) {
997 : break;
998 : }
999 21 : mainIndex++;
1000 : }
1001 89 : if (itOnMain2 != route2.end() && itOnMain2 > veh2->getCurrentRouteEdge()) {
1002 : auto itOnMain = mainStart + mainIndex;
1003 : double timeToMain = 0;
1004 371 : for (auto it = veh.getCurrentRouteEdge(); it != itOnMain; it++) {
1005 290 : timeToMain += (*it)->getMinimumTravelTime(&veh);
1006 : }
1007 : // veh2 may be anywhere on the current edge so we have to discount
1008 81 : double timeToMain2 = -veh2->getEdge()->getMinimumTravelTime(veh2) * veh2->getPositionOnLane() / veh2->getEdge()->getLength();
1009 : double timeToLastSignal2 = timeToMain2;
1010 562 : for (auto it = veh2->getCurrentRouteEdge(); it != itOnMain2; it++) {
1011 481 : timeToMain2 += (*it)->getMinimumTravelTime(veh2);
1012 481 : auto signal = getRailSignal(*it);
1013 481 : if (signal) {
1014 : timeToLastSignal2 = timeToMain2;
1015 : #ifdef DEBUG_OVERTAKING
1016 : std::cout << " lastBeforeMain2 " << signal->getID() << "\n";
1017 : #endif
1018 : }
1019 : }
1020 : double exitMainTime = timeToMain;
1021 : double exitMainBlockTime2 = timeToMain2;
1022 : double commonTime = 0;
1023 : double commonTime2 = 0;
1024 : int nCommon = 0;
1025 : auto exitMain2 = itOnMain2;
1026 : const MSRailSignal* firstAfterMain = nullptr;
1027 : const MSEdge* common = nullptr;
1028 81 : double vMinCommon = (*itOnMain)->getVehicleMaxSpeed(&veh);
1029 81 : double vMinCommon2 = (*itOnMain2)->getVehicleMaxSpeed(veh2);
1030 : while (itOnMain2 != route2.end()
1031 794 : && itOnMain != route.end()
1032 1657 : && *itOnMain == *itOnMain2) {
1033 : common = *itOnMain;
1034 785 : commonTime += common->getMinimumTravelTime(&veh);
1035 785 : commonTime2 += common->getMinimumTravelTime(veh2);
1036 785 : vMinCommon = MIN2(vMinCommon, common->getVehicleMaxSpeed(&veh));
1037 785 : vMinCommon2 = MIN2(vMinCommon2, common->getVehicleMaxSpeed(veh2));
1038 785 : const bool onMain = nCommon < (int)main.size() - mainIndex;
1039 785 : if (onMain) {
1040 240 : exitMainTime = timeToMain + commonTime;
1041 : }
1042 785 : if (firstAfterMain == nullptr) {
1043 480 : exitMainBlockTime2 = timeToMain2 + commonTime2;
1044 : }
1045 785 : auto signal = getRailSignal(common);
1046 785 : if (signal) {
1047 318 : if (!onMain && firstAfterMain == nullptr) {
1048 : firstAfterMain = signal;
1049 : #ifdef DEBUG_OVERTAKING
1050 : std::cout << " firstAfterMain " << signal->getID() << "\n";
1051 : #endif
1052 : }
1053 : }
1054 785 : nCommon++;
1055 : itOnMain++;
1056 : itOnMain2++;
1057 : }
1058 81 : const double vMaxLast = common->getVehicleMaxSpeed(&veh);
1059 81 : const double vMaxLast2 = common->getVehicleMaxSpeed(veh2);
1060 81 : commonTime += veh.getLength() / vMaxLast;
1061 81 : exitMainBlockTime2 += veh2->getLength() / vMaxLast2;
1062 81 : exitMain2 += MIN2(nCommon, (int)main.size() - mainIndex);
1063 81 : double timeLoss2 = MAX2(0.0, timeToMain + veh.getLength() / oloc.siding.front()->getVehicleMaxSpeed(&veh) - timeToLastSignal2);
1064 81 : const double saving = timeToMain + commonTime - (timeToMain2 + commonTime2) - timeLoss2;
1065 81 : const double loss = exitMainBlockTime2 - exitMainTime;
1066 162 : const double prio2 = veh2->getFloatParam(toString(SUMO_TAG_OVERTAKING_REROUTE) + ".prio", false, DEFAULT_PRIO_OVERTAKER, false);
1067 : // losses from acceleration after stopping at a signal
1068 81 : const double accelTimeLoss = loss > 0 ? 0.5 * vMinCommon / veh.getVehicleType().getCarFollowModel().getMaxAccel() : 0;
1069 81 : const double accelTimeLoss2 = timeLoss2 > 0 ? 0.5 * vMinCommon2 / veh2->getVehicleType().getCarFollowModel().getMaxAccel() : 0;
1070 81 : netSaving = prio2 * (saving - accelTimeLoss2) - prio * (loss + accelTimeLoss);
1071 : #ifdef DEBUG_OVERTAKING
1072 : std::cout << SIMTIME << " veh=" << veh.getID() << " veh2=" << veh2->getID()
1073 : << " sidingStart=" << oloc.siding.front()->getID()
1074 : << " ttm=" << timeToMain << " ttm2=" << timeToMain2
1075 : << " nCommon=" << nCommon << " cT=" << commonTime << " cT2=" << commonTime2
1076 : << " em=" << exitMainTime << " emb2=" << exitMainBlockTime2
1077 : << " ttls2=" << timeToLastSignal2
1078 : << " saving=" << saving << " loss=" << loss
1079 : << " atl=" << accelTimeLoss << " atl2=" << accelTimeLoss2 << " tl2=" << timeLoss2
1080 : << " prio=" << prio << " prio2=" << prio2 << " netSaving=" << netSaving << "\n";
1081 : #endif
1082 81 : if (netSaving > oloc.minSaving) {
1083 53 : MSRailSignal* s = findSignal(veh2->getCurrentRouteEdge(), exitMain2);
1084 53 : if (s != nullptr) {
1085 53 : return std::make_pair(veh2, s);
1086 : }
1087 : }
1088 : }
1089 : }
1090 : }
1091 130 : return std::make_pair(nullptr, nullptr);
1092 : }
1093 :
1094 :
1095 : void
1096 15 : MSTriggeredRerouter::checkStopSwitch(MSBaseVehicle& ego, const MSTriggeredRerouter::RerouteInterval* def) {
1097 : myBlockedStoppingPlaces.clear();
1098 : #ifdef DEBUG_REROUTER
1099 : std::cout << SIMTIME << " " << getID() << " ego=" << ego.getID() << "\n";
1100 : #endif
1101 15 : if (!ego.hasStops()) {
1102 1 : return;
1103 : }
1104 15 : const MSStop& stop = ego.getNextStop();
1105 15 : if (stop.reached || stop.joinTriggered || (stop.pars.arrival < 0 && stop.pars.until < 0)) {
1106 : return;
1107 : }
1108 15 : MSStoppingPlace* cur = nullptr;
1109 30 : for (MSStoppingPlace* sp : stop.getPlaces()) {
1110 15 : for (auto item : def->stopAlternatives) {
1111 15 : if (sp == item.first) {
1112 15 : cur = sp;
1113 15 : break;
1114 : }
1115 : }
1116 15 : }
1117 15 : if (cur == nullptr) {
1118 : return;
1119 : }
1120 15 : std::vector<const SUMOVehicle*> stopped = cur->getStoppedVehicles();
1121 : #ifdef DEBUG_REROUTER
1122 : std::cout << SIMTIME << " " << getID() << " ego=" << ego.getID() << " stopped=" << toString(stopped) << "\n";
1123 : #endif
1124 : SUMOTime stoppedDuration = -1;
1125 15 : if (stopped.empty()) {
1126 : /// look upstream for vehicles that stop on this lane before ego arrives
1127 9 : const MSLane& stopLane = cur->getLane();
1128 9 : MSVehicleControl& c = MSNet::getInstance()->getVehicleControl();
1129 28 : for (MSVehicleControl::constVehIt it_veh = c.loadedVehBegin(); it_veh != c.loadedVehEnd(); ++it_veh) {
1130 19 : const MSBaseVehicle* veh = dynamic_cast<const MSBaseVehicle*>((*it_veh).second);
1131 19 : if (veh->isOnRoad() && veh->hasStops()) {
1132 19 : const MSStop& vehStop = veh->getNextStop();
1133 19 : if (vehStop.pars.lane == stopLane.getID()) {
1134 : myBlockedStoppingPlaces.insert(cur);
1135 12 : if (veh->isStopped()) {
1136 : // stopped somewhere else on the same lane
1137 3 : stoppedDuration = MAX3((SUMOTime)0, stoppedDuration, veh->getStopDuration());
1138 : } else {
1139 9 : std::pair<double, double> timeDist = veh->estimateTimeToNextStop();
1140 9 : SUMOTime timeTo = TIME2STEPS(timeDist.first);
1141 9 : stoppedDuration = MAX3((SUMOTime)0, stoppedDuration, timeTo + vehStop.getMinDuration(SIMSTEP + timeTo));
1142 : }
1143 : }
1144 : }
1145 : }
1146 : } else {
1147 : stoppedDuration = 0;
1148 12 : for (const SUMOVehicle* veh : cur->getStoppedVehicles()) {
1149 6 : stoppedDuration = MAX2(stoppedDuration, veh->getStopDuration());
1150 6 : }
1151 : }
1152 15 : if (stoppedDuration < 0) {
1153 : return;
1154 : }
1155 : /// @todo: consider time for conflict veh to leave the block
1156 15 : const SUMOTime stopFree = SIMSTEP + stoppedDuration;
1157 15 : const SUMOTime scheduledArrival = stop.pars.arrival >= 0 ? stop.pars.arrival : stop.pars.until - stop.pars.duration;
1158 : #ifdef DEBUG_REROUTER
1159 : std::cout << SIMTIME << " " << getID() << " ego=" << ego.getID() << " stopFree=" << stopFree << " scheduledArrival=" << time2string(scheduledArrival) << "\n";
1160 : #endif
1161 15 : if (stopFree < scheduledArrival) {
1162 : // no conflict according to the schedule
1163 : return;
1164 : }
1165 14 : const SUMOTime estimatedArrival = SIMSTEP + (stop.pars.arrival >= 0
1166 14 : ? TIME2STEPS(ego.getStopArrivalDelay())
1167 16 : : TIME2STEPS(ego.getStopDelay()) - stop.pars.duration);
1168 : #ifdef DEBUG_REROUTER
1169 : std::cout << SIMTIME << " " << getID() << " ego=" << ego.getID() << " stopFree=" << stopFree << " estimatedArrival=" << time2string(estimatedArrival) << "\n";
1170 : #endif
1171 14 : if (stopFree < estimatedArrival) {
1172 : // no conflict when considering current delay
1173 : return;
1174 : }
1175 14 : const std::vector<double> probs(def->stopAlternatives.size(), 1.);
1176 : StoppingPlaceParamMap_t scores = {};
1177 : bool newDestination;
1178 : ConstMSEdgeVector newRoute;
1179 : // @todo: consider future conflicts caused by rerouting
1180 : // @todo: reject alternatives with large detour
1181 14 : const MSStoppingPlace* alternative = rerouteStoppingPlace(nullptr, def->stopAlternatives, probs, ego, newDestination, newRoute, scores);
1182 : #ifdef DEBUG_REROUTER
1183 : std::cout << SIMTIME << " " << getID() << " ego=" << ego.getID() << " alternative=" << Named::getIDSecure(alternative) << "\n";
1184 : #endif
1185 14 : if (alternative != nullptr) {
1186 : // @todo adapt plans of any riders
1187 : //for (MSTransportable* p : ego.getPersons()) {
1188 : // p->rerouteParkingArea(ego.getNextParkingArea(), newParkingArea);
1189 : //}
1190 :
1191 14 : if (newDestination && ego.getParameter().arrivalPosProcedure != ArrivalPosDefinition::DEFAULT) {
1192 : // update arrival parameters
1193 0 : SUMOVehicleParameter* newParameter = new SUMOVehicleParameter();
1194 0 : *newParameter = ego.getParameter();
1195 0 : newParameter->arrivalPosProcedure = ArrivalPosDefinition::GIVEN;
1196 0 : newParameter->arrivalPos = alternative->getEndLanePosition();
1197 0 : ego.replaceParameter(newParameter);
1198 : }
1199 :
1200 14 : SUMOVehicleParameter::Stop newStop = stop.pars;
1201 14 : newStop.lane = alternative->getLane().getID();
1202 14 : newStop.startPos = alternative->getBeginLanePosition();
1203 14 : newStop.endPos = alternative->getEndLanePosition();
1204 14 : switch (alternative->getElement()) {
1205 0 : case SUMO_TAG_PARKING_AREA:
1206 : newStop.parkingarea = alternative->getID();
1207 : break;
1208 0 : case SUMO_TAG_CONTAINER_STOP:
1209 : newStop.containerstop = alternative->getID();
1210 : break;
1211 0 : case SUMO_TAG_CHARGING_STATION:
1212 : newStop.chargingStation = alternative->getID();
1213 : break;
1214 0 : case SUMO_TAG_OVERHEAD_WIRE_SEGMENT:
1215 : newStop.overheadWireSegment = alternative->getID();
1216 : break;
1217 14 : case SUMO_TAG_BUS_STOP:
1218 : case SUMO_TAG_TRAIN_STOP:
1219 : default:
1220 : newStop.busstop = alternative->getID();
1221 : }
1222 : std::string errorMsg;
1223 28 : if (!ego.replaceStop(0, newStop, getID() + ":" + toString(SUMO_TAG_STATION_REROUTE), false, errorMsg)) {
1224 0 : WRITE_WARNING("Vehicle '" + ego.getID() + "' at rerouter '" + getID()
1225 : + "' could not perform stationReroute to '" + alternative->getID()
1226 : + "' reason=" + errorMsg + ", time=" + time2string(MSNet::getInstance()->getCurrentTimeStep()) + ".");
1227 : }
1228 14 : }
1229 29 : }
1230 :
1231 :
1232 : MSRailSignal*
1233 152 : MSTriggeredRerouter::findSignal(ConstMSEdgeVector::const_iterator begin, ConstMSEdgeVector::const_iterator end) {
1234 152 : auto it = end;
1235 : do {
1236 : it--;
1237 304 : auto signal = getRailSignal(*it);
1238 304 : if (signal != nullptr) {
1239 152 : return signal;
1240 : }
1241 152 : } while (it != begin);
1242 : return nullptr;
1243 : }
1244 :
1245 :
1246 : MSRailSignal*
1247 1570 : MSTriggeredRerouter::getRailSignal(const MSEdge* edge) {
1248 1570 : if (edge->getToJunction()->getType() == SumoXMLNodeType::RAIL_SIGNAL) {
1249 732 : for (const MSLink* link : edge->getLanes().front()->getLinkCont()) {
1250 732 : if (link->getTLLogic() != nullptr) {
1251 732 : return dynamic_cast<MSRailSignal*>(const_cast<MSTrafficLightLogic*>(link->getTLLogic()));
1252 : }
1253 : }
1254 : }
1255 : return nullptr;
1256 : }
1257 :
1258 : bool
1259 1202826 : MSTriggeredRerouter::applies(const SUMOTrafficObject& obj) const {
1260 1202826 : if (myVehicleTypes.empty() || myVehicleTypes.count(obj.getVehicleType().getOriginalID()) > 0) {
1261 1202742 : return true;
1262 : } else {
1263 168 : std::set<std::string> vTypeDists = MSNet::getInstance()->getVehicleControl().getVTypeDistributionMembership(obj.getVehicleType().getOriginalID());
1264 84 : for (auto vTypeDist : vTypeDists) {
1265 : if (myVehicleTypes.count(vTypeDist) > 0) {
1266 : return true;
1267 : }
1268 : }
1269 84 : return false;
1270 : }
1271 : }
1272 :
1273 :
1274 : bool
1275 130871 : MSTriggeredRerouter::affected(const std::set<SUMOTrafficObject::NumericalID>& edgeIndices, const MSEdgeVector& closed) {
1276 149017 : for (const MSEdge* const e : closed) {
1277 69896 : if (edgeIndices.count(e->getNumericalID()) > 0) {
1278 : return true;
1279 : }
1280 : }
1281 : return false;
1282 : }
1283 :
1284 :
1285 : void
1286 26481 : MSTriggeredRerouter::checkParkingRerouteConsistency() {
1287 : // if a parkingArea is a rerouting target, it should generally have a
1288 : // rerouter on its edge or vehicles will be stuck there once it's full.
1289 : // The user should receive a Warning in this case
1290 : std::set<MSEdge*> parkingRerouterEdges;
1291 : std::map<MSParkingArea*, std::string, ComparatorIdLess> targetedParkingArea; // paID -> targetingRerouter
1292 30657 : for (const auto& rr : myInstances) {
1293 : bool hasParkingReroute = false;
1294 8209 : for (const RerouteInterval& interval : rr.second->myIntervals) {
1295 4033 : if (interval.parkProbs.getOverallProb() > 0) {
1296 : hasParkingReroute = true;
1297 15870 : for (const StoppingPlaceVisible& pav : interval.parkProbs.getVals()) {
1298 27210 : targetedParkingArea[dynamic_cast<MSParkingArea*>(pav.first)] = rr.first;
1299 : }
1300 : }
1301 : }
1302 4176 : if (hasParkingReroute) {
1303 2265 : parkingRerouterEdges.insert(rr.second->myEdges.begin(), rr.second->myEdges.end());
1304 : }
1305 : }
1306 29400 : for (const auto& item : targetedParkingArea) {
1307 2919 : if (parkingRerouterEdges.count(&item.first->getLane().getEdge()) == 0) {
1308 1312 : WRITE_WARNINGF(TL("ParkingArea '%' is targeted by rerouter '%' but doesn't have its own rerouter. This may cause parking search to abort."),
1309 : item.first->getID(), item.second);
1310 : }
1311 : }
1312 26481 : }
1313 :
1314 :
1315 : void
1316 35399 : MSTriggeredRerouter::resetClosedEdges(bool hasReroutingDevice, const SUMOTrafficObject& o) {
1317 : // getRouterTT without prohibitions removes previous prohibitions
1318 35399 : if (o.isVehicle()) {
1319 : hasReroutingDevice
1320 70798 : ? MSRoutingEngine::getRouterTT(o.getRNGIndex(), o.getVClass())
1321 54877 : : MSNet::getInstance()->getRouterTT(o.getRNGIndex());
1322 : } else {
1323 : hasReroutingDevice
1324 0 : ? MSRoutingEngine::getIntermodalRouterTT(o.getRNGIndex())
1325 0 : : MSNet::getInstance()->getIntermodalRouter(o.getRNGIndex(), 0);
1326 : }
1327 35399 : }
1328 :
1329 : /****************************************************************************/
|