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/StaticCommand.h>
36 : #include <utils/common/StringUtils.h>
37 : #include <utils/xml/SUMOSAXAttributes.h>
38 : #include "MSRoutingEngine.h"
39 : #include "MSDevice_Routing.h"
40 :
41 :
42 : // ===========================================================================
43 : // method definitions
44 : // ===========================================================================
45 : // ---------------------------------------------------------------------------
46 : // static initialisation methods
47 : // ---------------------------------------------------------------------------
48 : void
49 36322 : MSDevice_Routing::insertOptions(OptionsCont& oc) {
50 72644 : insertDefaultAssignmentOptions("rerouting", "Routing", oc);
51 :
52 72644 : oc.doRegister("device.rerouting.period", new Option_String("0", "TIME"));
53 72644 : oc.addSynonyme("device.rerouting.period", "device.routing.period", true);
54 72644 : oc.addDescription("device.rerouting.period", "Routing", TL("The period with which the vehicle shall be rerouted"));
55 :
56 72644 : oc.doRegister("device.rerouting.pre-period", new Option_String("60", "TIME"));
57 72644 : oc.addSynonyme("device.rerouting.pre-period", "device.routing.pre-period", true);
58 72644 : oc.addDescription("device.rerouting.pre-period", "Routing", TL("The rerouting period before depart"));
59 :
60 36322 : oc.doRegister("device.rerouting.adaptation-weight", new Option_Float(0));
61 72644 : oc.addSynonyme("device.rerouting.adaptation-weight", "device.routing.adaptation-weight", true);
62 72644 : oc.addDescription("device.rerouting.adaptation-weight", "Routing", TL("The weight of prior edge weights for exponential moving average"));
63 :
64 36322 : oc.doRegister("device.rerouting.adaptation-steps", new Option_Integer(180));
65 72644 : oc.addSynonyme("device.rerouting.adaptation-steps", "device.routing.adaptation-steps", true);
66 72644 : oc.addDescription("device.rerouting.adaptation-steps", "Routing", TL("The number of steps for moving average weight of prior edge weights"));
67 :
68 72644 : oc.doRegister("device.rerouting.adaptation-interval", new Option_String("1", "TIME"));
69 72644 : oc.addSynonyme("device.rerouting.adaptation-interval", "device.routing.adaptation-interval", true);
70 72644 : oc.addDescription("device.rerouting.adaptation-interval", "Routing", TL("The interval for updating the edge weights"));
71 :
72 36322 : oc.doRegister("device.rerouting.with-taz", new Option_Bool(false));
73 72644 : oc.addSynonyme("device.rerouting.with-taz", "device.routing.with-taz", true);
74 72644 : oc.addSynonyme("device.rerouting.with-taz", "with-taz");
75 72644 : oc.addDescription("device.rerouting.with-taz", "Routing", TL("Use zones (districts) as routing start- and endpoints"));
76 :
77 72644 : oc.doRegister("device.rerouting.mode", new Option_String("0"));
78 72644 : oc.addDescription("device.rerouting.mode", "Routing", TL("Set routing flags (8 ignores temporary blockages)"));
79 :
80 36322 : oc.doRegister("device.rerouting.init-with-loaded-weights", new Option_Bool(false));
81 72644 : oc.addDescription("device.rerouting.init-with-loaded-weights", "Routing", TL("Use weight files given with option --weight-files for initializing edge weights"));
82 :
83 36322 : oc.doRegister("device.rerouting.threads", new Option_Integer(0));
84 72644 : oc.addSynonyme("device.rerouting.threads", "routing-threads");
85 72644 : oc.addDescription("device.rerouting.threads", "Routing", TL("The number of parallel execution threads used for rerouting"));
86 :
87 36322 : oc.doRegister("device.rerouting.synchronize", new Option_Bool(false));
88 72644 : oc.addDescription("device.rerouting.synchronize", "Routing", TL("Let rerouting happen at the same time for all vehicles"));
89 :
90 36322 : oc.doRegister("device.rerouting.railsignal", new Option_Bool(true));
91 72644 : oc.addDescription("device.rerouting.railsignal", "Routing", TL("Allow rerouting triggered by rail signals."));
92 :
93 36322 : oc.doRegister("device.rerouting.bike-speeds", new Option_Bool(false));
94 72644 : oc.addDescription("device.rerouting.bike-speeds", "Routing", TL("Compute separate average speeds for bicycles"));
95 :
96 72644 : oc.doRegister("device.rerouting.output", new Option_FileName());
97 72644 : oc.addDescription("device.rerouting.output", "Routing", TL("Save adapting weights to FILE"));
98 36322 : }
99 :
100 :
101 : bool
102 35752 : MSDevice_Routing::checkOptions(OptionsCont& oc) {
103 : bool ok = true;
104 71804 : if (!oc.isDefault("device.rerouting.adaptation-steps") && !oc.isDefault("device.rerouting.adaptation-weight")) {
105 0 : WRITE_ERROR(TL("Only one of the options 'device.rerouting.adaptation-steps' or 'device.rerouting.adaptation-weight' may be given."));
106 : ok = false;
107 : }
108 71504 : if (oc.getFloat("weights.random-factor") < 1) {
109 0 : WRITE_ERROR(TL("weights.random-factor cannot be less than 1"));
110 : ok = false;
111 : }
112 107256 : if (string2time(oc.getString("device.rerouting.adaptation-interval")) < 0) {
113 14 : WRITE_ERROR(TL("Negative value for device.rerouting.adaptation-interval!"));
114 : ok = false;
115 : }
116 107256 : if (oc.getFloat("device.rerouting.adaptation-weight") < 0. ||
117 107256 : oc.getFloat("device.rerouting.adaptation-weight") > 1.) {
118 0 : WRITE_ERROR(TL("The value for device.rerouting.adaptation-weight must be between 0 and 1!"));
119 : ok = false;
120 : }
121 : #ifndef HAVE_FOX
122 : if (oc.getInt("device.rerouting.threads") > 1) {
123 : WRITE_ERROR(TL("Parallel routing is only possible when compiled with Fox."));
124 : ok = false;
125 : }
126 : #endif
127 39607 : if (oc.getInt("threads") > 1 && oc.getInt("device.rerouting.threads") > 1 && oc.getInt("threads") != oc.getInt("device.rerouting.threads")) {
128 16 : WRITE_WARNING(TL("Adapting number of routing threads to number of simulation threads."));
129 : }
130 35752 : return ok;
131 : }
132 :
133 :
134 : void
135 4655793 : MSDevice_Routing::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
136 4655793 : const OptionsCont& oc = OptionsCont::getOptions();
137 4655793 : const bool equip = equippedByDefaultAssignmentOptions(oc, "rerouting", v, false);
138 4655787 : if (v.getParameter().wasSet(VEHPARS_FORCE_REROUTE) || equip) {
139 : // route computation is enabled
140 : // for implicitly equipped vehicles (trips, flows), option probability
141 : // can still be used to disable periodic rerouting after insertion for
142 : // parts of the fleet
143 2781180 : const SUMOTime period = equip || oc.isDefault("device.rerouting.probability") ? getTimeParam(v, oc, "rerouting.period", 0, false) : 0;
144 2781250 : const SUMOTime prePeriod = MAX2((SUMOTime)0, getTimeParam(v, oc, "rerouting.pre-period", string2time(oc.getString("device.rerouting.pre-period")), false));
145 1390625 : MSRoutingEngine::initWeightUpdate();
146 : // build the device
147 2143097 : into.push_back(new MSDevice_Routing(v, "routing_" + v.getID(), period, prePeriod));
148 : }
149 4655787 : }
150 :
151 :
152 : // ---------------------------------------------------------------------------
153 : // MSDevice_Routing-methods
154 : // ---------------------------------------------------------------------------
155 1390625 : MSDevice_Routing::MSDevice_Routing(SUMOVehicle& holder, const std::string& id,
156 1390625 : SUMOTime period, SUMOTime preInsertionPeriod) :
157 : MSVehicleDevice(holder, id),
158 1390625 : myPeriod(period),
159 1390625 : myPreInsertionPeriod(preInsertionPeriod),
160 1390625 : myLastRouting(-1),
161 1390625 : mySkipRouting(-1),
162 1390625 : myRerouteCommand(nullptr),
163 1390625 : myRerouteRailSignal(getBoolParam(holder, OptionsCont::getOptions(), "rerouting.railsignal", true, true)),
164 1390625 : myLastLaneEntryTime(-1),
165 1390625 : myRerouteAfterStop(false),
166 1390625 : myActive(true) {
167 1390625 : if (myPreInsertionPeriod > 0 || holder.getParameter().wasSet(VEHPARS_FORCE_REROUTE)) {
168 : // 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")
169 1389815 : myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::preInsertionReroute);
170 : // if we don't update the edge weights, we might as well reroute now and hopefully use our threads better
171 1389815 : const SUMOTime execTime = MSRoutingEngine::hasEdgeUpdates() ? holder.getParameter().depart : -1;
172 1389815 : MSNet::getInstance()->getInsertionEvents()->addEvent(myRerouteCommand, execTime);
173 : }
174 1390625 : }
175 :
176 :
177 2781234 : MSDevice_Routing::~MSDevice_Routing() {
178 : // make the rerouting command invalid if there is one
179 1390617 : if (myRerouteCommand != nullptr) {
180 : myRerouteCommand->deschedule();
181 : }
182 2781234 : }
183 :
184 :
185 : bool
186 1366122 : MSDevice_Routing::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification reason, const MSLane* enteredLane) {
187 1366122 : if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
188 1333987 : if (myRerouteCommand == nullptr && myPreInsertionPeriod > 0 && myHolder.getDepartDelay() > myPreInsertionPeriod) {
189 : // pre-insertion rerouting was disabled. Reroute once if insertion was delayed
190 : // this is happening in the run thread (not inbeginOfTimestepEvents) so we cannot safely use the threadPool
191 2060028 : myHolder.reroute(MSNet::getInstance()->getCurrentTimeStep(), "device.rerouting",
192 1373352 : MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass()),
193 : false, MSRoutingEngine::withTaz(), false);
194 : }
195 : // build repetition trigger if routing shall be done more often
196 1333987 : rebuildRerouteCommand();
197 : }
198 1366122 : if (MSGlobals::gWeightsSeparateTurns > 0) {
199 35980 : if (reason == MSMoveReminder::NOTIFICATION_JUNCTION) {
200 30980 : const SUMOTime t = SIMSTEP;
201 30980 : if (myLastLaneEntryTime >= 0 && enteredLane->isInternal()) {
202 : // record travel time on the previous edge but store on the internal ledge
203 10980 : MSRoutingEngine::addEdgeTravelTime(enteredLane->getEdge(), t - myLastLaneEntryTime);
204 : }
205 30980 : myLastLaneEntryTime = t;
206 : }
207 35980 : return true;
208 : } else {
209 : return false;
210 : }
211 : }
212 :
213 :
214 : void
215 0 : MSDevice_Routing::notifyStopEnded() {
216 0 : if (myRerouteAfterStop) {
217 0 : reroute(SIMSTEP);
218 0 : myRerouteAfterStop = false;
219 : }
220 0 : }
221 :
222 :
223 : void
224 1334015 : MSDevice_Routing::rebuildRerouteCommand() {
225 1334015 : if (myRerouteCommand != nullptr) {
226 : myRerouteCommand->deschedule();
227 350575 : myRerouteCommand = nullptr;
228 : }
229 1334015 : if (myPeriod > 0) {
230 33315 : myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::wrappedRerouteCommandExecute);
231 33315 : SUMOTime start = MSNet::getInstance()->getCurrentTimeStep();
232 66630 : if (OptionsCont::getOptions().getBool("device.rerouting.synchronize")) {
233 0 : start -= start % myPeriod;
234 : }
235 33315 : MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(myRerouteCommand, myPeriod + start);
236 : }
237 1334015 : }
238 :
239 :
240 : SUMOTime
241 15132548 : MSDevice_Routing::preInsertionReroute(const SUMOTime currentTime) {
242 15132548 : if (mySkipRouting == currentTime) {
243 13147639 : return DELTA_T;
244 : }
245 1984909 : if (myPreInsertionPeriod == 0) {
246 : // the event will deschedule and destroy itself so it does not need to be stored
247 496 : myRerouteCommand = nullptr;
248 : }
249 1984909 : const MSEdge* source = *myHolder.getRoute().begin();
250 1984909 : const MSEdge* dest = myHolder.getRoute().getLastEdge();
251 1984909 : if (source->isTazConnector() && dest->isTazConnector()) {
252 675361 : ConstMSRoutePtr cached = MSRoutingEngine::getCachedRoute(std::make_pair(source, dest));
253 675361 : if (cached != nullptr && cached->size() > 2) {
254 0 : myHolder.replaceRoute(cached, "device.rerouting", true);
255 0 : return myPreInsertionPeriod;
256 : }
257 : }
258 : try {
259 : std::string msg;
260 1984909 : if (myHolder.hasValidRouteStart(msg)) {
261 1984888 : reroute(currentTime, true);
262 : }
263 96 : } catch (ProcessError&) {
264 96 : myRerouteCommand = nullptr;
265 96 : throw;
266 96 : }
267 : // avoid repeated pre-insertion rerouting when the departure edge is fix and
268 : // the departure lane does not depend on the route
269 1984813 : if (myPreInsertionPeriod > 0 && !source->isTazConnector() && myHolder.getParameter().departLaneProcedure != DepartLaneDefinition::BEST_FREE) {
270 1010485 : myRerouteCommand = nullptr;
271 1010485 : return 0;
272 : }
273 974328 : return myPreInsertionPeriod;
274 : }
275 :
276 :
277 : SUMOTime
278 124996 : MSDevice_Routing::wrappedRerouteCommandExecute(SUMOTime currentTime) {
279 124996 : if (myHolder.isStopped()) {
280 8512 : myRerouteAfterStop = true;
281 : } else {
282 116484 : reroute(currentTime);
283 : }
284 124996 : return myPeriod;
285 : }
286 :
287 :
288 : void
289 2101372 : MSDevice_Routing::reroute(const SUMOTime currentTime, const bool onInit) {
290 2101372 : MSRoutingEngine::initEdgeWeights(myHolder.getVClass());
291 : //check whether the weights did change since the last reroute
292 2101372 : if (myLastRouting >= MSRoutingEngine::getLastAdaptation() || !myActive) {
293 : return;
294 : }
295 2099670 : myLastRouting = currentTime;
296 4199340 : MSRoutingEngine::reroute(myHolder, currentTime, "device.rerouting", onInit);
297 : }
298 :
299 :
300 : std::string
301 40 : MSDevice_Routing::getParameter(const std::string& key) const {
302 80 : if (StringUtils::startsWith(key, "edge:")) {
303 20 : const std::string edgeID = key.substr(5);
304 20 : const MSEdge* edge = MSEdge::dictionary(edgeID);
305 20 : if (edge == nullptr) {
306 0 : throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter retrieval of '" + deviceName() + "'");
307 : }
308 20 : return toString(MSRoutingEngine::getEffort(edge, &myHolder, 0));
309 20 : } else if (key == "period") {
310 20 : return time2string(myPeriod);
311 : }
312 0 : throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
313 : }
314 :
315 :
316 : void
317 38 : MSDevice_Routing::setParameter(const std::string& key, const std::string& value) {
318 : double doubleValue;
319 : try {
320 38 : doubleValue = StringUtils::toDouble(value);
321 0 : } catch (NumberFormatException&) {
322 0 : throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
323 0 : }
324 76 : if (StringUtils::startsWith(key, "edge:")) {
325 10 : const std::string edgeID = key.substr(5);
326 10 : const MSEdge* edge = MSEdge::dictionary(edgeID);
327 10 : if (edge == nullptr) {
328 0 : throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter setting of '" + deviceName() + "'");
329 : }
330 10 : MSRoutingEngine::setEdgeTravelTime(edge, doubleValue);
331 28 : } else if (key == "period") {
332 28 : myPeriod = TIME2STEPS(doubleValue);
333 : // re-schedule routing command
334 28 : rebuildRerouteCommand();
335 : } else {
336 0 : throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
337 : }
338 38 : }
339 :
340 :
341 : void
342 1462 : MSDevice_Routing::saveState(OutputDevice& out) const {
343 1462 : out.openTag(SUMO_TAG_DEVICE);
344 : out.writeAttr(SUMO_ATTR_ID, getID());
345 : std::vector<std::string> internals;
346 1462 : internals.push_back(toString(myPeriod));
347 1462 : out.writeAttr(SUMO_ATTR_STATE, toString(internals));
348 1462 : out.closeTag();
349 1462 : }
350 :
351 :
352 : void
353 3904 : MSDevice_Routing::loadState(const SUMOSAXAttributes& attrs) {
354 3904 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
355 3904 : bis >> myPeriod;
356 3904 : }
357 :
358 :
359 : /****************************************************************************/
|