1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-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 MSBaseVehicle.cpp
11 /// @author Michael Behrisch
12 /// @author Daniel Krajzewicz
13 /// @author Jakob Erdmann
14 /// @date Mon, 8 Nov 2010
15 /// @version $Id$
16 ///
17 // A base class for vehicle implementations
18 /****************************************************************************/
19
20
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25
26 #include <iostream>
27 #include <cassert>
28 #include <utils/common/StdDefs.h>
29 #include <utils/common/MsgHandler.h>
30 #include <utils/options/OptionsCont.h>
31 #include <utils/iodevices/OutputDevice.h>
32 #include <microsim/pedestrians/MSPerson.h>
33 #include "MSGlobals.h"
34 #include "MSTransportable.h"
35 #include "MSVehicleControl.h"
36 #include "MSVehicleType.h"
37 #include "MSEdge.h"
38 #include "MSLane.h"
39 #include "MSMoveReminder.h"
40 #include "MSBaseVehicle.h"
41 #include "MSNet.h"
42 #include "devices/MSDevice.h"
43 #include "devices/MSDevice_Routing.h"
44 #include <microsim/devices/MSDevice_Transportable.h>
45 #include "MSInsertionControl.h"
46
47 //#define DEBUG_REROUTE
48 //#define DEBUG_COND (getID() == "follower")
49 //#define DEBUG_COND (true)
50 #define DEBUG_COND (isSelected())
51
52 // ===========================================================================
53 // static members
54 // ===========================================================================
55 const SUMOTime MSBaseVehicle::NOT_YET_DEPARTED = SUMOTime_MAX;
56 std::vector<MSTransportable*> MSBaseVehicle::myEmptyTransportableVector;
57 #ifdef _DEBUG
58 std::set<std::string> MSBaseVehicle::myShallTraceMoveReminders;
59 #endif
60 SUMOVehicle::NumericalID MSBaseVehicle::myCurrentNumericalIndex = 0;
61
62
63 // ===========================================================================
64 // method definitions
65 // ===========================================================================
66
67 double
getPreviousSpeed() const68 MSBaseVehicle::getPreviousSpeed() const {
69 throw ProcessError("getPreviousSpeed() is not available for non-MSVehicles.");
70 }
71
72
MSBaseVehicle(SUMOVehicleParameter * pars,const MSRoute * route,MSVehicleType * type,const double speedFactor)73 MSBaseVehicle::MSBaseVehicle(SUMOVehicleParameter* pars, const MSRoute* route,
74 MSVehicleType* type, const double speedFactor) :
75 myParameter(pars),
76 myRoute(route),
77 myType(type),
78 myCurrEdge(route->begin()),
79 myChosenSpeedFactor(speedFactor),
80 myMoveReminders(0),
81 myPersonDevice(nullptr),
82 myContainerDevice(nullptr),
83 myDeparture(NOT_YET_DEPARTED),
84 myDepartPos(-1),
85 myArrivalPos(-1),
86 myArrivalLane(-1),
87 myNumberReroutes(0),
88 myNumericalID(myCurrentNumericalIndex++)
89 #ifdef _DEBUG
90 , myTraceMoveReminders(myShallTraceMoveReminders.count(pars->id) > 0)
91 #endif
92 {
93 if ((*myRoute->begin())->isTazConnector() || myRoute->getLastEdge()->isTazConnector()) {
94 pars->parametersSet |= VEHPARS_FORCE_REROUTE;
95 }
96 myRoute->addReference();
97 if (!pars->wasSet(VEHPARS_FORCE_REROUTE)) {
98 calculateArrivalParams();
99 if (MSGlobals::gCheckRoutes) {
100 std::string msg;
101 if (!hasValidRoute(msg)) {
102 throw ProcessError("Vehicle '" + pars->id + "' has no valid route. " + msg);
103 }
104 }
105 }
106 // init devices
107 MSDevice::buildVehicleDevices(*this, myDevices);
108 for (MSVehicleDevice* dev : myDevices) {
109 myMoveReminders.push_back(std::make_pair(dev, 0.));
110 }
111 }
112
113
~MSBaseVehicle()114 MSBaseVehicle::~MSBaseVehicle() {
115 myRoute->release();
116 if (myParameter->repetitionNumber == 0) {
117 MSRoute::checkDist(myParameter->routeid);
118 }
119 for (MSVehicleDevice* dev : myDevices) {
120 delete dev;
121 }
122 delete myParameter;
123 }
124
125
126 const std::string&
getID() const127 MSBaseVehicle::getID() const {
128 return myParameter->id;
129 }
130
131
132 const SUMOVehicleParameter&
getParameter() const133 MSBaseVehicle::getParameter() const {
134 return *myParameter;
135 }
136
137 void
replaceParameter(const SUMOVehicleParameter * newParameter)138 MSBaseVehicle::replaceParameter(const SUMOVehicleParameter* newParameter) {
139 delete myParameter;
140 myParameter = newParameter;
141 }
142
143 double
getMaxSpeed() const144 MSBaseVehicle::getMaxSpeed() const {
145 return myType->getMaxSpeed();
146 }
147
148
149 const MSEdge*
succEdge(int nSuccs) const150 MSBaseVehicle::succEdge(int nSuccs) const {
151 if (myCurrEdge + nSuccs < myRoute->end() && std::distance(myCurrEdge, myRoute->begin()) <= nSuccs) {
152 return *(myCurrEdge + nSuccs);
153 } else {
154 return nullptr;
155 }
156 }
157
158
159 const MSEdge*
getEdge() const160 MSBaseVehicle::getEdge() const {
161 return *myCurrEdge;
162 }
163
164
165 void
reroute(SUMOTime t,const std::string & info,SUMOAbstractRouter<MSEdge,SUMOVehicle> & router,const bool onInit,const bool withTaz,const bool silent)166 MSBaseVehicle::reroute(SUMOTime t, const std::string& info, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router, const bool onInit, const bool withTaz, const bool silent) {
167 // check whether to reroute
168 const MSEdge* source = withTaz && onInit ? MSEdge::dictionary(myParameter->fromTaz + "-source") : getRerouteOrigin();
169 if (source == nullptr) {
170 source = getRerouteOrigin();
171 }
172 const MSEdge* sink = withTaz ? MSEdge::dictionary(myParameter->toTaz + "-sink") : myRoute->getLastEdge();
173 if (sink == nullptr) {
174 sink = myRoute->getLastEdge();
175 }
176 ConstMSEdgeVector oldEdgesRemaining(source == *myCurrEdge ? myCurrEdge : myCurrEdge + 1, myRoute->end());
177 ConstMSEdgeVector edges;
178 ConstMSEdgeVector stops;
179 if (myParameter->via.size() == 0) {
180 double firstPos = -1;
181 double lastPos = -1;
182 stops = getStopEdges(firstPos, lastPos);
183 if (stops.size() > 0) {
184 const double sourcePos = onInit ? 0 : getPositionOnLane();
185 // avoid superfluous waypoints for first and last edge
186 const bool skipFirst = stops.front() == source && sourcePos < firstPos;
187 const bool skipLast = stops.back() == sink && myArrivalPos > lastPos;
188 #ifdef DEBUG_REROUTE
189 if (DEBUG_COND) {
190 std::cout << SIMTIME << " reroute " << info << " veh=" << getID() << " lane=" << getLane()->getID()
191 << " source=" << source->getID() << " sourcePos=" << sourcePos << " firstPos=" << firstPos << " arrivalPos=" << myArrivalPos << " lastPos=" << lastPos
192 << " route=" << toString(myRoute->getEdges()) << " stopEdges=" << toString(stops) << " skipFirst=" << skipFirst << " skipLast=" << skipLast << "\n";
193 }
194 #endif
195 if (stops.size() == 1 && (skipFirst || skipLast)) {
196 stops.clear();
197 } else {
198 if (skipFirst) {
199 stops.erase(stops.begin());
200 }
201 if (skipLast) {
202 stops.erase(stops.end() - 1);
203 }
204 }
205 }
206 } else {
207 // via takes precedence over stop edges
208 // XXX check for inconsistencies #2275
209 for (std::vector<std::string>::const_iterator it = myParameter->via.begin(); it != myParameter->via.end(); ++it) {
210 MSEdge* viaEdge = MSEdge::dictionary(*it);
211 if (viaEdge == source || viaEdge == sink) {
212 continue;
213 }
214 assert(viaEdge != 0);
215 if (!viaEdge->isTazConnector() && viaEdge->allowedLanes(getVClass()) == nullptr) {
216 throw ProcessError("Vehicle '" + getID() + "' is not allowed on any lane of via edge '" + viaEdge->getID() + "'.");
217 }
218 stops.push_back(viaEdge);
219 }
220 }
221
222 for (MSRouteIterator s = stops.begin(); s != stops.end(); ++s) {
223 // !!! need to adapt t here
224 ConstMSEdgeVector into;
225 router.computeLooped(source, *s, this, t, into, silent);
226 if (into.size() > 0) {
227 into.pop_back();
228 edges.insert(edges.end(), into.begin(), into.end());
229 if ((*s)->isTazConnector()) {
230 source = into.back();
231 edges.pop_back();
232 } else {
233 source = *s;
234 }
235 } else {
236 std::string error = "Vehicle '" + getID() + "' has no valid route from edge '" + source->getID() + "' to stop edge '" + (*s)->getID() + "'.";
237 if (MSGlobals::gCheckRoutes || silent) {
238 throw ProcessError(error);
239 } else {
240 WRITE_WARNING(error);
241 edges.push_back(source);
242 }
243 source = *s;
244 }
245 }
246 router.compute(source, sink, this, t, edges, silent);
247 if (!edges.empty() && edges.front()->isTazConnector()) {
248 edges.erase(edges.begin());
249 }
250 if (!edges.empty() && edges.back()->isTazConnector()) {
251 edges.pop_back();
252 }
253 const double routeCost = router.recomputeCosts(edges, this, t);
254 const double previousCost = onInit ? routeCost : router.recomputeCosts(oldEdgesRemaining, this, t);
255 const double savings = previousCost - routeCost;
256 //if (getID() == "43") std::cout << SIMTIME << " pCost=" << previousCost << " cost=" << routeCost
257 // << " onInit=" << onInit
258 // << " prevEdges=" << toString(oldEdgesRemaining)
259 // << " newEdges=" << toString(edges)
260 // << "\n";
261 replaceRouteEdges(edges, routeCost, savings, info, onInit);
262 // this must be called even if the route could not be replaced
263 if (onInit) {
264 if (edges.empty()) {
265 if (MSGlobals::gCheckRoutes) {
266 throw ProcessError("Vehicle '" + getID() + "' has no valid route.");
267 } else if (source->isTazConnector()) {
268 WRITE_WARNING("Removing vehicle '" + getID() + "' which has no valid route.");
269 MSNet::getInstance()->getInsertionControl().descheduleDeparture(this);
270 return;
271 }
272 }
273 calculateArrivalParams();
274 }
275 }
276
277
278 bool
replaceRouteEdges(ConstMSEdgeVector & edges,double cost,double savings,const std::string & info,bool onInit,bool check,bool removeStops)279 MSBaseVehicle::replaceRouteEdges(ConstMSEdgeVector& edges, double cost, double savings, const std::string& info, bool onInit, bool check, bool removeStops) {
280 if (edges.empty()) {
281 WRITE_WARNING("No route for vehicle '" + getID() + "' found.");
282 return false;
283 }
284 // build a new id, first
285 std::string id = getID();
286 if (id[0] != '!') {
287 id = "!" + id;
288 }
289 if (myRoute->getID().find("!var#") != std::string::npos) {
290 id = myRoute->getID().substr(0, myRoute->getID().rfind("!var#") + 5) + toString(getNumberReroutes() + 1);
291 } else {
292 id = id + "!var#1";
293 }
294 int oldSize = (int)edges.size();
295 if (!onInit) {
296 const MSEdge* const origin = getRerouteOrigin();
297 if (origin != *myCurrEdge && edges.front() == origin) {
298 edges.insert(edges.begin(), *myCurrEdge);
299 oldSize = (int)edges.size();
300 }
301 edges.insert(edges.begin(), myRoute->begin(), myCurrEdge);
302 }
303 if (edges == myRoute->getEdges() && !StringUtils::endsWith(info, toString(SUMO_TAG_PARKING_ZONE_REROUTE))) {
304 // re-assign stop iterators when rerouting to a new parkingArea
305 return true;
306 }
307 const RGBColor& c = myRoute->getColor();
308 MSRoute* newRoute = new MSRoute(id, edges, false, &c == &RGBColor::DEFAULT_COLOR ? nullptr : new RGBColor(c), std::vector<SUMOVehicleParameter::Stop>());
309 newRoute->setCosts(cost);
310 newRoute->setSavings(savings);
311 if (!MSRoute::dictionary(id, newRoute)) {
312 delete newRoute;
313 return false;
314 }
315
316 std::string msg;
317 if (check && !hasValidRoute(msg, newRoute)) {
318 WRITE_WARNING("Invalid route replacement for vehicle '" + getID() + "'. " + msg);
319 if (MSGlobals::gCheckRoutes) {
320 newRoute->addReference();
321 newRoute->release();
322 return false;
323 }
324 }
325 if (!replaceRoute(newRoute, info, onInit, (int)edges.size() - oldSize, false, removeStops)) {
326 newRoute->addReference();
327 newRoute->release();
328 return false;
329 }
330 return true;
331 }
332
333
334 double
getAcceleration() const335 MSBaseVehicle::getAcceleration() const {
336 return 0;
337 }
338
339
340 double
getSlope() const341 MSBaseVehicle::getSlope() const {
342 return 0;
343 }
344
345
346 void
onDepart()347 MSBaseVehicle::onDepart() {
348 myDeparture = MSNet::getInstance()->getCurrentTimeStep();
349 myDepartPos = getPositionOnLane();
350 MSNet::getInstance()->getVehicleControl().vehicleDeparted(*this);
351 }
352
353
354 bool
hasDeparted() const355 MSBaseVehicle::hasDeparted() const {
356 return myDeparture != NOT_YET_DEPARTED;
357 }
358
359
360 bool
hasArrived() const361 MSBaseVehicle::hasArrived() const {
362 return succEdge(1) == nullptr;
363 }
364
365 void
addPerson(MSTransportable * person)366 MSBaseVehicle::addPerson(MSTransportable* person) {
367 if (myPersonDevice == nullptr) {
368 myPersonDevice = MSDevice_Transportable::buildVehicleDevices(*this, myDevices, false);
369 myMoveReminders.push_back(std::make_pair(myPersonDevice, 0.));
370 if (myParameter->departProcedure == DEPART_TRIGGERED && myParameter->depart == -1) {
371 const_cast<SUMOVehicleParameter*>(myParameter)->depart = MSNet::getInstance()->getCurrentTimeStep();
372 }
373 }
374 myPersonDevice->addTransportable(person);
375 }
376
377 void
addContainer(MSTransportable * container)378 MSBaseVehicle::addContainer(MSTransportable* container) {
379 if (myContainerDevice == nullptr) {
380 myContainerDevice = MSDevice_Transportable::buildVehicleDevices(*this, myDevices, true);
381 myMoveReminders.push_back(std::make_pair(myContainerDevice, 0.));
382 if (myParameter->departProcedure == DEPART_TRIGGERED && myParameter->depart == -1) {
383 const_cast<SUMOVehicleParameter*>(myParameter)->depart = MSNet::getInstance()->getCurrentTimeStep();
384 }
385 }
386 myContainerDevice->addTransportable(container);
387 }
388
389 bool
hasValidRoute(std::string & msg,const MSRoute * route) const390 MSBaseVehicle::hasValidRoute(std::string& msg, const MSRoute* route) const {
391 MSRouteIterator start = myCurrEdge;
392 if (route == nullptr) {
393 route = myRoute;
394 } else {
395 start = route->begin();
396 }
397 MSRouteIterator last = route->end() - 1;
398 // check connectivity, first
399 for (MSRouteIterator e = start; e != last; ++e) {
400 if ((*e)->allowedLanes(**(e + 1), myType->getVehicleClass()) == nullptr) {
401 msg = "No connection between edge '" + (*e)->getID() + "' and edge '" + (*(e + 1))->getID() + "'.";
402 return false;
403 }
404 }
405 last = route->end();
406 // check usable lanes, then
407 for (MSRouteIterator e = start; e != last; ++e) {
408 if ((*e)->prohibits(this)) {
409 msg = "Edge '" + (*e)->getID() + "' prohibits.";
410 return false;
411 }
412 }
413 return true;
414 }
415
416
417 void
addReminder(MSMoveReminder * rem)418 MSBaseVehicle::addReminder(MSMoveReminder* rem) {
419 #ifdef _DEBUG
420 if (myTraceMoveReminders) {
421 traceMoveReminder("add", rem, 0, true);
422 }
423 #endif
424 myMoveReminders.push_back(std::make_pair(rem, 0.));
425 }
426
427
428 void
removeReminder(MSMoveReminder * rem)429 MSBaseVehicle::removeReminder(MSMoveReminder* rem) {
430 for (MoveReminderCont::iterator r = myMoveReminders.begin(); r != myMoveReminders.end(); ++r) {
431 if (r->first == rem) {
432 #ifdef _DEBUG
433 if (myTraceMoveReminders) {
434 traceMoveReminder("remove", rem, 0, false);
435 }
436 #endif
437 myMoveReminders.erase(r);
438 return;
439 }
440 }
441 }
442
443
444 void
activateReminders(const MSMoveReminder::Notification reason,const MSLane * enteredLane)445 MSBaseVehicle::activateReminders(const MSMoveReminder::Notification reason, const MSLane* enteredLane) {
446 for (MoveReminderCont::iterator rem = myMoveReminders.begin(); rem != myMoveReminders.end();) {
447 if (rem->first->notifyEnter(*this, reason, enteredLane)) {
448 #ifdef _DEBUG
449 if (myTraceMoveReminders) {
450 traceMoveReminder("notifyEnter", rem->first, rem->second, true);
451 }
452 #endif
453 ++rem;
454 } else {
455 #ifdef _DEBUG
456 if (myTraceMoveReminders) {
457 traceMoveReminder("notifyEnter", rem->first, rem->second, false);
458 }
459 #endif
460 rem = myMoveReminders.erase(rem);
461 }
462 }
463 }
464
465
466 void
calculateArrivalParams()467 MSBaseVehicle::calculateArrivalParams() {
468 if (myRoute->getLastEdge()->isTazConnector()) {
469 return;
470 }
471 const std::vector<MSLane*>& lanes = myRoute->getLastEdge()->getLanes();
472 const double lastLaneLength = lanes[0]->getLength();
473 switch (myParameter->arrivalPosProcedure) {
474 case ARRIVAL_POS_GIVEN:
475 if (fabs(myParameter->arrivalPos) > lastLaneLength) {
476 WRITE_WARNING("Vehicle '" + getID() + "' will not be able to arrive at the given position!");
477 }
478 // Maybe we should warn the user about invalid inputs!
479 myArrivalPos = MIN2(myParameter->arrivalPos, lastLaneLength);
480 if (myArrivalPos < 0) {
481 myArrivalPos = MAX2(myArrivalPos + lastLaneLength, 0.);
482 }
483 break;
484 case ARRIVAL_POS_RANDOM:
485 myArrivalPos = RandHelper::rand(lastLaneLength);
486 break;
487 case ARRIVAL_POS_CENTER:
488 myArrivalPos = lastLaneLength / 2.;
489 break;
490 default:
491 myArrivalPos = lastLaneLength;
492 break;
493 }
494 if (myParameter->arrivalLaneProcedure == ARRIVAL_LANE_GIVEN) {
495 if (myParameter->arrivalLane >= (int)lanes.size() || !lanes[myParameter->arrivalLane]->allowsVehicleClass(myType->getVehicleClass())) {
496 WRITE_WARNING("Vehicle '" + getID() + "' will not be able to arrive at the given lane '" + myRoute->getLastEdge()->getID() + "_" + toString(myParameter->arrivalLane) + "'!");
497 }
498 myArrivalLane = MIN2(myParameter->arrivalLane, (int)(lanes.size() - 1));
499 }
500 if (myParameter->arrivalSpeedProcedure == ARRIVAL_SPEED_GIVEN) {
501 for (std::vector<MSLane*>::const_iterator l = lanes.begin(); l != lanes.end(); ++l) {
502 if (myParameter->arrivalSpeed <= (*l)->getVehicleMaxSpeed(this)) {
503 return;
504 }
505 }
506 WRITE_WARNING("Vehicle '" + getID() + "' will not be able to arrive with the given speed!");
507 }
508 }
509
510
511 double
getImpatience() const512 MSBaseVehicle::getImpatience() const {
513 return MAX2(0., MIN2(1., getVehicleType().getImpatience() +
514 (MSGlobals::gTimeToImpatience > 0 ? (double)getWaitingTime() / MSGlobals::gTimeToImpatience : 0)));
515 }
516
517
518 MSVehicleDevice*
getDevice(const std::type_info & type) const519 MSBaseVehicle::getDevice(const std::type_info& type) const {
520 for (MSVehicleDevice* const dev : myDevices) {
521 if (typeid(*dev) == type) {
522 return dev;
523 }
524 }
525 return nullptr;
526 }
527
528
529 void
saveState(OutputDevice & out)530 MSBaseVehicle::saveState(OutputDevice& out) {
531 // this saves lots of departParameters which are only needed for vehicles that did not yet depart
532 // the parameters may hold the name of a vTypeDistribution but we are interested in the actual type
533 myParameter->write(out, OptionsCont::getOptions(), SUMO_TAG_VEHICLE, getVehicleType().getID());
534 // params and stops must be written in child classes since they may wish to add additional attributes first
535 out.writeAttr(SUMO_ATTR_ROUTE, myRoute->getID());
536 out.writeAttr(SUMO_ATTR_SPEEDFACTOR, myChosenSpeedFactor);
537 if (myParameter->wasSet(VEHPARS_FORCE_REROUTE) && !hasDeparted()) {
538 out.writeAttr(SUMO_ATTR_REROUTE, true);
539 }
540 // here starts the vehicle internal part (see loading)
541 // @note: remember to close the vehicle tag when calling this in a subclass!
542 }
543
544
545 void
addStops(const bool ignoreStopErrors)546 MSBaseVehicle::addStops(const bool ignoreStopErrors) {
547 for (std::vector<SUMOVehicleParameter::Stop>::const_iterator i = myRoute->getStops().begin(); i != myRoute->getStops().end(); ++i) {
548 std::string errorMsg;
549 if (!addStop(*i, errorMsg, myParameter->depart) && !ignoreStopErrors) {
550 throw ProcessError(errorMsg);
551 }
552 if (errorMsg != "") {
553 WRITE_WARNING(errorMsg);
554 }
555 }
556 const SUMOTime untilOffset = myParameter->repetitionOffset > 0 ? myParameter->repetitionsDone * myParameter->repetitionOffset : 0;
557 for (std::vector<SUMOVehicleParameter::Stop>::const_iterator i = myParameter->stops.begin(); i != myParameter->stops.end(); ++i) {
558 std::string errorMsg;
559 if (!addStop(*i, errorMsg, untilOffset) && !ignoreStopErrors) {
560 throw ProcessError(errorMsg);
561 }
562 if (errorMsg != "") {
563 WRITE_WARNING(errorMsg);
564 }
565 }
566 }
567
568
569 int
getPersonNumber() const570 MSBaseVehicle::getPersonNumber() const {
571 int boarded = myPersonDevice == nullptr ? 0 : myPersonDevice->size();
572 return boarded + myParameter->personNumber;
573 }
574
575 std::vector<std::string>
getPersonIDList() const576 MSBaseVehicle::getPersonIDList() const {
577 std::vector<std::string> ret;
578 const std::vector<MSTransportable*>& persons = getPersons();
579 for (std::vector<MSTransportable*>::const_iterator it_p = persons.begin(); it_p != persons.end(); ++it_p) {
580 ret.push_back((*it_p)->getID());
581 }
582 return ret;
583 }
584
585 int
getContainerNumber() const586 MSBaseVehicle::getContainerNumber() const {
587 int loaded = myContainerDevice == nullptr ? 0 : myContainerDevice->size();
588 return loaded + myParameter->containerNumber;
589 }
590
591
592 void
removeTransportable(MSTransportable * t)593 MSBaseVehicle::removeTransportable(MSTransportable* t) {
594 // this might be called from the MSTransportable destructor so we cannot do a dynamic cast to determine the type
595 if (myPersonDevice != nullptr) {
596 myPersonDevice->removeTransportable(t);
597 }
598 if (myContainerDevice != nullptr) {
599 myContainerDevice->removeTransportable(t);
600 }
601 }
602
603
604 const std::vector<MSTransportable*>&
getPersons() const605 MSBaseVehicle::getPersons() const {
606 if (myPersonDevice == nullptr) {
607 return myEmptyTransportableVector;
608 } else {
609 return myPersonDevice->getTransportables();
610 }
611 }
612
613
614 const std::vector<MSTransportable*>&
getContainers() const615 MSBaseVehicle::getContainers() const {
616 if (myContainerDevice == nullptr) {
617 return myEmptyTransportableVector;
618 } else {
619 return myContainerDevice->getTransportables();
620 }
621 }
622
623
624
625 bool
hasDevice(const std::string & deviceName) const626 MSBaseVehicle::hasDevice(const std::string& deviceName) const {
627 for (MSDevice* const dev : myDevices) {
628 if (dev->deviceName() == deviceName) {
629 return true;
630 }
631 }
632 return false;
633 }
634
635
636 void
createDevice(const std::string & deviceName)637 MSBaseVehicle::createDevice(const std::string& deviceName) {
638 if (!hasDevice(deviceName)) {
639 if (deviceName == "rerouting") {
640 ((SUMOVehicleParameter*)myParameter)->setParameter("has." + deviceName + ".device", "true");
641 MSDevice_Routing::buildVehicleDevices(*this, myDevices);
642 if (hasDeparted()) {
643 // vehicle already departed: disable pre-insertion rerouting and enable regular routing behavior
644 MSDevice_Routing* routingDevice = static_cast<MSDevice_Routing*>(getDevice(typeid(MSDevice_Routing)));
645 assert(routingDevice != 0);
646 routingDevice->notifyEnter(*this, MSMoveReminder::NOTIFICATION_DEPARTED);
647 }
648 } else {
649 throw InvalidArgument("Creating device of type '" + deviceName + "' is not supported");
650 }
651 }
652 }
653
654
655 std::string
getDeviceParameter(const std::string & deviceName,const std::string & key) const656 MSBaseVehicle::getDeviceParameter(const std::string& deviceName, const std::string& key) const {
657 for (MSVehicleDevice* const dev : myDevices) {
658 if (dev->deviceName() == deviceName) {
659 return dev->getParameter(key);
660 }
661 }
662 throw InvalidArgument("No device of type '" + deviceName + "' exists");
663 }
664
665
666 void
setDeviceParameter(const std::string & deviceName,const std::string & key,const std::string & value)667 MSBaseVehicle::setDeviceParameter(const std::string& deviceName, const std::string& key, const std::string& value) {
668 for (MSVehicleDevice* const dev : myDevices) {
669 if (dev->deviceName() == deviceName) {
670 dev->setParameter(key, value);
671 return;
672 }
673 }
674 throw InvalidArgument("No device of type '" + deviceName + "' exists");
675 }
676
677
678 void
replaceVehicleType(MSVehicleType * type)679 MSBaseVehicle::replaceVehicleType(MSVehicleType* type) {
680 assert(type != nullptr);
681 if (myType->isVehicleSpecific() && type != myType) {
682 MSNet::getInstance()->getVehicleControl().removeVType(myType);
683 }
684 myType = type;
685 }
686
687
688 MSVehicleType&
getSingularType()689 MSBaseVehicle::getSingularType() {
690 if (myType->isVehicleSpecific()) {
691 return *myType;
692 }
693 MSVehicleType* type = myType->buildSingularType(myType->getID() + "@" + getID());
694 replaceVehicleType(type);
695 return *type;
696 }
697
698 std::mt19937*
getRNG() const699 MSBaseVehicle::getRNG() const {
700 const MSLane* lane = getLane();
701 if (lane == nullptr) {
702 return getEdge()->getLanes()[0]->getRNG();
703 } else {
704 return lane->getRNG();
705 }
706 }
707
708 #ifdef _DEBUG
709 void
initMoveReminderOutput(const OptionsCont & oc)710 MSBaseVehicle::initMoveReminderOutput(const OptionsCont& oc) {
711 if (oc.isSet("movereminder-output.vehicles")) {
712 const std::vector<std::string> vehicles = oc.getStringVector("movereminder-output.vehicles");
713 myShallTraceMoveReminders.insert(vehicles.begin(), vehicles.end());
714 }
715 }
716
717
718 void
traceMoveReminder(const std::string & type,MSMoveReminder * rem,double pos,bool keep) const719 MSBaseVehicle::traceMoveReminder(const std::string& type, MSMoveReminder* rem, double pos, bool keep) const {
720 OutputDevice& od = OutputDevice::getDeviceByOption("movereminder-output");
721 od.openTag("movereminder");
722 od.writeAttr(SUMO_ATTR_TIME, STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep()));
723 od.writeAttr("veh", getID());
724 od.writeAttr(SUMO_ATTR_ID, rem->getDescription());
725 od.writeAttr("type", type);
726 od.writeAttr("pos", toString(pos));
727 od.writeAttr("keep", toString(keep));
728 od.closeTag();
729 }
730 #endif
731
732 /****************************************************************************/
733
734