1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2009-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_Vehroutes.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Laura Bieker
13 /// @author  Michael Behrisch
14 /// @author  Jakob Erdmann
15 /// @date    Fri, 30.01.2009
16 /// @version $Id$
17 ///
18 // A device which collects info on the vehicle trip
19 /****************************************************************************/
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <microsim/MSGlobals.h>
27 #include <microsim/MSNet.h>
28 #include <microsim/MSLane.h>
29 #include <microsim/MSEdge.h>
30 #include <microsim/MSRoute.h>
31 #include <microsim/MSVehicle.h>
32 #include <microsim/MSVehicleType.h>
33 #include <utils/vehicle/SUMOVehicle.h>
34 #include <utils/options/OptionsCont.h>
35 #include <utils/iodevices/OutputDevice_String.h>
36 #include <utils/xml/SUMOSAXAttributes.h>
37 #include "MSDevice_Vehroutes.h"
38 
39 
40 // ===========================================================================
41 // static member variables
42 // ===========================================================================
43 bool MSDevice_Vehroutes::mySaveExits = false;
44 bool MSDevice_Vehroutes::myLastRouteOnly = false;
45 bool MSDevice_Vehroutes::myDUAStyle = false;
46 bool MSDevice_Vehroutes::myWriteCosts = false;
47 bool MSDevice_Vehroutes::mySorted = false;
48 bool MSDevice_Vehroutes::myIntendedDepart = false;
49 bool MSDevice_Vehroutes::myRouteLength = false;
50 bool MSDevice_Vehroutes::mySkipPTLines = false;
51 MSDevice_Vehroutes::StateListener MSDevice_Vehroutes::myStateListener;
52 std::map<const SUMOTime, int> MSDevice_Vehroutes::myDepartureCounts;
53 std::map<const SUMOTime, std::map<const std::string, std::string> > MSDevice_Vehroutes::myRouteInfos;
54 
55 
56 // ===========================================================================
57 // method definitions
58 // ===========================================================================
59 // ---------------------------------------------------------------------------
60 // static initialisation methods
61 // ---------------------------------------------------------------------------
62 void
init()63 MSDevice_Vehroutes::init() {
64     const OptionsCont& oc = OptionsCont::getOptions();
65     if (oc.isSet("vehroute-output")) {
66         OutputDevice::createDeviceByOption("vehroute-output", "routes", "routes_file.xsd");
67         mySaveExits = oc.getBool("vehroute-output.exit-times");
68         myLastRouteOnly = oc.getBool("vehroute-output.last-route");
69         myDUAStyle = oc.getBool("vehroute-output.dua");
70         myWriteCosts = oc.getBool("vehroute-output.cost");
71         mySorted = myDUAStyle || oc.getBool("vehroute-output.sorted");
72         myIntendedDepart = oc.getBool("vehroute-output.intended-depart");
73         myRouteLength = oc.getBool("vehroute-output.route-length");
74         mySkipPTLines = oc.getBool("vehroute-output.skip-ptlines");
75         MSNet::getInstance()->addVehicleStateListener(&myStateListener);
76     }
77 }
78 
79 
80 MSDevice_Vehroutes*
buildVehicleDevices(SUMOVehicle & v,std::vector<MSVehicleDevice * > & into,int maxRoutes)81 MSDevice_Vehroutes::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into, int maxRoutes) {
82     if (maxRoutes < std::numeric_limits<int>::max()) {
83         return new MSDevice_Vehroutes(v, "vehroute_" + v.getID(), maxRoutes);
84     }
85     if (OptionsCont::getOptions().isSet("vehroute-output")) {
86         if (myLastRouteOnly) {
87             maxRoutes = 0;
88         }
89         myStateListener.myDevices[&v] = new MSDevice_Vehroutes(v, "vehroute_" + v.getID(), maxRoutes);
90         into.push_back(myStateListener.myDevices[&v]);
91         return myStateListener.myDevices[&v];
92     }
93     return nullptr;
94 }
95 
96 
97 // ---------------------------------------------------------------------------
98 // MSDevice_Vehroutes::StateListener-methods
99 // ---------------------------------------------------------------------------
100 void
vehicleStateChanged(const SUMOVehicle * const vehicle,MSNet::VehicleState to,const std::string & info)101 MSDevice_Vehroutes::StateListener::vehicleStateChanged(const SUMOVehicle* const vehicle, MSNet::VehicleState to, const std::string& info) {
102     if (to == MSNet::VEHICLE_STATE_NEWROUTE) {
103         myDevices[vehicle]->addRoute(info);
104     }
105 }
106 
107 
108 // ---------------------------------------------------------------------------
109 // MSDevice_Vehroutes-methods
110 // ---------------------------------------------------------------------------
MSDevice_Vehroutes(SUMOVehicle & holder,const std::string & id,int maxRoutes)111 MSDevice_Vehroutes::MSDevice_Vehroutes(SUMOVehicle& holder, const std::string& id, int maxRoutes) :
112     MSVehicleDevice(holder, id),
113     myCurrentRoute(&holder.getRoute()),
114     myMaxRoutes(maxRoutes),
115     myLastSavedAt(nullptr),
116     myDepartLane(-1),
117     myDepartPos(-1),
118     myDepartSpeed(-1),
119     myDepartPosLat(0),
120     myStopOut(false, 2) {
121     myCurrentRoute->addReference();
122 }
123 
124 
~MSDevice_Vehroutes()125 MSDevice_Vehroutes::~MSDevice_Vehroutes() {
126     for (std::vector<RouteReplaceInfo>::iterator i = myReplacedRoutes.begin(); i != myReplacedRoutes.end(); ++i) {
127         (*i).route->release();
128     }
129     myCurrentRoute->release();
130     myStateListener.myDevices.erase(&myHolder);
131 }
132 
133 
134 bool
notifyEnter(SUMOTrafficObject & veh,MSMoveReminder::Notification reason,const MSLane *)135 MSDevice_Vehroutes::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
136     MSVehicle& vehicle = static_cast<MSVehicle&>(veh);
137     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
138         if (mySorted && myStateListener.myDevices[&vehicle] == this) {
139             const SUMOTime departure = myIntendedDepart ? myHolder.getParameter().depart : MSNet::getInstance()->getCurrentTimeStep();
140             myDepartureCounts[departure]++;
141         }
142         if (!MSGlobals::gUseMesoSim) {
143             myDepartLane = vehicle.getLane()->getIndex();
144             myDepartPosLat = vehicle.getLateralPositionOnLane();
145         }
146         myDepartSpeed = veh.getSpeed();
147         myDepartPos = veh.getPositionOnLane();
148     }
149     return mySaveExits;
150 }
151 
152 
153 bool
notifyLeave(SUMOTrafficObject & veh,double,MSMoveReminder::Notification reason,const MSLane *)154 MSDevice_Vehroutes::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
155     if (mySaveExits && reason != NOTIFICATION_LANE_CHANGE) {
156         if (reason != NOTIFICATION_TELEPORT && myLastSavedAt == veh.getEdge()) { // need to check this for internal lanes
157             myExits.back() = MSNet::getInstance()->getCurrentTimeStep();
158         } else if (myLastSavedAt != veh.getEdge()) {
159             myExits.push_back(MSNet::getInstance()->getCurrentTimeStep());
160             myLastSavedAt = veh.getEdge();
161         }
162     }
163     return mySaveExits;
164 }
165 
166 
167 void
stopEnded(const SUMOVehicleParameter::Stop & stop)168 MSDevice_Vehroutes::stopEnded(const SUMOVehicleParameter::Stop& stop) {
169     stop.write(myStopOut);
170 }
171 
172 
173 void
writeXMLRoute(OutputDevice & os,int index) const174 MSDevice_Vehroutes::writeXMLRoute(OutputDevice& os, int index) const {
175     if (index == 0 && myReplacedRoutes[index].route->size() == 2 &&
176             myReplacedRoutes[index].route->getEdges().front()->isTazConnector() &&
177             myReplacedRoutes[index].route->getEdges().back()->isTazConnector()) {
178         return;
179     }
180     // check if a previous route shall be written
181     os.openTag(SUMO_TAG_ROUTE);
182     if (index >= 0) {
183         assert((int)myReplacedRoutes.size() > index);
184         if (myDUAStyle || myWriteCosts) {
185             os.writeAttr(SUMO_ATTR_COST, myReplacedRoutes[index].route->getCosts());
186         }
187         if (myWriteCosts) {
188             os.writeAttr(SUMO_ATTR_SAVINGS, myReplacedRoutes[index].route->getSavings());
189         }
190         // write edge on which the vehicle was when the route was valid
191         os.writeAttr("replacedOnEdge", (myReplacedRoutes[index].edge ?
192                                         myReplacedRoutes[index].edge->getID() : ""));
193         // write the reason for replacement
194         os.writeAttr("reason", myReplacedRoutes[index].info);
195 
196         // write the time at which the route was replaced
197         os.writeAttr("replacedAtTime", time2string(myReplacedRoutes[index].time));
198         os.writeAttr(SUMO_ATTR_PROB, "0");
199         os << " edges=\"";
200         // get the route
201         int i = index;
202         while (i > 0 && myReplacedRoutes[i - 1].edge) {
203             i--;
204         }
205         const MSEdge* lastEdge = nullptr;
206         for (; i < index; ++i) {
207             myReplacedRoutes[i].route->writeEdgeIDs(os, lastEdge, myReplacedRoutes[i].edge);
208             lastEdge = myReplacedRoutes[i].edge;
209         }
210         myReplacedRoutes[index].route->writeEdgeIDs(os, lastEdge);
211     } else {
212         if (myDUAStyle || myWriteCosts) {
213             os.writeAttr(SUMO_ATTR_COST, myHolder.getRoute().getCosts());
214         }
215         if (myWriteCosts) {
216             os.writeAttr(SUMO_ATTR_SAVINGS, myHolder.getRoute().getSavings());
217         }
218         os << " edges=\"";
219         const MSEdge* lastEdge = nullptr;
220         int numWritten = 0;
221         if (myHolder.getNumberReroutes() > 0) {
222             assert((int)myReplacedRoutes.size() <= myHolder.getNumberReroutes());
223             int i = (int)myReplacedRoutes.size();
224             while (i > 0 && myReplacedRoutes[i - 1].edge) {
225                 i--;
226             }
227             for (; i < (int)myReplacedRoutes.size(); ++i) {
228                 numWritten += myReplacedRoutes[i].route->writeEdgeIDs(os, lastEdge, myReplacedRoutes[i].edge);
229                 lastEdge = myReplacedRoutes[i].edge;
230             }
231         }
232         const MSEdge* upTo = nullptr;
233         if (mySaveExits) {
234             int remainingWithExitTime = (int)myExits.size() - numWritten;
235             assert(remainingWithExitTime >= 0);
236             assert(remainingWithExitTime <= (int)myCurrentRoute->size());
237             if (remainingWithExitTime < (int)myCurrentRoute->size()) {
238                 upTo = *(myCurrentRoute->begin() + remainingWithExitTime);
239             }
240         }
241         myCurrentRoute->writeEdgeIDs(os, lastEdge, upTo);
242         if (mySaveExits) {
243             os << "\" exitTimes=\"";
244             for (std::vector<SUMOTime>::const_iterator it = myExits.begin(); it != myExits.end(); ++it) {
245                 if (it != myExits.begin()) {
246                     os << " ";
247                 }
248                 os << time2string(*it);
249             }
250         }
251     }
252     (os << "\"").closeTag();
253 }
254 
255 
256 void
generateOutput() const257 MSDevice_Vehroutes::generateOutput() const {
258     writeOutput(true);
259 }
260 
261 
262 void
writeOutput(const bool hasArrived) const263 MSDevice_Vehroutes::writeOutput(const bool hasArrived) const {
264     if (mySkipPTLines && myHolder.getParameter().line != "") {
265         return;
266     }
267     OutputDevice& routeOut = OutputDevice::getDeviceByOption("vehroute-output");
268     OutputDevice_String od(routeOut.isBinary(), 1);
269     SUMOVehicleParameter tmp = myHolder.getParameter();
270     tmp.depart = myIntendedDepart ? myHolder.getParameter().depart : myHolder.getDeparture();
271     if (!MSGlobals::gUseMesoSim) {
272         if (tmp.wasSet(VEHPARS_DEPARTLANE_SET)) {
273             tmp.departLaneProcedure = DEPART_LANE_GIVEN;
274             tmp.departLane = myDepartLane;
275         }
276         if (tmp.wasSet(VEHPARS_DEPARTPOSLAT_SET)) {
277             tmp.departPosLatProcedure = DEPART_POSLAT_GIVEN;
278             tmp.departPosLat = myDepartPosLat;
279         }
280     }
281     if (tmp.wasSet(VEHPARS_DEPARTPOS_SET)) {
282         tmp.departPosProcedure = DEPART_POS_GIVEN;
283         tmp.departPos = myDepartPos;
284     }
285     if (tmp.wasSet(VEHPARS_DEPARTSPEED_SET)) {
286         tmp.departSpeedProcedure = DEPART_SPEED_GIVEN;
287         tmp.departSpeed = myDepartSpeed;
288     }
289     const std::string typeID = myHolder.getVehicleType().getID() != DEFAULT_VTYPE_ID ? myHolder.getVehicleType().getID() : "";
290     tmp.write(od, OptionsCont::getOptions(), SUMO_TAG_VEHICLE, typeID);
291     if (hasArrived) {
292         od.writeAttr("arrival", time2string(MSNet::getInstance()->getCurrentTimeStep()));
293         if (myRouteLength) {
294             const bool includeInternalLengths = MSGlobals::gUsingInternalLanes && MSNet::getInstance()->hasInternalLinks();
295             const double routeLength = myHolder.getRoute().getDistanceBetween(myHolder.getDepartPos(), myHolder.getArrivalPos(),
296                                        myHolder.getRoute().begin(), myHolder.getCurrentRouteEdge(), includeInternalLengths);
297             od.writeAttr("routeLength", routeLength);
298         }
299     }
300     if (myDUAStyle) {
301         const RandomDistributor<const MSRoute*>* const routeDist = MSRoute::distDictionary("!" + myHolder.getID());
302         if (routeDist != nullptr) {
303             const std::vector<const MSRoute*>& routes = routeDist->getVals();
304             unsigned index = 0;
305             while (index < routes.size() && routes[index] != myCurrentRoute) {
306                 ++index;
307             }
308             od.openTag(SUMO_TAG_ROUTE_DISTRIBUTION).writeAttr(SUMO_ATTR_LAST, index);
309             const std::vector<double>& probs = routeDist->getProbs();
310             for (int i = 0; i < (int)routes.size(); ++i) {
311                 od.setPrecision();
312                 od.openTag(SUMO_TAG_ROUTE);
313                 od.writeAttr(SUMO_ATTR_COST, routes[i]->getCosts());
314                 if (myWriteCosts) {
315                     od.writeAttr(SUMO_ATTR_SAVINGS, routes[i]->getSavings());
316                 }
317                 od.setPrecision(8);
318                 od.writeAttr(SUMO_ATTR_PROB, probs[i]);
319                 od.setPrecision();
320                 od << " edges=\"";
321                 routes[i]->writeEdgeIDs(od, *routes[i]->begin());
322                 (od << "\"").closeTag();
323             }
324             od.closeTag();
325         } else {
326             writeXMLRoute(od);
327         }
328     } else {
329         if (myReplacedRoutes.size() > 0) {
330             od.openTag(SUMO_TAG_ROUTE_DISTRIBUTION);
331             for (int i = 0; i < (int)myReplacedRoutes.size(); ++i) {
332                 writeXMLRoute(od, i);
333             }
334         }
335         writeXMLRoute(od);
336         if (myReplacedRoutes.size() > 0) {
337             od.closeTag();
338         }
339     }
340     od << myStopOut.getString();
341     myHolder.getParameter().writeParams(od);
342     od.closeTag();
343     od.lf();
344     if (mySorted) {
345         myRouteInfos[tmp.depart][myHolder.getID()] = od.getString();
346         myDepartureCounts[tmp.depart]--;
347         std::map<const SUMOTime, int>::iterator it = myDepartureCounts.begin();
348         while (it != myDepartureCounts.end() && it->second == 0) {
349             std::map<const std::string, std::string>& infos = myRouteInfos[it->first];
350             for (std::map<const std::string, std::string>::const_iterator it2 = infos.begin(); it2 != infos.end(); ++it2) {
351                 routeOut << it2->second;
352             }
353             myRouteInfos.erase(it->first);
354             myDepartureCounts.erase(it);
355             it = myDepartureCounts.begin();
356         }
357     } else {
358         routeOut << od.getString();
359     }
360 }
361 
362 
363 const MSRoute*
getRoute(int index) const364 MSDevice_Vehroutes::getRoute(int index) const {
365     if (index < (int)myReplacedRoutes.size()) {
366         return myReplacedRoutes[index].route;
367     } else {
368         return nullptr;
369     }
370 }
371 
372 
373 void
addRoute(const std::string & info)374 MSDevice_Vehroutes::addRoute(const std::string& info) {
375     if (myMaxRoutes > 0) {
376         if (myHolder.hasDeparted()) {
377             myReplacedRoutes.push_back(RouteReplaceInfo(myHolder.getEdge(), MSNet::getInstance()->getCurrentTimeStep(), myCurrentRoute, info));
378         } else {
379             myReplacedRoutes.push_back(RouteReplaceInfo(nullptr, MSNet::getInstance()->getCurrentTimeStep(), myCurrentRoute, info));
380         }
381         if ((int)myReplacedRoutes.size() > myMaxRoutes) {
382             myReplacedRoutes.front().route->release();
383             myReplacedRoutes.erase(myReplacedRoutes.begin());
384         }
385     } else {
386         myCurrentRoute->release();
387     }
388     myCurrentRoute = &myHolder.getRoute();
389     myCurrentRoute->addReference();
390 }
391 
392 
393 void
generateOutputForUnfinished()394 MSDevice_Vehroutes::generateOutputForUnfinished() {
395     for (const auto& it : myStateListener.myDevices) {
396         if (it.first->hasDeparted()) {
397             it.second->writeOutput(false);
398         }
399     }
400 }
401 
402 
403 void
saveState(OutputDevice & out) const404 MSDevice_Vehroutes::saveState(OutputDevice& out) const {
405     out.openTag(SUMO_TAG_DEVICE);
406     out.writeAttr(SUMO_ATTR_ID, getID());
407     std::vector<std::string> internals;
408     if (!MSGlobals::gUseMesoSim) {
409         internals.push_back(toString(myDepartLane));
410         internals.push_back(toString(myDepartPosLat));
411     }
412     internals.push_back(toString(myDepartSpeed));
413     internals.push_back(toString(myDepartPos));
414     internals.push_back(toString(myReplacedRoutes.size()));
415     for (int i = 0; i < (int)myReplacedRoutes.size(); ++i) {
416         const std::string replacedOnEdge = myReplacedRoutes[i].edge == nullptr ? "!NULL" : myReplacedRoutes[i].edge->getID();
417         internals.push_back(replacedOnEdge);
418         internals.push_back(toString(myReplacedRoutes[i].time));
419         internals.push_back(myReplacedRoutes[i].route->getID());
420         internals.push_back(myReplacedRoutes[i].info);
421     }
422     out.writeAttr(SUMO_ATTR_STATE, toString(internals));
423     out.closeTag();
424 }
425 
426 
427 void
loadState(const SUMOSAXAttributes & attrs)428 MSDevice_Vehroutes::loadState(const SUMOSAXAttributes& attrs) {
429     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
430     if (!MSGlobals::gUseMesoSim) {
431         bis >> myDepartLane;
432         bis >> myDepartPosLat;
433     }
434     bis >> myDepartSpeed;
435     bis >> myDepartPos;
436     int size;
437     bis >> size;
438     for (int i = 0; i < size; ++i) {
439         std::string edgeID;
440         SUMOTime time;
441         std::string routeID;
442         std::string info;
443         bis >> edgeID;
444         bis >> time;
445         bis >> routeID;
446         bis >> info;
447         const MSRoute* route = MSRoute::dictionary(routeID);
448         route->addReference();
449         myReplacedRoutes.push_back(RouteReplaceInfo(MSEdge::dictionary(edgeID), time, route, info));
450     }
451 }
452 
453 
454 /****************************************************************************/
455