1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2007-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file MSDevice_Routing.cpp
11 /// @author Michael Behrisch
12 /// @author Daniel Krajzewicz
13 /// @author Laura Bieker
14 /// @author Christoph Sommer
15 /// @author Jakob Erdmann
16 /// @date Tue, 04 Dec 2007
17 /// @version $Id$
18 ///
19 // A device that performs vehicle rerouting based on current edge speeds
20 /****************************************************************************/
21
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26
27 #include <microsim/MSNet.h>
28 #include <microsim/MSLane.h>
29 #include <microsim/MSEdge.h>
30 #include <microsim/MSEdgeControl.h>
31 #include <microsim/MSEventControl.h>
32 #include <microsim/MSGlobals.h>
33 #include <microsim/MSVehicleControl.h>
34 #include <utils/options/OptionsCont.h>
35 #include <utils/common/WrappingCommand.h>
36 #include <utils/common/StaticCommand.h>
37 #include <utils/common/StringUtils.h>
38 #include <utils/xml/SUMOSAXAttributes.h>
39 #include "MSRoutingEngine.h"
40 #include "MSDevice_Routing.h"
41
42
43 // ===========================================================================
44 // method definitions
45 // ===========================================================================
46 // ---------------------------------------------------------------------------
47 // static initialisation methods
48 // ---------------------------------------------------------------------------
49 void
insertOptions(OptionsCont & oc)50 MSDevice_Routing::insertOptions(OptionsCont& oc) {
51 insertDefaultAssignmentOptions("rerouting", "Routing", oc);
52
53 oc.doRegister("device.rerouting.period", new Option_String("0", "TIME"));
54 oc.addSynonyme("device.rerouting.period", "device.routing.period", true);
55 oc.addDescription("device.rerouting.period", "Routing", "The period with which the vehicle shall be rerouted");
56
57 oc.doRegister("device.rerouting.pre-period", new Option_String("60", "TIME"));
58 oc.addSynonyme("device.rerouting.pre-period", "device.routing.pre-period", true);
59 oc.addDescription("device.rerouting.pre-period", "Routing", "The rerouting period before depart");
60
61 oc.doRegister("device.rerouting.adaptation-weight", new Option_Float(0));
62 oc.addSynonyme("device.rerouting.adaptation-weight", "device.routing.adaptation-weight", true);
63 oc.addDescription("device.rerouting.adaptation-weight", "Routing", "The weight of prior edge weights for exponential moving average");
64
65 oc.doRegister("device.rerouting.adaptation-steps", new Option_Integer(180));
66 oc.addSynonyme("device.rerouting.adaptation-steps", "device.routing.adaptation-steps", true);
67 oc.addDescription("device.rerouting.adaptation-steps", "Routing", "The number of steps for moving average weight of prior edge weights");
68
69 oc.doRegister("device.rerouting.adaptation-interval", new Option_String("1", "TIME"));
70 oc.addSynonyme("device.rerouting.adaptation-interval", "device.routing.adaptation-interval", true);
71 oc.addDescription("device.rerouting.adaptation-interval", "Routing", "The interval for updating the edge weights");
72
73 oc.doRegister("device.rerouting.with-taz", new Option_Bool(false));
74 oc.addSynonyme("device.rerouting.with-taz", "device.routing.with-taz", true);
75 oc.addSynonyme("device.rerouting.with-taz", "with-taz");
76 oc.addDescription("device.rerouting.with-taz", "Routing", "Use zones (districts) as routing start- and endpoints");
77
78 oc.doRegister("device.rerouting.init-with-loaded-weights", new Option_Bool(false));
79 oc.addDescription("device.rerouting.init-with-loaded-weights", "Routing", "Use weight files given with option --weight-files for initializing edge weights");
80
81 oc.doRegister("device.rerouting.threads", new Option_Integer(0));
82 oc.addDescription("device.rerouting.threads", "Routing", "The number of parallel execution threads used for rerouting");
83
84 oc.doRegister("device.rerouting.synchronize", new Option_Bool(false));
85 oc.addDescription("device.rerouting.synchronize", "Routing", "Let rerouting happen at the same time for all vehicles");
86
87 oc.doRegister("device.rerouting.output", new Option_FileName());
88 oc.addDescription("device.rerouting.output", "Routing", "Save adapting weights to FILE");
89 }
90
91
92 bool
checkOptions(OptionsCont & oc)93 MSDevice_Routing::checkOptions(OptionsCont& oc) {
94 bool ok = true;
95 if (!oc.isDefault("device.rerouting.adaptation-steps") && !oc.isDefault("device.rerouting.adaptation-weight")) {
96 WRITE_ERROR("Only one of the options 'device.rerouting.adaptation-steps' or 'device.rerouting.adaptation-weight' may be given.");
97 ok = false;
98 }
99 if (oc.getFloat("weights.random-factor") < 1) {
100 WRITE_ERROR("weights.random-factor cannot be less than 1");
101 ok = false;
102 }
103 if (string2time(oc.getString("device.rerouting.adaptation-interval")) < 0) {
104 WRITE_ERROR("Negative value for device.rerouting.adaptation-interval!");
105 ok = false;
106 }
107 if (oc.getFloat("device.rerouting.adaptation-weight") < 0. ||
108 oc.getFloat("device.rerouting.adaptation-weight") > 1.) {
109 WRITE_ERROR("The value for device.rerouting.adaptation-weight must be between 0 and 1!");
110 ok = false;
111 }
112 #ifndef HAVE_FOX
113 if (oc.getInt("device.rerouting.threads") > 1) {
114 WRITE_ERROR("Parallel routing is only possible when compiled with Fox.");
115 ok = false;
116 }
117 #endif
118 return ok;
119 }
120
121
122 void
buildVehicleDevices(SUMOVehicle & v,std::vector<MSVehicleDevice * > & into)123 MSDevice_Routing::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
124 const OptionsCont& oc = OptionsCont::getOptions();
125 if (v.getParameter().wasSet(VEHPARS_FORCE_REROUTE) || equippedByDefaultAssignmentOptions(oc, "rerouting", v, false)) {
126 // route computation is enabled
127 const SUMOTime period = string2time(oc.getString("device.rerouting.period"));
128 const SUMOTime prePeriod = string2time(oc.getString("device.rerouting.pre-period"));
129 MSRoutingEngine::initWeightUpdate();
130 // build the device
131 into.push_back(new MSDevice_Routing(v, "routing_" + v.getID(), period, prePeriod));
132 }
133 }
134
135
136 // ---------------------------------------------------------------------------
137 // MSDevice_Routing-methods
138 // ---------------------------------------------------------------------------
MSDevice_Routing(SUMOVehicle & holder,const std::string & id,SUMOTime period,SUMOTime preInsertionPeriod)139 MSDevice_Routing::MSDevice_Routing(SUMOVehicle& holder, const std::string& id,
140 SUMOTime period, SUMOTime preInsertionPeriod)
141 : MSVehicleDevice(holder, id), myPeriod(period), myPreInsertionPeriod(preInsertionPeriod), myLastRouting(-1), mySkipRouting(-1), myRerouteCommand(nullptr) {
142 if (myPreInsertionPeriod > 0 || holder.getParameter().wasSet(VEHPARS_FORCE_REROUTE)) {
143 // 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")
144 myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::preInsertionReroute);
145 // if we don't update the edge weights, we might as well reroute now and hopefully use our threads better
146 const SUMOTime execTime = MSRoutingEngine::hasEdgeUpdates() ? holder.getParameter().depart : -1;
147 MSNet::getInstance()->getInsertionEvents()->addEvent(myRerouteCommand, execTime);
148 if (myPreInsertionPeriod == 0) {
149 // the event will deschedule and destroy itself so it does not need to be stored
150 myRerouteCommand = nullptr;
151 }
152 }
153 }
154
155
~MSDevice_Routing()156 MSDevice_Routing::~MSDevice_Routing() {
157 // make the rerouting command invalid if there is one
158 if (myRerouteCommand != nullptr) {
159 myRerouteCommand->deschedule();
160 }
161 }
162
163
164 bool
notifyEnter(SUMOTrafficObject &,MSMoveReminder::Notification reason,const MSLane *)165 MSDevice_Routing::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
166 if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
167 // clean up pre depart rerouting
168 if (myRerouteCommand != nullptr) {
169 myRerouteCommand->deschedule();
170 }
171 myRerouteCommand = nullptr;
172 // build repetition trigger if routing shall be done more often
173 if (myPeriod > 0) {
174 myRerouteCommand = new WrappingCommand<MSDevice_Routing>(this, &MSDevice_Routing::wrappedRerouteCommandExecute);
175 SUMOTime start = MSNet::getInstance()->getCurrentTimeStep();
176 if (OptionsCont::getOptions().getBool("device.rerouting.synchronize")) {
177 start -= start % myPeriod;
178 }
179 MSNet::getInstance()->getBeginOfTimestepEvents()->addEvent(myRerouteCommand, myPeriod + start);
180 }
181 }
182 return false;
183 }
184
185
186 SUMOTime
preInsertionReroute(const SUMOTime currentTime)187 MSDevice_Routing::preInsertionReroute(const SUMOTime currentTime) {
188 if (mySkipRouting == currentTime) {
189 return DELTA_T;
190 }
191 const MSEdge* source = *myHolder.getRoute().begin();
192 const MSEdge* dest = myHolder.getRoute().getLastEdge();
193 if (source->isTazConnector() && dest->isTazConnector()) {
194 const MSRoute* cached = MSRoutingEngine::getCachedRoute(std::make_pair(source, dest));
195 if (cached != nullptr && cached->size() > 2) {
196 myHolder.replaceRoute(cached, "device.rerouting", true);
197 return myPreInsertionPeriod;
198 }
199 }
200 try {
201 reroute(currentTime, true);
202 } catch (ProcessError&) {
203 myRerouteCommand = nullptr;
204 throw;
205 }
206 return myPreInsertionPeriod;
207 }
208
209
210 SUMOTime
wrappedRerouteCommandExecute(SUMOTime currentTime)211 MSDevice_Routing::wrappedRerouteCommandExecute(SUMOTime currentTime) {
212 reroute(currentTime);
213 return myPeriod;
214 }
215
216
217 void
reroute(const SUMOTime currentTime,const bool onInit)218 MSDevice_Routing::reroute(const SUMOTime currentTime, const bool onInit) {
219 MSRoutingEngine::initEdgeWeights();
220 //check whether the weights did change since the last reroute
221 if (myLastRouting >= MSRoutingEngine::getLastAdaptation()) {
222 return;
223 }
224 myLastRouting = currentTime;
225 MSRoutingEngine::reroute(myHolder, currentTime, onInit);
226 }
227
228
229 std::string
getParameter(const std::string & key) const230 MSDevice_Routing::getParameter(const std::string& key) const {
231 if (StringUtils::startsWith(key, "edge:")) {
232 const std::string edgeID = key.substr(5);
233 const MSEdge* edge = MSEdge::dictionary(edgeID);
234 if (edge == nullptr) {
235 throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter retrieval of '" + deviceName() + "'");
236 }
237 return toString(MSRoutingEngine::getEffort(edge, &myHolder, 0));
238 } else if (key == "period") {
239 return time2string(myPeriod);
240 }
241 throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
242 }
243
244
245 void
setParameter(const std::string & key,const std::string & value)246 MSDevice_Routing::setParameter(const std::string& key, const std::string& value) {
247 double doubleValue;
248 try {
249 doubleValue = StringUtils::toDouble(value);
250 } catch (NumberFormatException&) {
251 throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
252 }
253 if (StringUtils::startsWith(key, "edge:")) {
254 const std::string edgeID = key.substr(5);
255 const MSEdge* edge = MSEdge::dictionary(edgeID);
256 if (edge == nullptr) {
257 throw InvalidArgument("Edge '" + edgeID + "' is invalid for parameter setting of '" + deviceName() + "'");
258 }
259 MSRoutingEngine::setEdgeTravelTime(edge, doubleValue);
260 } else if (key == "period") {
261 const SUMOTime oldPeriod = myPeriod;
262 myPeriod = TIME2STEPS(doubleValue);
263 if (myPeriod <= 0) {
264 myRerouteCommand->deschedule();
265 } else if (oldPeriod <= 0) {
266 // re-schedule routing command
267 notifyEnter(myHolder, MSMoveReminder::NOTIFICATION_DEPARTED, nullptr);
268 }
269 } else {
270 throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
271 }
272 }
273
274
275 void
saveState(OutputDevice & out) const276 MSDevice_Routing::saveState(OutputDevice& out) const {
277 out.openTag(SUMO_TAG_DEVICE);
278 out.writeAttr(SUMO_ATTR_ID, getID());
279 std::vector<std::string> internals;
280 internals.push_back(toString(myPeriod));
281 out.writeAttr(SUMO_ATTR_STATE, toString(internals));
282 out.closeTag();
283 }
284
285
286 void
loadState(const SUMOSAXAttributes & attrs)287 MSDevice_Routing::loadState(const SUMOSAXAttributes& attrs) {
288 std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
289 bis >> myPeriod;
290 }
291
292
293 /****************************************************************************/
294