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