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