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