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