Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2007-2024 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSDevice_Routing.cpp
15 : /// @author Michael Behrisch
16 : /// @author Daniel Krajzewicz
17 : /// @author Laura Bieker
18 : /// @author Christoph Sommer
19 : /// @author Jakob Erdmann
20 : /// @date Tue, 04 Dec 2007
21 : ///
22 : // A device that performs vehicle rerouting based on current edge speeds
23 : /****************************************************************************/
24 : #include <config.h>
25 :
26 : #include <microsim/MSNet.h>
27 : #include <microsim/MSLane.h>
28 : #include <microsim/MSEdge.h>
29 : #include <microsim/MSEdgeControl.h>
30 : #include <microsim/MSEventControl.h>
31 : #include <microsim/MSGlobals.h>
32 : #include <microsim/MSVehicleControl.h>
33 : #include <utils/options/OptionsCont.h>
34 : #include <utils/common/WrappingCommand.h>
35 : #include <utils/common/StringUtils.h>
36 : #include <utils/xml/SUMOSAXAttributes.h>
37 : #include "MSRoutingEngine.h"
38 : #include "MSDevice_Routing.h"
39 :
40 :
41 : // ===========================================================================
42 : // method definitions
43 : // ===========================================================================
44 : // ---------------------------------------------------------------------------
45 : // static initialisation methods
46 : // ---------------------------------------------------------------------------
47 : void
48 43644 : MSDevice_Routing::insertOptions(OptionsCont& oc) {
49 87288 : insertDefaultAssignmentOptions("rerouting", "Routing", oc);
50 :
51 87288 : oc.doRegister("device.rerouting.period", new Option_String("0", "TIME"));
52 87288 : oc.addSynonyme("device.rerouting.period", "device.routing.period", true);
53 87288 : oc.addDescription("device.rerouting.period", "Routing", TL("The period with which the vehicle shall be rerouted"));
54 :
55 87288 : oc.doRegister("device.rerouting.pre-period", new Option_String("60", "TIME"));
56 87288 : oc.addSynonyme("device.rerouting.pre-period", "device.routing.pre-period", true);
57 87288 : oc.addDescription("device.rerouting.pre-period", "Routing", TL("The rerouting period before depart"));
58 :
59 43644 : oc.doRegister("device.rerouting.adaptation-weight", new Option_Float(0));
60 87288 : oc.addSynonyme("device.rerouting.adaptation-weight", "device.routing.adaptation-weight", true);
61 87288 : oc.addDescription("device.rerouting.adaptation-weight", "Routing", TL("The weight of prior edge weights for exponential moving average"));
62 :
63 43644 : oc.doRegister("device.rerouting.adaptation-steps", new Option_Integer(180));
64 87288 : oc.addSynonyme("device.rerouting.adaptation-steps", "device.routing.adaptation-steps", true);
65 87288 : oc.addDescription("device.rerouting.adaptation-steps", "Routing", TL("The number of steps for moving average weight of prior edge weights"));
66 :
67 87288 : oc.doRegister("device.rerouting.adaptation-interval", new Option_String("1", "TIME"));
68 87288 : oc.addSynonyme("device.rerouting.adaptation-interval", "device.routing.adaptation-interval", true);
69 87288 : oc.addDescription("device.rerouting.adaptation-interval", "Routing", TL("The interval for updating the edge weights"));
70 :
71 43644 : oc.doRegister("device.rerouting.with-taz", new Option_Bool(false));
72 87288 : oc.addSynonyme("device.rerouting.with-taz", "device.routing.with-taz", true);
73 87288 : oc.addSynonyme("device.rerouting.with-taz", "with-taz");
74 87288 : oc.addDescription("device.rerouting.with-taz", "Routing", TL("Use zones (districts) as routing start- and endpoints"));
75 :
76 87288 : oc.doRegister("device.rerouting.mode", new Option_String("0"));
77 87288 : oc.addDescription("device.rerouting.mode", "Routing", TL("Set routing flags (8 ignores temporary blockages)"));
78 :
79 43644 : oc.doRegister("device.rerouting.init-with-loaded-weights", new Option_Bool(false));
80 87288 : oc.addDescription("device.rerouting.init-with-loaded-weights", "Routing", TL("Use weight files given with option --weight-files for initializing edge weights"));
81 :
82 43644 : oc.doRegister("device.rerouting.threads", new Option_Integer(0));
83 87288 : oc.addSynonyme("device.rerouting.threads", "routing-threads");
84 87288 : oc.addDescription("device.rerouting.threads", "Routing", TL("The number of parallel execution threads used for rerouting"));
85 :
86 43644 : oc.doRegister("device.rerouting.synchronize", new Option_Bool(false));
87 87288 : oc.addDescription("device.rerouting.synchronize", "Routing", TL("Let rerouting happen at the same time for all vehicles"));
88 :
89 43644 : oc.doRegister("device.rerouting.railsignal", new Option_Bool(false));
90 87288 : oc.addDescription("device.rerouting.railsignal", "Routing", TL("Allow rerouting triggered by rail signals."));
91 :
92 43644 : oc.doRegister("device.rerouting.bike-speeds", new Option_Bool(false));
93 87288 : oc.addDescription("device.rerouting.bike-speeds", "Routing", TL("Compute separate average speeds for bicycles"));
94 :
95 43644 : oc.doRegister("device.rerouting.output", new Option_FileName());
96 87288 : oc.addDescription("device.rerouting.output", "Routing", TL("Save adapting weights to FILE"));
97 43644 : }
98 :
99 :
100 : bool
101 43320 : MSDevice_Routing::checkOptions(OptionsCont& oc) {
102 : bool ok = true;
103 43604 : if (!oc.isDefault("device.rerouting.adaptation-steps") && !oc.isDefault("device.rerouting.adaptation-weight")) {
104 0 : WRITE_ERROR(TL("Only one of the options 'device.rerouting.adaptation-steps' or 'device.rerouting.adaptation-weight' may be given."));
105 : ok = false;
106 : }
107 86640 : if (oc.getFloat("weights.random-factor") < 1) {
108 0 : WRITE_ERROR(TL("weights.random-factor cannot be less than 1"));
109 : ok = false;
110 : }
111 86640 : if (string2time(oc.getString("device.rerouting.adaptation-interval")) < 0) {
112 14 : WRITE_ERROR(TL("Negative value for device.rerouting.adaptation-interval!"));
113 : ok = false;
114 : }
115 129960 : if (oc.getFloat("device.rerouting.adaptation-weight") < 0. ||
116 129960 : oc.getFloat("device.rerouting.adaptation-weight") > 1.) {
117 0 : WRITE_ERROR(TL("The value for device.rerouting.adaptation-weight must be between 0 and 1!"));
118 : ok = false;
119 : }
120 : #ifndef HAVE_FOX
121 : if (oc.getInt("device.rerouting.threads") > 1) {
122 : WRITE_ERROR(TL("Parallel routing is only possible when compiled with Fox."));
123 : ok = false;
124 : }
125 : #endif
126 47389 : if (oc.getInt("threads") > 1 && oc.getInt("device.rerouting.threads") > 1 && oc.getInt("threads") != oc.getInt("device.rerouting.threads")) {
127 16 : WRITE_WARNING(TL("Adapting number of routing threads to number of simulation threads."));
128 : }
129 43320 : return ok;
130 : }
131 :
132 :
133 : void
134 5104388 : MSDevice_Routing::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
135 5104388 : const OptionsCont& oc = OptionsCont::getOptions();
136 5104388 : const bool equip = equippedByDefaultAssignmentOptions(oc, "rerouting", v, false);
137 5104382 : if (v.getParameter().wasSet(VEHPARS_FORCE_REROUTE) || equip) {
138 : // route computation is enabled
139 : // for implicitly equipped vehicles (trips, flows), option probability
140 : // can still be used to disable periodic rerouting after insertion for
141 : // parts of the fleet
142 1347988 : const SUMOTime period = (equip || (
143 2695258 : oc.isDefault("device.rerouting.probability") &&
144 4209193 : v.getFloatParam("device.rerouting.probability") == oc.getFloat("device.rerouting.probability"))
145 4376506 : ? v.getTimeParam("device.rerouting.period") : 0);
146 1514653 : const SUMOTime prePeriod = MAX2((SUMOTime)0, v.getTimeParam("device.rerouting.pre-period"));
147 1514653 : MSRoutingEngine::initWeightUpdate();
148 : // build the device
149 3029306 : into.push_back(new MSDevice_Routing(v, "routing_" + v.getID(), period, prePeriod));
150 : }
151 5104382 : }
152 :
153 :
154 : // ---------------------------------------------------------------------------
155 : // MSDevice_Routing-methods
156 : // ---------------------------------------------------------------------------
157 1514653 : MSDevice_Routing::MSDevice_Routing(SUMOVehicle& holder, const std::string& id,
158 1514653 : SUMOTime period, SUMOTime preInsertionPeriod) :
159 : MSVehicleDevice(holder, id),
160 1514653 : myPeriod(period),
161 1514653 : myPreInsertionPeriod(preInsertionPeriod),
162 1514653 : myLastRouting(-1),
163 1514653 : mySkipRouting(-1),
164 1514653 : myRerouteCommand(nullptr),
165 1514653 : myRerouteRailSignal(holder.getBoolParam("device.rerouting.railsignal", true)),
166 1514653 : myLastLaneEntryTime(-1),
167 1514653 : myRerouteAfterStop(false),
168 1514653 : myActive(true) {
169 1514653 : if (myPreInsertionPeriod > 0 || holder.getParameter().wasSet(VEHPARS_FORCE_REROUTE)) {
170 : // we do always a pre insertion reroute for trips to fill the best lanes of the vehicle with somehow meaningful values (especially for deaprtLane="best")
171 1513892 : myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::preInsertionReroute);
172 : // if we don't update the edge weights, we might as well reroute now and hopefully use our threads better
173 1513892 : const SUMOTime execTime = MSRoutingEngine::hasEdgeUpdates() ? holder.getParameter().depart : -1;
174 1513892 : MSNet::getInstance()->getInsertionEvents()->addEvent(myRerouteCommand, execTime);
175 : }
176 1514653 : }
177 :
178 :
179 3029284 : MSDevice_Routing::~MSDevice_Routing() {
180 : // make the rerouting command invalid if there is one
181 1514642 : if (myRerouteCommand != nullptr) {
182 : myRerouteCommand->deschedule();
183 : }
184 3029284 : }
185 :
186 :
187 : bool
188 966204 : MSDevice_Routing::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification reason, const MSLane* enteredLane) {
189 966204 : if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
190 933755 : if (myRerouteCommand == nullptr && myPreInsertionPeriod > 0 && myHolder.getDepartDelay() > myPreInsertionPeriod) {
191 : // pre-insertion rerouting was disabled. Reroute once if insertion was delayed
192 : // this is happening in the run thread (not inbeginOfTimestepEvents) so we cannot safely use the threadPool
193 865984 : myHolder.reroute(MSNet::getInstance()->getCurrentTimeStep(), "device.rerouting",
194 865984 : MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass()),
195 : false, MSRoutingEngine::withTaz(), false);
196 : }
197 : // build repetition trigger if routing shall be done more often
198 933755 : rebuildRerouteCommand();
199 : }
200 966204 : if (MSGlobals::gWeightsSeparateTurns > 0) {
201 36105 : if (reason == MSMoveReminder::NOTIFICATION_JUNCTION) {
202 31105 : const SUMOTime t = SIMSTEP;
203 31105 : if (myLastLaneEntryTime >= 0 && enteredLane->isInternal()) {
204 : // record travel time on the previous edge but store on the internal ledge
205 11090 : MSRoutingEngine::addEdgeTravelTime(enteredLane->getEdge(), t - myLastLaneEntryTime);
206 : }
207 31105 : myLastLaneEntryTime = t;
208 : }
209 36105 : return true;
210 : } else {
211 : return false;
212 : }
213 : }
214 :
215 :
216 : void
217 0 : MSDevice_Routing::notifyStopEnded() {
218 0 : if (myRerouteAfterStop) {
219 0 : reroute(SIMSTEP);
220 0 : myRerouteAfterStop = false;
221 : }
222 0 : }
223 :
224 :
225 : void
226 935134 : MSDevice_Routing::rebuildRerouteCommand() {
227 935134 : if (myRerouteCommand != nullptr) {
228 : myRerouteCommand->deschedule();
229 216384 : myRerouteCommand = nullptr;
230 : }
231 935134 : if (myPeriod > 0) {
232 41243 : myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::wrappedRerouteCommandExecute);
233 41243 : SUMOTime start = MSNet::getInstance()->getCurrentTimeStep();
234 82486 : if (OptionsCont::getOptions().getBool("device.rerouting.synchronize")) {
235 302 : start -= start % myPeriod;
236 : }
237 41243 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(myRerouteCommand, myPeriod + start);
238 : }
239 935134 : }
240 :
241 :
242 : SUMOTime
243 15151977 : MSDevice_Routing::preInsertionReroute(const SUMOTime currentTime) {
244 15151977 : if (mySkipRouting == currentTime) {
245 13063587 : return DELTA_T;
246 : }
247 2088390 : if (myPreInsertionPeriod == 0) {
248 : // the event will deschedule and destroy itself so it does not need to be stored
249 496 : myRerouteCommand = nullptr;
250 : }
251 2088390 : const MSEdge* source = *myHolder.getRoute().begin();
252 2088390 : const MSEdge* dest = myHolder.getRoute().getLastEdge();
253 2088390 : if (source->isTazConnector() && dest->isTazConnector()) {
254 566045 : ConstMSRoutePtr cached = MSRoutingEngine::getCachedRoute(std::make_pair(source, dest));
255 566045 : if (cached != nullptr && cached->size() > 2) {
256 0 : myHolder.replaceRoute(cached, "device.rerouting", true);
257 0 : return myPreInsertionPeriod;
258 : }
259 : }
260 : try {
261 : std::string msg;
262 2088390 : if (myHolder.hasValidRouteStart(msg)) {
263 2088369 : reroute(currentTime, true);
264 : }
265 97 : } catch (ProcessError&) {
266 97 : myRerouteCommand = nullptr;
267 97 : throw;
268 97 : }
269 : // avoid repeated pre-insertion rerouting when the departure edge is fix and
270 : // the departure lane does not depend on the route
271 2088293 : if (myPreInsertionPeriod > 0 && !source->isTazConnector() && myHolder.getParameter().departLaneProcedure != DepartLaneDefinition::BEST_FREE) {
272 1035871 : myRerouteCommand = nullptr;
273 1035871 : return 0;
274 : }
275 1052422 : return myPreInsertionPeriod;
276 : }
277 :
278 :
279 : SUMOTime
280 127984 : MSDevice_Routing::wrappedRerouteCommandExecute(SUMOTime currentTime) {
281 127984 : if (myHolder.isStopped()) {
282 14090 : myRerouteAfterStop = true;
283 : } else {
284 113894 : reroute(currentTime);
285 : }
286 127984 : return myPeriod;
287 : }
288 :
289 :
290 : void
291 2202263 : MSDevice_Routing::reroute(const SUMOTime currentTime, const bool onInit) {
292 2202263 : MSRoutingEngine::initEdgeWeights(myHolder.getVClass());
293 : //check whether the weights did change since the last reroute
294 2202263 : if (myLastRouting >= MSRoutingEngine::getLastAdaptation() || !myActive) {
295 : return;
296 : }
297 2200633 : myLastRouting = currentTime;
298 4401266 : MSRoutingEngine::reroute(myHolder, currentTime, "device.rerouting", onInit);
299 : }
300 :
301 :
302 : std::string
303 36 : MSDevice_Routing::getParameter(const std::string& key) const {
304 72 : if (StringUtils::startsWith(key, "edge:")) {
305 18 : const std::string edgeID = key.substr(5);
306 18 : const MSEdge* edge = MSEdge::dictionary(edgeID);
307 18 : if (edge == nullptr) {
308 0 : throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter retrieval of '" + deviceName() + "'");
309 : }
310 18 : return toString(MSRoutingEngine::getEffort(edge, &myHolder, 0));
311 18 : } else if (key == "period") {
312 18 : return time2string(myPeriod);
313 : }
314 0 : throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
315 : }
316 :
317 :
318 : void
319 33 : MSDevice_Routing::setParameter(const std::string& key, const std::string& value) {
320 : double doubleValue;
321 : try {
322 33 : doubleValue = StringUtils::toDouble(value);
323 0 : } catch (NumberFormatException&) {
324 0 : throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
325 0 : }
326 66 : if (StringUtils::startsWith(key, "edge:")) {
327 9 : const std::string edgeID = key.substr(5);
328 9 : const MSEdge* edge = MSEdge::dictionary(edgeID);
329 9 : if (edge == nullptr) {
330 0 : throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter setting of '" + deviceName() + "'");
331 : }
332 9 : MSRoutingEngine::setEdgeTravelTime(edge, doubleValue);
333 24 : } else if (key == "period") {
334 24 : myPeriod = TIME2STEPS(doubleValue);
335 : // re-schedule routing command
336 24 : rebuildRerouteCommand();
337 : } else {
338 0 : throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
339 : }
340 33 : }
341 :
342 :
343 : void
344 1936 : MSDevice_Routing::saveState(OutputDevice& out) const {
345 1936 : out.openTag(SUMO_TAG_DEVICE);
346 : out.writeAttr(SUMO_ATTR_ID, getID());
347 : std::vector<std::string> internals;
348 1936 : internals.push_back(toString(myPeriod));
349 1936 : out.writeAttr(SUMO_ATTR_STATE, toString(internals));
350 1936 : out.closeTag();
351 1936 : }
352 :
353 :
354 : void
355 3807 : MSDevice_Routing::loadState(const SUMOSAXAttributes& attrs) {
356 3807 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
357 3807 : bis >> myPeriod;
358 3807 : if (myHolder.hasDeparted()) {
359 1355 : rebuildRerouteCommand();
360 : }
361 3807 : }
362 :
363 :
364 : /****************************************************************************/
|