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_Tripinfo.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/MSVehicle.h>
31 #include <mesosim/MEVehicle.h>
32 #include <utils/options/OptionsCont.h>
33 #include <utils/iodevices/OutputDevice.h>
34 #include <utils/xml/SUMOSAXAttributes.h>
35 #include "MSDevice_Tripinfo.h"
36 
37 #define NOT_ARRIVED TIME2STEPS(-1)
38 
39 
40 // ===========================================================================
41 // static members
42 // ===========================================================================
43 std::set<const MSDevice_Tripinfo*, ComparatorNumericalIdLess> MSDevice_Tripinfo::myPendingOutput;
44 
45 double MSDevice_Tripinfo::myVehicleCount(0);
46 double MSDevice_Tripinfo::myTotalRouteLength(0);
47 SUMOTime MSDevice_Tripinfo::myTotalDuration(0);
48 SUMOTime MSDevice_Tripinfo::myTotalWaitingTime(0);
49 SUMOTime MSDevice_Tripinfo::myTotalTimeLoss(0);
50 SUMOTime MSDevice_Tripinfo::myTotalDepartDelay(0);
51 SUMOTime MSDevice_Tripinfo::myWaitingDepartDelay(-1);
52 
53 int MSDevice_Tripinfo::myWalkCount(0);
54 double MSDevice_Tripinfo::myTotalWalkRouteLength(0);
55 SUMOTime MSDevice_Tripinfo::myTotalWalkDuration(0);
56 SUMOTime MSDevice_Tripinfo::myTotalWalkTimeLoss(0);
57 
58 int MSDevice_Tripinfo::myRideCount(0);
59 int MSDevice_Tripinfo::myRideBusCount(0);
60 int MSDevice_Tripinfo::myRideRailCount(0);
61 int MSDevice_Tripinfo::myRideBikeCount(0);
62 int MSDevice_Tripinfo::myRideAbortCount(0);
63 double MSDevice_Tripinfo::myTotalRideWaitingTime(0);
64 double MSDevice_Tripinfo::myTotalRideRouteLength(0);
65 SUMOTime MSDevice_Tripinfo::myTotalRideDuration(0);
66 
67 // ===========================================================================
68 // method definitions
69 // ===========================================================================
70 // ---------------------------------------------------------------------------
71 // static initialisation methods
72 // ---------------------------------------------------------------------------
73 void
buildVehicleDevices(SUMOVehicle & v,std::vector<MSVehicleDevice * > & into)74 MSDevice_Tripinfo::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
75     if (OptionsCont::getOptions().isSet("tripinfo-output") || OptionsCont::getOptions().getBool("duration-log.statistics")) {
76         MSDevice_Tripinfo* device = new MSDevice_Tripinfo(v, "tripinfo_" + v.getID());
77         into.push_back(device);
78         myPendingOutput.insert(device);
79     }
80 }
81 
82 
83 // ---------------------------------------------------------------------------
84 // MSDevice_Tripinfo-methods
85 // ---------------------------------------------------------------------------
MSDevice_Tripinfo(SUMOVehicle & holder,const std::string & id)86 MSDevice_Tripinfo::MSDevice_Tripinfo(SUMOVehicle& holder, const std::string& id) :
87     MSVehicleDevice(holder, id),
88     myDepartLane(""),
89     myDepartSpeed(-1),
90     myDepartPosLat(0),
91     myWaitingTime(0),
92     myAmWaiting(false),
93     myWaitingCount(0),
94     myStoppingTime(0),
95     myParkingStarted(0),
96     myArrivalTime(NOT_ARRIVED),
97     myArrivalLane(""),
98     myArrivalPos(-1),
99     myArrivalPosLat(0),
100     myArrivalSpeed(-1),
101     myMesoTimeLoss(0) {
102 }
103 
104 
~MSDevice_Tripinfo()105 MSDevice_Tripinfo::~MSDevice_Tripinfo() {
106     // ensure clean up for vaporized vehicles which do not generate output
107     myPendingOutput.erase(this);
108 }
109 
110 void
cleanup()111 MSDevice_Tripinfo::cleanup() {
112     myVehicleCount = 0;
113     myTotalRouteLength = 0;
114     myTotalDuration = 0;
115     myTotalWaitingTime = 0;
116     myTotalTimeLoss = 0;
117     myTotalDepartDelay = 0;
118     myWaitingDepartDelay = -1;
119 
120     myWalkCount = 0;
121     myTotalWalkRouteLength = 0;
122     myTotalWalkDuration = 0;
123     myTotalWalkTimeLoss = 0;
124 
125     myRideCount = 0;
126     myRideBusCount = 0;
127     myRideRailCount = 0;
128     myRideBikeCount = 0;
129     myRideAbortCount = 0;
130     myTotalRideWaitingTime = 0;
131     myTotalRideRouteLength = 0;
132     myTotalRideDuration = 0;
133 }
134 
135 bool
notifyMove(SUMOTrafficObject & veh,double,double,double newSpeed)136 MSDevice_Tripinfo::notifyMove(SUMOTrafficObject& veh, double /*oldPos*/,
137                               double /*newPos*/, double newSpeed) {
138     if (veh.isStopped()) {
139         myStoppingTime += DELTA_T;
140     } else if (newSpeed <= SUMO_const_haltingSpeed) {
141         myWaitingTime += DELTA_T;
142         if (!myAmWaiting) {
143             myWaitingCount++;
144             myAmWaiting = true;
145         }
146     } else {
147         myAmWaiting = false;
148     }
149     return true;
150 }
151 
152 void
notifyMoveInternal(const SUMOTrafficObject & veh,const double,const double timeOnLane,const double,const double meanSpeedVehicleOnLane,const double,const double,const double)153 MSDevice_Tripinfo::notifyMoveInternal(const SUMOTrafficObject& veh,
154                                       const double /* frontOnLane */,
155                                       const double timeOnLane,
156                                       const double /* meanSpeedFrontOnLane */,
157                                       const double meanSpeedVehicleOnLane,
158                                       const double /* travelledDistanceFrontOnLane */,
159                                       const double /* travelledDistanceVehicleOnLane */,
160                                       const double /* meanLengthOnLane */) {
161 
162     // called by meso
163     const MEVehicle* mesoVeh = dynamic_cast<const MEVehicle*>(&veh);
164     assert(mesoVeh);
165     const double vmax = veh.getEdge()->getVehicleMaxSpeed(&veh);
166     if (vmax > 0) {
167         myMesoTimeLoss += TIME2STEPS(timeOnLane * (vmax - meanSpeedVehicleOnLane) / vmax);
168     }
169     myWaitingTime += veh.getWaitingTime();
170     myStoppingTime += TIME2STEPS(mesoVeh->getCurrentStoppingTimeSeconds());
171 }
172 
173 bool
notifyEnter(SUMOTrafficObject & veh,MSMoveReminder::Notification reason,const MSLane *)174 MSDevice_Tripinfo::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
175     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
176         if (!MSGlobals::gUseMesoSim) {
177             myDepartLane = static_cast<MSVehicle&>(veh).getLane()->getID();
178             myDepartPosLat = static_cast<MSVehicle&>(veh).getLateralPositionOnLane();
179         }
180         myDepartSpeed = veh.getSpeed();
181     } else if (reason == MSMoveReminder::NOTIFICATION_PARKING) {
182         // notifyMove is not called while parking
183         // @note insertion delay when resuming after parking is included
184         myStoppingTime += (MSNet::getInstance()->getCurrentTimeStep() - myParkingStarted);
185     }
186     return true;
187 }
188 
189 
190 bool
notifyLeave(SUMOTrafficObject & veh,double,MSMoveReminder::Notification reason,const MSLane *)191 MSDevice_Tripinfo::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
192                                MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
193     if (reason >= MSMoveReminder::NOTIFICATION_ARRIVED) {
194         myArrivalTime = MSNet::getInstance()->getCurrentTimeStep();
195         if (!MSGlobals::gUseMesoSim) {
196             myArrivalLane = static_cast<MSVehicle&>(veh).getLane()->getID();
197             myArrivalPosLat = static_cast<MSVehicle&>(veh).getLateralPositionOnLane();
198         }
199         // @note vehicle may have moved past its arrivalPos during the last step
200         // due to non-zero arrivalspeed but we consider it as arrived at the desired position
201         // However, vaporization may happen anywhere (via TraCI)
202         if (reason == MSMoveReminder::NOTIFICATION_VAPORIZED) {
203             myArrivalPos = veh.getPositionOnLane();
204         } else {
205             myArrivalPos = myHolder.getArrivalPos();
206         }
207         myArrivalSpeed = veh.getSpeed();
208     } else if (reason == MSMoveReminder::NOTIFICATION_PARKING) {
209         myParkingStarted = MSNet::getInstance()->getCurrentTimeStep();
210     }
211     return true;
212 }
213 
214 void
computeLengthAndDuration(double & routeLength,SUMOTime & duration) const215 MSDevice_Tripinfo::computeLengthAndDuration(double& routeLength, SUMOTime& duration) const {
216     SUMOTime finalTime;
217     double finalPos;
218     double finalPosOnInternal = 0;
219     if (myArrivalTime == NOT_ARRIVED) {
220         finalTime = MSNet::getInstance()->getCurrentTimeStep();
221         finalPos = myHolder.getPositionOnLane();
222         if (!MSGlobals::gUseMesoSim) {
223             const MSLane* lane = static_cast<MSVehicle&>(myHolder).getLane();
224             if (lane->getEdge().isInternal()) {
225                 finalPosOnInternal = finalPos;
226                 finalPos = myHolder.getEdge()->getLength();
227             }
228         }
229     } else {
230         finalTime = myArrivalTime;
231         finalPos = myArrivalPos;
232     }
233     const bool includeInternalLengths = MSGlobals::gUsingInternalLanes && MSNet::getInstance()->hasInternalLinks();
234     routeLength = myHolder.getRoute().getDistanceBetween(myHolder.getDepartPos(), finalPos,
235                   myHolder.getRoute().begin(), myHolder.getCurrentRouteEdge(), includeInternalLengths) + finalPosOnInternal;
236 
237     duration = finalTime - myHolder.getDeparture();
238 }
239 
240 
241 void
generateOutput() const242 MSDevice_Tripinfo::generateOutput() const {
243     const SUMOTime timeLoss = MSGlobals::gUseMesoSim ? myMesoTimeLoss : static_cast<MSVehicle&>(myHolder).getTimeLoss();
244     updateStatistics(timeLoss);
245     if (!OptionsCont::getOptions().isSet("tripinfo-output")) {
246         return;
247     }
248     myPendingOutput.erase(this);
249     double routeLength;
250     SUMOTime duration;
251     computeLengthAndDuration(routeLength, duration);
252 
253     // write
254     OutputDevice& os = OutputDevice::getDeviceByOption("tripinfo-output");
255     os.openTag("tripinfo").writeAttr("id", myHolder.getID());
256     os.writeAttr("depart", time2string(myHolder.getDeparture()));
257     os.writeAttr("departLane", myDepartLane);
258     os.writeAttr("departPos", myHolder.getDepartPos());
259     if (MSGlobals::gLateralResolution > 0) {
260         os.writeAttr("departPosLat", myDepartPosLat);
261     }
262     os.writeAttr("departSpeed", myDepartSpeed);
263     os.writeAttr("departDelay", time2string(myHolder.getDepartDelay()));
264     os.writeAttr("arrival", time2string(myArrivalTime));
265     os.writeAttr("arrivalLane", myArrivalLane);
266     os.writeAttr("arrivalPos", myArrivalPos);
267     if (MSGlobals::gLateralResolution > 0) {
268         os.writeAttr("arrivalPosLat", myArrivalPosLat);
269     }
270     os.writeAttr("arrivalSpeed", myArrivalSpeed);
271     os.writeAttr("duration", time2string(duration));
272     os.writeAttr("routeLength", routeLength);
273     os.writeAttr("waitingTime", time2string(myWaitingTime));
274     os.writeAttr("waitingCount", myWaitingCount);
275     os.writeAttr("stopTime", time2string(myStoppingTime));
276     os.writeAttr("timeLoss", time2string(timeLoss));
277     os.writeAttr("rerouteNo", myHolder.getNumberReroutes());
278     os.writeAttr("devices", toString(myHolder.getDevices()));
279     os.writeAttr("vType", myHolder.getVehicleType().getID());
280     os.writeAttr("speedFactor", myHolder.getChosenSpeedFactor());
281     os.writeAttr("vaporized", (myHolder.getEdge() == *(myHolder.getRoute().end() - 1) ? "" : "0"));
282     // cannot close tag because emission device output might follow
283 }
284 
285 
286 void
generateOutputForUnfinished()287 MSDevice_Tripinfo::generateOutputForUnfinished() {
288     const bool writeTripinfos = OptionsCont::getOptions().isSet("tripinfo-output");
289     myWaitingDepartDelay = 0;
290     int undeparted = 0;
291     int departed = 0;
292     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
293     while (myPendingOutput.size() > 0) {
294         const MSDevice_Tripinfo* d = *myPendingOutput.begin();
295         if (d->myHolder.hasDeparted()) {
296             departed++;
297             d->generateOutput();
298             if (writeTripinfos) {
299                 // @todo also generate emission output if holder has a device
300                 OutputDevice::getDeviceByOption("tripinfo-output").closeTag();
301             } else {
302                 myPendingOutput.erase(d);
303             }
304         } else {
305             undeparted++;
306             myWaitingDepartDelay += (t - d->myHolder.getParameter().depart);
307             myPendingOutput.erase(d);
308         }
309     }
310     if (myWaitingDepartDelay > 0) {
311         myWaitingDepartDelay /= undeparted;
312     }
313 }
314 
315 
316 void
updateStatistics(SUMOTime timeLoss) const317 MSDevice_Tripinfo::updateStatistics(SUMOTime timeLoss) const {
318     double routeLength;
319     SUMOTime duration;
320     computeLengthAndDuration(routeLength, duration);
321 
322     myVehicleCount++;
323     myTotalRouteLength += routeLength;
324     myTotalDuration += duration;
325     myTotalWaitingTime += myWaitingTime;
326     myTotalTimeLoss += timeLoss;
327     myTotalDepartDelay += myHolder.getDepartDelay();
328 }
329 
330 
331 void
addPedestrianData(double walkLength,SUMOTime walkDuration,SUMOTime walkTimeLoss)332 MSDevice_Tripinfo::addPedestrianData(double walkLength, SUMOTime walkDuration, SUMOTime walkTimeLoss) {
333     myWalkCount++;
334     myTotalWalkRouteLength += walkLength;
335     myTotalWalkDuration += walkDuration;
336     myTotalWalkTimeLoss += walkTimeLoss;
337 }
338 
339 void
addRideData(double rideLength,SUMOTime rideDuration,SUMOVehicleClass vClass,const std::string & line,SUMOTime waitingTime)340 MSDevice_Tripinfo::addRideData(double rideLength, SUMOTime rideDuration, SUMOVehicleClass vClass, const std::string& line, SUMOTime waitingTime) {
341     myRideCount++;
342     if (rideDuration > 0) {
343         myTotalRideWaitingTime += waitingTime;
344         myTotalRideRouteLength += rideLength;
345         myTotalRideDuration += rideDuration;
346         if (vClass == SVC_BICYCLE) {
347             myRideBikeCount++;
348         } else if (!line.empty()) {
349             if (isRailway(vClass)) {
350                 myRideRailCount++;
351             } else {
352                 // some kind of road vehicle
353                 myRideBusCount++;
354             }
355         }
356     } else {
357         myRideAbortCount++;
358     }
359 }
360 
361 
362 std::string
printStatistics()363 MSDevice_Tripinfo::printStatistics() {
364     std::ostringstream msg;
365     msg.setf(msg.fixed);
366     msg.precision(gPrecision);
367     msg << "Statistics (avg):\n"
368         << " RouteLength: " << getAvgRouteLength() << "\n"
369         << " Duration: " << getAvgDuration() << "\n"
370         << " WaitingTime: " << getAvgWaitingTime() << "\n"
371         << " TimeLoss: " << getAvgTimeLoss() << "\n"
372         << " DepartDelay: " << getAvgDepartDelay() << "\n";
373     if (myWaitingDepartDelay >= 0) {
374         msg << " DepartDelayWaiting: " << STEPS2TIME(myWaitingDepartDelay) << "\n";
375     }
376     if (myWalkCount > 0) {
377         msg << "Pedestrian Statistics (avg of " << myWalkCount << " walks):\n"
378             << " RouteLength: " << getAvgWalkRouteLength() << "\n"
379             << " Duration: " << getAvgWalkDuration() << "\n"
380             << " TimeLoss: " << getAvgWalkTimeLoss() << "\n";
381     }
382     if (myRideCount > 0) {
383         msg << "Ride Statistics (avg of " << myRideCount << " rides):\n"
384             << " WaitingTime: " << getAvgRideWaitingTime() << "\n"
385             << " RouteLength: " << getAvgRideRouteLength() << "\n"
386             << " Duration: " << getAvgRideDuration() << "\n"
387             << " Bus: " << myRideBusCount << "\n"
388             << " Train: " << myRideRailCount << "\n"
389             << " Bike: " << myRideBikeCount << "\n"
390             << " Aborted: " << myRideAbortCount << "\n";
391     }
392     return msg.str();
393 }
394 
395 
396 double
getAvgRouteLength()397 MSDevice_Tripinfo::getAvgRouteLength() {
398     if (myVehicleCount > 0) {
399         return myTotalRouteLength / myVehicleCount;
400     } else {
401         return 0;
402     }
403 }
404 
405 double
getAvgDuration()406 MSDevice_Tripinfo::getAvgDuration() {
407     if (myVehicleCount > 0) {
408         return STEPS2TIME(myTotalDuration / myVehicleCount);
409     } else {
410         return 0;
411     }
412 }
413 
414 double
getAvgWaitingTime()415 MSDevice_Tripinfo::getAvgWaitingTime() {
416     if (myVehicleCount > 0) {
417         return STEPS2TIME(myTotalWaitingTime / myVehicleCount);
418     } else {
419         return 0;
420     }
421 }
422 
423 
424 double
getAvgTimeLoss()425 MSDevice_Tripinfo::getAvgTimeLoss() {
426     if (myVehicleCount > 0) {
427         return STEPS2TIME(myTotalTimeLoss / myVehicleCount);
428     } else {
429         return 0;
430     }
431 }
432 
433 
434 double
getAvgDepartDelay()435 MSDevice_Tripinfo::getAvgDepartDelay() {
436     if (myVehicleCount > 0) {
437         return STEPS2TIME(myTotalDepartDelay / myVehicleCount);
438     } else {
439         return 0;
440     }
441 }
442 
443 
444 double
getAvgWalkRouteLength()445 MSDevice_Tripinfo::getAvgWalkRouteLength() {
446     if (myWalkCount > 0) {
447         return myTotalWalkRouteLength / myWalkCount;
448     } else {
449         return 0;
450     }
451 }
452 
453 double
getAvgWalkDuration()454 MSDevice_Tripinfo::getAvgWalkDuration() {
455     if (myWalkCount > 0) {
456         return STEPS2TIME(myTotalWalkDuration / myWalkCount);
457     } else {
458         return 0;
459     }
460 }
461 
462 
463 double
getAvgWalkTimeLoss()464 MSDevice_Tripinfo::getAvgWalkTimeLoss() {
465     if (myWalkCount > 0) {
466         return STEPS2TIME(myTotalWalkTimeLoss / myWalkCount);
467     } else {
468         return 0;
469     }
470 }
471 
472 
473 double
getAvgRideDuration()474 MSDevice_Tripinfo::getAvgRideDuration() {
475     if (myRideCount > 0) {
476         return STEPS2TIME(myTotalRideDuration / myRideCount);
477     } else {
478         return 0;
479     }
480 }
481 
482 double
getAvgRideWaitingTime()483 MSDevice_Tripinfo::getAvgRideWaitingTime() {
484     if (myRideCount > 0) {
485         return STEPS2TIME(myTotalRideWaitingTime / myRideCount);
486     } else {
487         return 0;
488     }
489 }
490 
491 double
getAvgRideRouteLength()492 MSDevice_Tripinfo::getAvgRideRouteLength() {
493     if (myRideCount > 0) {
494         return myTotalRideRouteLength / myRideCount;
495     } else {
496         return 0;
497     }
498 }
499 
500 
501 void
saveState(OutputDevice & out) const502 MSDevice_Tripinfo::saveState(OutputDevice& out) const {
503     out.openTag(SUMO_TAG_DEVICE);
504     out.writeAttr(SUMO_ATTR_ID, getID());
505     std::vector<std::string> internals;
506     internals.push_back(myDepartLane);
507     internals.push_back(toString(myDepartPosLat));
508     internals.push_back(toString(myDepartSpeed));
509     out.writeAttr(SUMO_ATTR_STATE, toString(internals));
510     out.closeTag();
511 }
512 
513 
514 void
loadState(const SUMOSAXAttributes & attrs)515 MSDevice_Tripinfo::loadState(const SUMOSAXAttributes& attrs) {
516     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
517     bis >> myDepartLane;
518     bis >> myDepartPosLat;
519     bis >> myDepartSpeed;
520 }
521 
522 
523 /****************************************************************************/
524