LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Taxi.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.9 % 397 361
Test Date: 2025-11-13 15:38:19 Functions: 100.0 % 34 34

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    MSDevice_Taxi.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    16.12.2019
      17              : ///
      18              : // A device which controls a taxi
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <utils/common/StringUtils.h>
      23              : #include <utils/common/StaticCommand.h>
      24              : #include <utils/common/StringTokenizer.h>
      25              : #include <utils/options/OptionsCont.h>
      26              : #include <utils/iodevices/OutputDevice.h>
      27              : #include <utils/vehicle/SUMOVehicle.h>
      28              : #include <utils/router/SUMOAbstractRouter.h>
      29              : #include <microsim/transportables/MSTransportable.h>
      30              : #include <microsim/MSEventControl.h>
      31              : #include <microsim/MSGlobals.h>
      32              : #include <microsim/MSVehicle.h>
      33              : #include <microsim/MSEdge.h>
      34              : #include <microsim/MSLane.h>
      35              : #include <microsim/MSStop.h>
      36              : #include <microsim/MSStoppingPlace.h>
      37              : #include <microsim/trigger/MSTriggeredRerouter.h>
      38              : 
      39              : #include "MSDispatch.h"
      40              : #include "MSDispatch_Greedy.h"
      41              : #include "MSDispatch_GreedyShared.h"
      42              : #include "MSDispatch_RouteExtension.h"
      43              : #include "MSDispatch_TraCI.h"
      44              : 
      45              : #include "MSIdling.h"
      46              : 
      47              : #include "MSRoutingEngine.h"
      48              : #include "MSDevice_Routing.h"
      49              : #include "MSDevice_Taxi.h"
      50              : 
      51              : //#define DEBUG_DISPATCH
      52              : //#define DEBUG_CANCEL
      53              : 
      54              : //#define DEBUG_COND (myHolder.isSelected())
      55              : #define DEBUG_COND (true)
      56              : 
      57              : // ===========================================================================
      58              : // static member variables
      59              : // ===========================================================================
      60              : SUMOTime MSDevice_Taxi::myDispatchPeriod(0);
      61              : /// @brief the dispatch algorithm
      62              : MSDispatch* MSDevice_Taxi::myDispatcher(nullptr);
      63              : /// @brief The repeated call to the dispatcher
      64              : Command* MSDevice_Taxi::myDispatchCommand(nullptr);
      65              : // @brief the list of available taxis
      66              : std::vector<MSDevice_Taxi*> MSDevice_Taxi::myFleet;
      67              : int MSDevice_Taxi::myMaxCapacity(0);
      68              : int MSDevice_Taxi::myMaxContainerCapacity(0);
      69              : std::set<std::string> MSDevice_Taxi::myVClassWarningVTypes;
      70              : 
      71              : #define TAXI_SERVICE "taxi"
      72              : #define TAXI_SERVICE_PREFIX "taxi:"
      73              : 
      74              : // ===========================================================================
      75              : // method definitions
      76              : // ===========================================================================
      77              : // ---------------------------------------------------------------------------
      78              : // static initialisation methods
      79              : // ---------------------------------------------------------------------------
      80              : void
      81        39784 : MSDevice_Taxi::insertOptions(OptionsCont& oc) {
      82        39784 :     oc.addOptionSubTopic("Taxi Device");
      83        79568 :     insertDefaultAssignmentOptions("taxi", "Taxi Device", oc);
      84              : 
      85        79568 :     oc.doRegister("device.taxi.dispatch-algorithm", new Option_String("greedy"));
      86        79568 :     oc.addDescription("device.taxi.dispatch-algorithm", "Taxi Device", TL("The dispatch algorithm [greedy|greedyClosest|greedyShared|routeExtension|traci]"));
      87              : 
      88        39784 :     oc.doRegister("device.taxi.dispatch-algorithm.output", new Option_FileName());
      89        79568 :     oc.addDescription("device.taxi.dispatch-algorithm.output", "Taxi Device", TL("Write information from the dispatch algorithm to FILE"));
      90              : 
      91        79568 :     oc.doRegister("device.taxi.dispatch-algorithm.params", new Option_String(""));
      92        79568 :     oc.addDescription("device.taxi.dispatch-algorithm.params", "Taxi Device", TL("Load dispatch algorithm parameters in format KEY1:VALUE1[,KEY2:VALUE]"));
      93              : 
      94        79568 :     oc.doRegister("device.taxi.dispatch-period", new Option_String("60", "TIME"));
      95        79568 :     oc.addDescription("device.taxi.dispatch-period", "Taxi Device", TL("The period between successive calls to the dispatcher"));
      96              : 
      97        79568 :     oc.doRegister("device.taxi.dispatch-keep-unreachable", new Option_String("3600", "TIME"));
      98        79568 :     oc.addDescription("device.taxi.dispatch-keep-unreachable", "Taxi Device", TL("The time before aborting unreachable reservations"));
      99              : 
     100        79568 :     oc.doRegister("device.taxi.idle-algorithm", new Option_String("stop"));
     101        79568 :     oc.addDescription("device.taxi.idle-algorithm", "Taxi Device", TL("The behavior of idle taxis [stop|randomCircling|taxistand]"));
     102              : 
     103        39784 :     oc.doRegister("device.taxi.idle-algorithm.output", new Option_FileName());
     104        79568 :     oc.addDescription("device.taxi.idle-algorithm.output", "Taxi Device", TL("Write information from the idling algorithm to FILE"));
     105        39784 : }
     106              : 
     107              : 
     108              : void
     109      5371069 : MSDevice_Taxi::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
     110      5371069 :     OptionsCont& oc = OptionsCont::getOptions();
     111     10742138 :     if (equippedByDefaultAssignmentOptions(oc, "taxi", v, false)) {
     112              :         // build the device
     113         6572 :         MSDevice_Taxi* device = new MSDevice_Taxi(v, "taxi_" + v.getID());
     114         6560 :         into.push_back(device);
     115         6560 :         myFleet.push_back(device);
     116         6560 :         if (v.getParameter().line == "") {
     117              :             // automatically set the line so that persons are willing to enter
     118              :             // (see MSStageDriving::isWaitingFor)
     119         6463 :             const_cast<SUMOVehicleParameter&>(v.getParameter()).line = TAXI_SERVICE;
     120              :         }
     121         6560 :         if (v.getVClass() != SVC_TAXI && myVClassWarningVTypes.count(v.getVehicleType().getID()) == 0) {
     122          162 :             WRITE_WARNINGF(TL("Vehicle '%' with device.taxi should have vClass taxi instead of '%'."), v.getID(), toString(v.getVClass()));
     123           54 :             myVClassWarningVTypes.insert(v.getVehicleType().getID());
     124              :         }
     125         6560 :         const int personCapacity = v.getVehicleType().getPersonCapacity();
     126         6560 :         const int containerCapacity = v.getVehicleType().getContainerCapacity();
     127         6560 :         myMaxCapacity = MAX2(myMaxCapacity, personCapacity);
     128         6560 :         myMaxContainerCapacity = MAX2(myMaxContainerCapacity, containerCapacity);
     129         6560 :         if (personCapacity < 1 && containerCapacity < 1) {
     130            0 :             WRITE_WARNINGF(TL("Vehicle '%' with personCapacity % and containerCapacity % is not usable as taxi."), v.getID(), toString(personCapacity), toString(containerCapacity));
     131              :         }
     132              :     }
     133      5371063 : }
     134              : 
     135              : 
     136              : void
     137          773 : MSDevice_Taxi::initDispatch() {
     138          773 :     OptionsCont& oc = OptionsCont::getOptions();
     139          773 :     myDispatchPeriod = string2time(oc.getString("device.taxi.dispatch-period"));
     140              :     // init dispatch algorithm
     141          773 :     std::string algo = oc.getString("device.taxi.dispatch-algorithm");
     142          773 :     Parameterised params;
     143         1546 :     params.setParametersStr(OptionsCont::getOptions().getString("device.taxi.dispatch-algorithm.params"), ":", ",");
     144          773 :     if (algo == "greedy") {
     145          468 :         myDispatcher = new MSDispatch_Greedy(params.getParametersMap());
     146          305 :     } else if (algo == "greedyClosest") {
     147           36 :         myDispatcher = new MSDispatch_GreedyClosest(params.getParametersMap());
     148          287 :     } else if (algo == "greedyShared") {
     149          114 :         myDispatcher = new MSDispatch_GreedyShared(params.getParametersMap());
     150          173 :     } else if (algo == "routeExtension") {
     151          122 :         myDispatcher = new MSDispatch_RouteExtension(params.getParametersMap());
     152          112 :     } else if (algo == "traci") {
     153          224 :         myDispatcher = new MSDispatch_TraCI(params.getParametersMap());
     154              :     } else {
     155            0 :         throw ProcessError(TLF("Dispatch algorithm '%' is not known", algo));
     156              :     }
     157          773 :     myDispatchCommand = new StaticCommand<MSDevice_Taxi>(&MSDevice_Taxi::triggerDispatch);
     158              :     // round to next multiple of myDispatchPeriod
     159          773 :     const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
     160          773 :     const SUMOTime begin = string2time(oc.getString("begin"));
     161          773 :     const SUMOTime delay = (myDispatchPeriod - ((now - begin) % myDispatchPeriod)) % myDispatchPeriod;
     162          773 :     MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myDispatchCommand, now + delay);
     163         1546 : }
     164              : 
     165              : bool
     166        79604 : MSDevice_Taxi::isReservation(const std::set<std::string>& lines) {
     167        79604 :     return lines.size() == 1 && (
     168        79590 :                *lines.begin() == TAXI_SERVICE
     169       154096 :                || StringUtils::startsWith(*lines.begin(), TAXI_SERVICE_PREFIX));
     170              : }
     171              : 
     172              : void
     173         2609 : MSDevice_Taxi::addReservation(MSTransportable* person,
     174              :                               const std::set<std::string>& lines,
     175              :                               SUMOTime reservationTime,
     176              :                               SUMOTime pickupTime,
     177              :                               SUMOTime earliestPickupTime,
     178              :                               const MSEdge* from, double fromPos,
     179              :                               const MSStoppingPlace* fromStop,
     180              :                               const MSEdge* to, double toPos,
     181              :                               const MSStoppingPlace* toStop,
     182              :                               const std::string& group) {
     183         2609 :     if (!isReservation(lines)) {
     184              :         return;
     185              :     }
     186         2609 :     if ((to->getPermissions() & SVC_TAXI) == 0) {
     187            0 :         throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
     188            0 :                            + " '" + person->getID() + "' because destination edge '" + to->getID() + "'"
     189            0 :                            + " does not permit taxi access");
     190              :     }
     191         2609 :     if ((from->getPermissions() & SVC_TAXI) == 0) {
     192            0 :         throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
     193            0 :                            + " '" + person->getID() + "' because origin edge '" + from->getID() + "'"
     194            0 :                            + " does not permit taxi access");
     195              :     }
     196         2609 :     if (myDispatchCommand == nullptr) {
     197          773 :         initDispatch();
     198              :     }
     199         2609 :     if (fromStop != nullptr && &fromStop->getLane().getEdge() == from) {
     200              :         // pickup position should be at the stop-endPos
     201          885 :         fromPos = fromStop->getEndLanePosition();
     202              :     }
     203         5218 :     myDispatcher->addReservation(person, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, *lines.begin(), myMaxCapacity, myMaxContainerCapacity);
     204              : }
     205              : 
     206              : void
     207           93 : MSDevice_Taxi::removeReservation(MSTransportable* person,
     208              :                                  const std::set<std::string>& lines,
     209              :                                  const MSEdge* from, double fromPos,
     210              :                                  const MSEdge* to, double toPos,
     211              :                                  const std::string& group) {
     212           93 :     if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
     213          154 :         myDispatcher->removeReservation(person, from, fromPos, to, toPos, group);
     214              :     }
     215           93 : }
     216              : 
     217              : void
     218          162 : MSDevice_Taxi::updateReservationFromPos(MSTransportable* person,
     219              :                                         const std::set<std::string>& lines,
     220              :                                         const MSEdge* from, double fromPos,
     221              :                                         const MSEdge* to, double toPos,
     222              :                                         const std::string& group, double newFromPos) {
     223          162 :     if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
     224          300 :         myDispatcher->updateReservationFromPos(person, from, fromPos, to, toPos, group, newFromPos);
     225              :     }
     226          162 : }
     227              : 
     228              : 
     229              : SUMOTime
     230        13038 : MSDevice_Taxi::triggerDispatch(SUMOTime currentTime) {
     231              :     std::vector<MSDevice_Taxi*> active;
     232        33841 :     for (MSDevice_Taxi* taxi : myFleet) {
     233        20803 :         if (taxi->getHolder().hasDeparted()) {
     234        20705 :             active.push_back(taxi);
     235              :         }
     236              :     }
     237        13038 :     myDispatcher->computeDispatch(currentTime, active);
     238        13036 :     return myDispatchPeriod;
     239        13038 : }
     240              : 
     241              : bool
     242      3498633 : MSDevice_Taxi::hasServableReservations() {
     243      3498633 :     return myDispatcher != nullptr && myDispatcher->hasServableReservations();
     244              : }
     245              : 
     246              : void
     247        38670 : MSDevice_Taxi::cleanup() {
     248        38670 :     if (myDispatcher != nullptr) {
     249          773 :         delete myDispatcher;
     250          773 :         myDispatcher = nullptr;
     251              :     }
     252        38670 :     myDispatchCommand = nullptr;
     253              :     myVClassWarningVTypes.clear();
     254        38670 : }
     255              : 
     256              : // ---------------------------------------------------------------------------
     257              : // MSDevice_Taxi-methods
     258              : // ---------------------------------------------------------------------------
     259         6566 : MSDevice_Taxi::MSDevice_Taxi(SUMOVehicle& holder, const std::string& id) :
     260         6566 :     MSVehicleDevice(holder, id) {
     261         6566 :     std::string defaultServiceEnd = toString(1e15);
     262        13138 :     const std::string algo = holder.getStringParam("device.taxi.idle-algorithm");
     263         6566 :     if (algo == "stop") {
     264         6400 :         myIdleAlgorithm = new MSIdling_Stop();
     265          166 :     } else if (algo == "randomCircling") {
     266           82 :         myIdleAlgorithm = new MSIdling_RandomCircling();
     267              :         // make sure simulation terminates
     268           82 :         defaultServiceEnd = toString(STEPS2TIME(
     269              :                                          myHolder.getParameter().departProcedure == DepartDefinition::GIVEN
     270              :                                          ? myHolder.getParameter().depart
     271          164 :                                          : MSNet::getInstance()->getCurrentTimeStep()) + (3600 * 8));
     272           84 :     } else if (algo == "taxistand") {
     273          156 :         const std::string rerouterID = holder.getStringParam("device.taxi.stands-rerouter");
     274           78 :         if (rerouterID.empty()) {
     275            0 :             throw ProcessError("Idle algorithm '" + algo + "' requires a rerouter id to be defined using device param 'stands-rerouter' for vehicle '" + myHolder.getID() + "'");
     276              :         }
     277              :         if (MSTriggeredRerouter::getInstances().count(rerouterID) == 0) {
     278            0 :             throw ProcessError("Unknown rerouter '" + rerouterID + "' when loading taxi stands for vehicle '" + myHolder.getID() + "'");
     279              :         }
     280           78 :         MSTriggeredRerouter* rerouter = MSTriggeredRerouter::getInstances().find(rerouterID)->second;
     281           78 :         myIdleAlgorithm = new MSIdling_TaxiStand(rerouter);
     282              :     } else {
     283           18 :         throw ProcessError("Idle algorithm '" + algo + "' is not known for vehicle '" + myHolder.getID() + "'");
     284              :     }
     285         6566 :     myServiceEnd = string2time(holder.getStringParam("device.taxi.end", false, defaultServiceEnd));
     286         6560 :     myRoutingDevice = static_cast<MSDevice_Routing*>(myHolder.getDevice(typeid(MSDevice_Routing)));
     287         6566 : }
     288              : 
     289              : 
     290        13120 : MSDevice_Taxi::~MSDevice_Taxi() {
     291         6560 :     myFleet.erase(std::find(myFleet.begin(), myFleet.end(), this));
     292              :     // recompute myMaxCapacity
     293         6560 :     myMaxCapacity = 0;
     294         6560 :     myMaxContainerCapacity = 0;
     295        16714 :     for (MSDevice_Taxi* taxi : myFleet) {
     296        10154 :         myMaxCapacity = MAX2(myMaxCapacity, taxi->getHolder().getVehicleType().getPersonCapacity());
     297        10154 :         myMaxContainerCapacity = MAX2(myMaxContainerCapacity, taxi->getHolder().getVehicleType().getContainerCapacity());
     298              :     }
     299         6560 :     delete myIdleAlgorithm;
     300        13120 : }
     301              : 
     302              : 
     303              : bool
     304        10904 : MSDevice_Taxi::hasFleet() {
     305        10904 :     return myFleet.size() > 0;;
     306              : }
     307              : 
     308              : 
     309              : void
     310         1148 : MSDevice_Taxi::dispatch(const Reservation& res) {
     311         1148 :     dispatchShared({&res, &res});
     312         1146 : }
     313              : 
     314              : 
     315              : void
     316         1489 : MSDevice_Taxi::dispatchShared(std::vector<const Reservation*> reservations) {
     317              : #ifdef DEBUG_DISPATCH
     318              :     if (DEBUG_COND) {
     319              :         std::cout << SIMTIME << " taxi=" << myHolder.getID() << " dispatch:\n";
     320              :         for (const Reservation* res : reservations) {
     321              :             std::cout << "   persons=" << toString(res->persons) << "\n";
     322              :         }
     323              :     }
     324              : #endif
     325         1489 :     myLastDispatch = reservations;
     326              :     ConstMSEdgeVector tmpEdges;
     327              :     StopParVector stops;
     328         1489 :     double lastPos = myHolder.getPositionOnLane();
     329         1489 :     const MSEdge* rerouteOrigin = *myHolder.getRerouteOrigin();
     330         1489 :     if (isEmpty()) {
     331              :         // start fresh from the current edge
     332         1398 :         if (myHolder.isStoppedParking()) {
     333              :             // parking stop must be ended normally
     334          732 :             MSStop& stop = myHolder.getNextStopMutable();
     335          732 :             stop.duration = 0;
     336          732 :             lastPos = stop.pars.endPos;
     337          732 :             if (myHolder.isStoppedTriggered()) {
     338          624 :                 stop.triggered = false;
     339          624 :                 stop.containerTriggered = false;
     340          624 :                 stop.joinTriggered = false;
     341          624 :                 myHolder.unregisterWaiting();
     342              :             }
     343              :             // prevent unauthorized/premature entry
     344          732 :             const_cast<SUMOVehicleParameter::Stop&>(stop.pars).permitted.insert("");
     345          756 :             while (myHolder.getStops().size() > 1) {
     346           24 :                 myHolder.abortNextStop(1);
     347              :             }
     348              :         } else {
     349         1237 :             while (myHolder.hasStops()) {
     350              :                 // in meso there might be more than 1 stop at this point
     351          571 :                 myHolder.abortNextStop();
     352              :             }
     353              :             assert(!myHolder.hasStops());
     354              :         }
     355         1398 :         tmpEdges.push_back(myHolder.getEdge());
     356         1398 :         if (myHolder.getEdge() != rerouteOrigin) {
     357            8 :             tmpEdges.push_back(rerouteOrigin);
     358              :         }
     359              :     } else {
     360              :         assert(myHolder.hasStops());
     361              :         // check how often existing customers appear in the new reservations
     362              :         std::map<const MSTransportable*, int> nOccur;
     363          629 :         for (const Reservation* res : reservations) {
     364         1076 :             for (const MSTransportable* person : res->persons) {
     365              :                 if (myCustomers.count(person) != 0) {
     366          298 :                     nOccur[person] += 1;
     367              :                     if (myCurrentReservations.count(res) == 0) {
     368            0 :                         throw ProcessError(TLF("Invalid Re-dispatch for existing customer '%' with a new reservation", person->getID()));
     369              :                     }
     370              :                 }
     371              :             }
     372              :         }
     373              : #ifdef DEBUG_DISPATCH
     374              :         if (DEBUG_COND) {
     375              :             for (auto item : nOccur) {
     376              :                 std::cout << "   previousCustomer=" << item.first->getID() << " occurs=" << item.second << "\n";
     377              :             }
     378              :         }
     379              : #endif
     380           91 :         if (nOccur.size() == 0) {
     381              :             // no overlap with existing customers - extend route
     382           10 :             tmpEdges = myHolder.getRoute().getEdges();
     383           10 :             lastPos = myHolder.getStops().back().pars.endPos;
     384              : #ifdef DEBUG_DISPATCH
     385              :             if (DEBUG_COND) {
     386              :                 std::cout << " re-dispatch with route-extension\n";
     387              :             }
     388              : #endif
     389           81 :         } else if (nOccur.size() == myCustomers.size()) {
     390              :             // redefine route (verify correct number of mentions)
     391              :             std::set<const MSTransportable*> onBoard;
     392           81 :             const std::vector<MSTransportable*>& onBoardP = myHolder.getPersons();
     393           81 :             const std::vector<MSTransportable*>& onBoardC = myHolder.getContainers();
     394              :             onBoard.insert(onBoardP.begin(), onBoardP.end());
     395              :             onBoard.insert(onBoardC.begin(), onBoardC.end());
     396              :             std::set<const MSTransportable*> redundantPickup;
     397          244 :             for (auto item : nOccur) {
     398          167 :                 if (item.second == 1) {
     399              :                     // customers must already be on board
     400              :                     if (onBoard.count(item.first) == 0) {
     401           12 :                         throw ProcessError(TLF("Re-dispatch did not mention pickup for existing customer '%'", item.first->getID()));
     402              :                     }
     403          131 :                 } else if (item.second == 2) {
     404              :                     if (onBoard.count(item.first) == 0) {
     405              :                         // treat like a new customer
     406              :                         // TODO: need to be checked
     407              :                         myCustomers.erase(item.first);
     408              :                     } else {
     409              :                         redundantPickup.insert(item.first);
     410              :                     }
     411              :                 } else {
     412            0 :                     throw ProcessError("Re-dispatch mentions existing customer '" + item.first->getID() + "' " + toString(item.second) + " times");
     413              :                 }
     414              :             }
     415              :             // remove redundancy
     416           77 :             if (!redundantPickup.empty()) {
     417          183 :                 for (auto it = reservations.begin(); it != reservations.end();) {
     418              :                     bool isRedundant = false;
     419          262 :                     for (const MSTransportable* person : (*it)->persons) {
     420              :                         if (redundantPickup.count(person) != 0) {
     421              :                             isRedundant = true;
     422              :                             break;
     423              :                         }
     424              :                     }
     425          154 :                     if (isRedundant) {
     426           92 :                         for (const MSTransportable* person : (*it)->persons) {
     427              :                             redundantPickup.erase(person);
     428              :                         }
     429              :                         it = reservations.erase(it);
     430              :                     } else {
     431              :                         it++;
     432              :                     }
     433              :                 }
     434              :             }
     435          321 :             while (myHolder.hasStops()) {
     436          244 :                 myHolder.abortNextStop();
     437              :             }
     438           81 :             tmpEdges.push_back(myHolder.getEdge());
     439           77 :             if (myHolder.getEdge() != rerouteOrigin) {
     440           10 :                 tmpEdges.push_back(rerouteOrigin);
     441              :             }
     442              : #ifdef DEBUG_DISPATCH
     443              :             if (DEBUG_COND) {
     444              :                 std::cout << " re-dispatch from scratch\n";
     445              :             }
     446              : #endif
     447              :         } else {
     448              :             // inconsistent re-dispatch
     449              :             std::vector<std::string> missing;
     450            0 :             for (const MSTransportable* c : myCustomers) {
     451              :                 if (nOccur.count(c) == 0) {
     452            0 :                     missing.push_back(c->getID());
     453              :                 }
     454              :             }
     455            0 :             throw ProcessError("Re-dispatch did mention some customers but failed to mention " + joinToStringSorting(missing, " "));
     456            0 :         }
     457              :     }
     458              : 
     459         1485 :     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
     460              :     bool hasPickup = false;
     461         5861 :     for (const Reservation* res : reservations) {
     462              :         myCurrentReservations.insert(res);
     463              :         bool isPickup = false;
     464         8972 :         for (const MSTransportable* person : res->persons) {
     465              :             if (myCustomers.count(person) == 0) {
     466              :                 myCustomers.insert(person);
     467              :                 isPickup = true;
     468              :                 hasPickup = true;
     469              :             }
     470              :         }
     471         4376 :         if (isPickup) {
     472         6447 :             prepareStop(tmpEdges, stops, lastPos, res->from, res->fromPos, res->fromStop, "pickup " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
     473         4408 :             for (const MSTransportable* const transportable : res->persons) {
     474         2259 :                 if (transportable->isPerson()) {
     475         2085 :                     stops.back().triggered = true;
     476              :                 } else {
     477          174 :                     stops.back().containerTriggered = true;
     478              :                 }
     479              :                 stops.back().permitted.insert(transportable->getID());
     480              :             }
     481              :             // proof this lines: Is needed for pre-booking?
     482              :             std::set<const MSTransportable*> persons = res->persons;
     483         4408 :             for (auto itr = persons.begin(); itr != persons.end(); itr++) {
     484         2259 :                 stops.back().awaitedPersons.insert((*itr)->getID());
     485              :             }
     486              : 
     487         2149 :             stops.back().parametersSet |= STOP_PERMITTED_SET;
     488         2149 :             if (stops.back().duration == -1) {
     489              :                 // keep dropOffDuration if the stop is dropOff and pickUp
     490         1797 :                 stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.pickUpDuration", false, 0));
     491              :             }
     492              :         } else {
     493         4454 :             prepareStop(tmpEdges, stops, lastPos, res->to, res->toPos, res->toStop, "dropOff " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
     494         2227 :             stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.dropOffDuration", false, 60)); // pay and collect bags
     495              :         }
     496              :     }
     497              : #ifdef DEBUG_DISPATCH
     498              :     if (DEBUG_COND) {
     499              :         std::cout << "   tmpEdges=" << toString(tmpEdges) << "\n";
     500              :     }
     501              : #endif
     502         2970 :     if (!myHolder.replaceRouteEdges(tmpEdges, -1, 0, "taxi:prepare_dispatch", false, false, false)) {
     503            0 :         throw ProcessError("Route replacement for taxi dispatch failed for vehicle '" + myHolder.getID()
     504            0 :                            + "' at time=" + time2string(t) + ".");
     505              :     }
     506              : #ifdef DEBUG_DISPATCH
     507              :     if (DEBUG_COND) std::cout << "   replacedRoute=" << toString(tmpEdges)
     508              :                                   << "\n     actualRoute=" << toString(myHolder.getRoute().getEdges()) << "\n";
     509              : #endif
     510         5008 :     for (SUMOVehicleParameter::Stop& stop : stops) {
     511              :         std::string error;
     512         3523 :         myHolder.addStop(stop, error);
     513         3523 :         if (error != "") {
     514            0 :             WRITE_WARNINGF(TL("Could not add taxi stop, time=%, error=%"), myHolder.getID(), stop.actType, time2string(t), error)
     515              :         }
     516              :     }
     517         1485 :     SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
     518              :     // SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myHolder.getInfluencer().getRouterTT(veh->getRNGIndex())
     519         1485 :     myHolder.reroute(t, "taxi:dispatch", router, false);
     520              : #ifdef DEBUG_DISPATCH
     521              :     if (DEBUG_COND) {
     522              :         std::cout << "\n      finalRoute=" << toString(myHolder.getRoute().getEdges()) << " routeIndex=" << myHolder.getRoutePosition() << "\n";
     523              :     }
     524              : #endif
     525         1483 :     if (hasPickup) {
     526         1451 :         myState |= PICKUP;
     527              :     }
     528         1495 : }
     529              : 
     530              : 
     531              : void
     532           88 : MSDevice_Taxi::cancelCurrentCustomers() {
     533              :     // check if taxi has stopped
     534           88 :     if (myHolder.getNextStopParameter() == nullptr) {
     535            0 :         return;
     536              :     }
     537              :     // find customers of the current stop
     538              :     std::set<const MSTransportable*> customersToBeRemoved;
     539              :     std::set<const MSTransportable*> onBoard;
     540           88 :     onBoard.insert(myHolder.getPersons().begin(), myHolder.getPersons().end());
     541           88 :     onBoard.insert(myHolder.getContainers().begin(), myHolder.getContainers().end());
     542          142 :     for (std::string tID : myHolder.getNextStopParameter()->permitted) {
     543          220 :         for (auto t : myCustomers) {
     544          166 :             if (t->getID() == tID && onBoard.count(t) == 0) {
     545              :                 customersToBeRemoved.insert(t);
     546              :             }
     547              :         }
     548              :     }
     549           88 :     if (!customersToBeRemoved.empty()) {
     550           90 :         WRITE_WARNINGF(TL("Taxi '%' aborts waiting for customers: % at time=%."),
     551              :                        myHolder.getID(), toString(customersToBeRemoved), time2string(SIMSTEP));
     552              :     }
     553          122 :     for (auto t : customersToBeRemoved) {
     554           34 :         cancelCustomer(t);
     555              :     }
     556              : }
     557              : 
     558              : 
     559              : bool
     560           44 : MSDevice_Taxi::cancelCustomer(const MSTransportable* t) {
     561              : #ifdef DEBUG_CANCEL
     562              :     if (DEBUG_COND) {
     563              :         std::cout << SIMTIME << " taxi=" << myHolder.getID() << " cancelCustomer " << t->getID() << "\n";
     564              :     }
     565              : #endif
     566              : 
     567              :     // is the given transportable a customer of the reservations?
     568              :     if (myCustomers.count(t) == 0) {
     569              :         return false;
     570              :     }
     571              :     myCustomers.erase(t);
     572              :     // check whether a single reservation has been fulfilled or another customer is part of the reservation
     573          132 :     for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
     574              :         bool fulfilled = false;
     575           88 :         if ((*resIt)->persons.size() == 1 && (*resIt)->persons.count(t) != 0) {
     576              :             // the reservation contains only the customer
     577              :             fulfilled = true;
     578              :         }
     579              :         if (fulfilled) {
     580              :             const Reservation* res = *resIt;
     581              :             // remove reservation from the current dispatch
     582          220 :             for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
     583          176 :                 if (*it == res) {
     584           88 :                     it = myLastDispatch.erase(it);
     585              :                 } else {
     586              :                     ++it;
     587              :                 }
     588              :             }
     589              :             // remove reservation from the served reservations
     590              :             resIt = myCurrentReservations.erase(resIt);
     591              :             // delete the reservation
     592           44 :             myDispatcher->fulfilledReservation(res);
     593              :         } else {
     594              :             ++resIt;
     595              :         }
     596              :     }
     597           44 :     myState &= ~PICKUP;  // remove state PICKUP
     598           88 :     for (const Reservation* res : myCurrentReservations) {
     599              :         // if there is another pickup in the dispatch left, add the state PICKUP
     600           44 :         if (std::count(myLastDispatch.begin(), myLastDispatch.end(), res) == 2) {
     601           44 :             myState |= PICKUP;  // add state PICKUP
     602              :         }
     603              :     }
     604              :     // we also have to clean reservations from myLastDispatch where the customers arrived in the meantime
     605          132 :     for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
     606              :         if (myCurrentReservations.count(*it) == 0) {
     607            0 :             it = myLastDispatch.erase(it);
     608              :         } else {
     609              :             ++it;
     610              :         }
     611              :     }
     612              :     // if there are reservations left, go on with the dispatch
     613              :     // in meso, wait for the next dispatch cycle to avoid updating stops in this stage
     614           44 :     if (!MSGlobals::gUseMesoSim) {
     615           36 :         dispatchShared(myLastDispatch);
     616              :     }
     617              :     return true;
     618              : }
     619              : 
     620              : 
     621              : void
     622         4376 : MSDevice_Taxi::prepareStop(ConstMSEdgeVector& edges,
     623              :                            StopParVector& stops,
     624              :                            double& lastPos, const MSEdge* stopEdge, double stopPos,
     625              :                            const MSStoppingPlace* stopPlace,
     626              :                            const std::string& action, const Reservation* res, const bool isPickup) {
     627              :     assert(!edges.empty());
     628         4376 :     if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
     629          779 :         stopPos = stopPlace->getEndLanePosition();
     630              :     }
     631         4376 :     if (stopPos < lastPos && stopPos + NUMERICAL_EPS >= lastPos) {
     632              :         stopPos = lastPos;
     633              :     }
     634              :     bool addedEdge = false;
     635              : 
     636         4376 :     if (stops.empty()) {
     637              :         // check brakeGap
     638         1465 :         double distToStop = stopPos - lastPos;
     639         1465 :         const double brakeGap = myHolder.getBrakeGap();
     640         1465 :         if (myHolder.getLane() != nullptr && myHolder.getLane()->isInternal()) {
     641            4 :             distToStop += myHolder.getLane()->getLength();
     642              :         }
     643         1465 :         if (stopEdge != edges.back()) {
     644         1213 :             distToStop += edges.back()->getLength();
     645         1213 :             if (distToStop < brakeGap) {
     646              :                 // the distance between current edge and stop edge may be small
     647           14 :                 SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
     648              :                 ConstMSEdgeVector toFirstStop;
     649            7 :                 router.compute(edges.back(), stopEdge, &myHolder, SIMSTEP, toFirstStop, true);
     650           31 :                 for (int i = 1; i < (int)toFirstStop.size() - 1; i++) {
     651           24 :                     distToStop += toFirstStop[i]->getLength();
     652              :                 }
     653            7 :             }
     654              :         }
     655         1465 :         if (distToStop < brakeGap) {
     656              :             // circle back to stopEdge
     657              :             //std::cout << SIMTIME << " taxi=" << getID() << " brakeGap=" << brakeGap << " distToStop=" << distToStop << "\n";
     658          179 :             edges.push_back(stopEdge);
     659              :             addedEdge = true;
     660              :         }
     661              :     }
     662              : 
     663         4376 :     if (stopEdge == edges.back() && !stops.empty()) {
     664         1024 :         if (stopPos >= lastPos && stopPos <= stops.back().endPos) {
     665              :             // no new stop and no adaption needed
     666          853 :             stops.back().actType += "," + action;
     667          853 :             return;
     668              :         }
     669          171 :         if (stopPos >= lastPos && stopPos <= lastPos + myHolder.getVehicleType().getLength()) {
     670              :             // stop length adaption needed
     671            0 :             stops.back().endPos = MIN2(lastPos + myHolder.getVehicleType().getLength(), stopEdge->getLength());
     672            0 :             stops.back().actType += "," + action;
     673            0 :             return;
     674              :         }
     675              :     }
     676         3523 :     if (!addedEdge && (stopEdge != edges.back() || stopPos < lastPos)) {
     677              :         //std::cout << SIMTIME << " stopPos=" << stopPos << " lastPos=" << lastPos << "\n";
     678         3118 :         edges.push_back(stopEdge);
     679              :     }
     680         3523 :     lastPos = stopPos;
     681         3523 :     SUMOVehicleParameter::Stop stop;
     682         3523 :     stop.lane = getStopLane(stopEdge, action)->getID();
     683         3523 :     if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
     684          546 :         stop.startPos = stopPlace->getBeginLanePosition();
     685          546 :         stop.endPos = stopPlace->getEndLanePosition();
     686              :         const SumoXMLTag tag = stopPlace->getElement();
     687          546 :         if (tag == SUMO_TAG_BUS_STOP || tag == SUMO_TAG_TRAIN_STOP) {
     688              :             stop.busstop = stopPlace->getID();
     689          245 :         } else if (tag == SUMO_TAG_PARKING_AREA) {
     690              :             stop.parkingarea = stopPlace->getID();
     691          240 :         } else if (tag == SUMO_TAG_CONTAINER_STOP) {
     692              :             stop.containerstop = stopPlace->getID();
     693              :         }
     694              :     } else {
     695         2977 :         stop.startPos = stopPos;
     696         5954 :         stop.endPos = MAX2(stopPos, MIN2(myHolder.getVehicleType().getLength(), stopEdge->getLength()));
     697              :     }
     698         7046 :     stop.parking = SUMOVehicleParameter::parseParkingType(myHolder.getStringParam("device.taxi.parking", false, "true"));
     699              :     stop.actType = action;
     700         3523 :     stop.index = STOP_INDEX_END;
     701              :     // In case of prebooking if person is not there/ comes to late for pickup set maximum waiting time:
     702         3523 :     SUMOTime earliestPickupTime = res->earliestPickupTime;
     703         3523 :     if (isPickup && earliestPickupTime >= 0) {
     704          138 :         stop.waitUntil = earliestPickupTime;
     705              :         // TODO: replace hard coded extension with parameter
     706          138 :         stop.extension = static_cast<SUMOTime>(3 * 60 * 1000);  // 3mins
     707              :     }
     708         3523 :     stops.push_back(stop);
     709         3523 : }
     710              : 
     711              : 
     712              : MSLane*
     713         3523 : MSDevice_Taxi::getStopLane(const MSEdge* edge, const std::string& action) {
     714         3523 :     const std::vector<MSLane*>* allowedLanes = edge->allowedLanes(myHolder.getVClass());
     715         3523 :     if (allowedLanes == nullptr) {
     716            0 :         throw ProcessError("Taxi vehicle '" + myHolder.getID() + "' cannot stop on edge '" + edge->getID() + "' (" + action + ")");
     717              :     }
     718         3523 :     return allowedLanes->front();
     719              : }
     720              : 
     721              : bool
     722       449520 : MSDevice_Taxi::isEmpty() {
     723       449520 :     return myState == EMPTY;
     724              : }
     725              : 
     726              : 
     727              : bool
     728        27070 : MSDevice_Taxi::allowsBoarding(const MSTransportable* t) const {
     729        27070 :     return myCustomers.count(t) != 0;
     730              : }
     731              : 
     732              : 
     733              : void
     734       358948 : MSDevice_Taxi::updateMove(const SUMOTime traveltime, const double travelledDist) {
     735       358948 :     if (myHolder.getPersonNumber() > 0 || myHolder.getContainerNumber() > 0) {
     736        99331 :         myOccupiedDistance += travelledDist;
     737        99331 :         myOccupiedTime += traveltime;
     738              :     }
     739       358948 :     if (isEmpty()) {
     740       215599 :         if (MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     741       213765 :             myIdleAlgorithm->idle(this);
     742       213765 :             if (myRoutingDevice != nullptr) {
     743              :                 // prevent rerouting during idling (#11079)
     744              :                 myRoutingDevice->setActive(false);
     745              :             }
     746         1834 :         } else if (!myReachedServiceEnd) {
     747          228 :             WRITE_WARNINGF(TL("Taxi '%' reaches scheduled end of service at time=%."), myHolder.getID(), time2string(SIMSTEP));
     748           76 :             myReachedServiceEnd = true;
     749              :         }
     750       143349 :     } else if (myRoutingDevice != nullptr) {
     751              :         myRoutingDevice->setActive(true);
     752              :     }
     753       358948 :     if (myHolder.isStopped() && (isEmpty() || MSGlobals::gUseMesoSim) && myHolder.getNextStop().endBoarding > myServiceEnd) {
     754              :         // limit duration of stop (but only for idling-related stops)
     755         2226 :         myHolder.getNextStopMutable().endBoarding = myServiceEnd;
     756              :     }
     757              : #ifdef DEBUG_DISPATCH
     758              :     if (DEBUG_COND && myIsStopped != myHolder.isStopped()) {
     759              :         std::cout << SIMTIME << " updateMove veh=" << myHolder.getID() << " myIsStopped=" << myIsStopped << " myHolderStopped=" << myHolder.isStopped() << " myState=" << myState << "\n";
     760              :     }
     761              : #endif
     762       358948 :     myIsStopped = myHolder.isStopped();
     763       358948 : }
     764              : 
     765              : 
     766              : bool
     767       339821 : MSDevice_Taxi::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
     768              :                           double newPos, double /*newSpeed*/) {
     769       339821 :     updateMove(DELTA_T, newPos - oldPos);
     770       339821 :     return true; // keep the device
     771              : }
     772              : 
     773              : 
     774              : void
     775        19127 : MSDevice_Taxi::notifyMoveInternal(const SUMOTrafficObject& /* veh */,
     776              :                                   const double /* frontOnLane */,
     777              :                                   const double timeOnLane,
     778              :                                   const double /* meanSpeedFrontOnLane */,
     779              :                                   const double /* meanSpeedVehicleOnLane */,
     780              :                                   const double travelledDistanceFrontOnLane,
     781              :                                   const double /* travelledDistanceVehicleOnLane */,
     782              :                                   const double /* meanLengthOnLane */) {
     783        19127 :     updateMove(TIME2STEPS(timeOnLane), travelledDistanceFrontOnLane);
     784        19127 : }
     785              : 
     786              : 
     787              : bool
     788        60484 : MSDevice_Taxi::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
     789        60484 :     if (isEmpty() && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     790        39858 :         myIdleAlgorithm->idle(this);
     791              :     }
     792        60484 :     return true; // keep the device
     793              : }
     794              : 
     795              : 
     796              : void
     797         2088 : MSDevice_Taxi::customerEntered(const MSTransportable* t) {
     798         2088 :     myState |= OCCUPIED;
     799         2088 :     if (!hasFuturePickup()) {
     800         1620 :         myState &= ~PICKUP;
     801              :     }
     802        11126 :     for (const Reservation* res : myCurrentReservations) {
     803        16188 :         for (const MSTransportable* cand : res->persons) {
     804         9238 :             if (cand == t) {
     805         2088 :                 const_cast<Reservation*>(res)->state = Reservation::ONBOARD;
     806         2088 :                 break;
     807              :             }
     808              :         }
     809              :     }
     810         2088 : }
     811              : 
     812              : 
     813              : void
     814         2088 : MSDevice_Taxi::customerArrived(const MSTransportable* person) {
     815         2088 :     myCustomersServed++;
     816              :     myCustomers.erase(person);
     817         2088 :     if (myHolder.getPersonNumber() == 0 && myHolder.getContainerNumber() == 0) {
     818         1367 :         myState &= ~OCCUPIED;
     819         1367 :         if (myHolder.getStops().size() > 1 && (myState & PICKUP) == 0) {
     820            0 :             WRITE_WARNINGF(TL("All customers left vehicle '%' at time=% but there are % remaining stops"),
     821              :                            myHolder.getID(), time2string(SIMSTEP), myHolder.getStops().size() - 1);
     822            0 :             while (myHolder.getStops().size() > 1) {
     823            0 :                 myHolder.abortNextStop(1);
     824              :             }
     825              :         }
     826              :     }
     827         2088 :     if (isEmpty()) {
     828              :         // cleanup
     829         2672 :         for (const Reservation* res : myCurrentReservations) {
     830         1342 :             myDispatcher->fulfilledReservation(res);
     831              :         }
     832              :         myCurrentReservations.clear();
     833         1330 :         if (MSGlobals::gUseMesoSim && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     834          405 :             myIdleAlgorithm->idle(this);
     835              :         }
     836              :     } else {
     837              :         // check whether a single reservation has been fulfilled
     838         5051 :         for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
     839              :             bool fulfilled = true;
     840         5124 :             for (const MSTransportable* t : (*resIt)->persons) {
     841              :                 if (myCustomers.count(t) != 0) {
     842              :                     fulfilled = false;
     843              :                     break;
     844              :                 }
     845              :             }
     846         4293 :             if (fulfilled) {
     847          648 :                 myDispatcher->fulfilledReservation(*resIt);
     848              :                 resIt = myCurrentReservations.erase(resIt);
     849              :             } else {
     850              :                 ++resIt;
     851              :             }
     852              :         }
     853              :     }
     854         2088 : }
     855              : 
     856              : 
     857              : bool
     858         2088 : MSDevice_Taxi::hasFuturePickup() {
     859         5984 :     for (const auto& stop : myHolder.getStops()) {
     860         4364 :         if (stop.reached) {
     861         2090 :             continue;
     862              :         }
     863         2274 :         if (stop.pars.permitted.size() > 0) {
     864              :             return true;
     865              :         }
     866              :     }
     867              :     return false;
     868              : }
     869              : 
     870              : void
     871          847 : MSDevice_Taxi::generateOutput(OutputDevice* tripinfoOut) const {
     872          847 :     if (tripinfoOut != nullptr) {
     873          823 :         tripinfoOut->openTag("taxi");
     874         1646 :         tripinfoOut->writeAttr("customers", toString(myCustomersServed));
     875         1646 :         tripinfoOut->writeAttr("occupiedDistance", toString(myOccupiedDistance));
     876         1646 :         tripinfoOut->writeAttr("occupiedTime", time2string(myOccupiedTime));
     877         1646 :         tripinfoOut->closeTag();
     878              :     }
     879          847 : }
     880              : 
     881              : std::string
     882         1832 : MSDevice_Taxi::getParameter(const std::string& key) const {
     883         1832 :     if (key == "customers") {
     884            0 :         return toString(myCustomersServed);
     885         1832 :     } else if (key == "occupiedDistance") {
     886            0 :         return toString(myOccupiedDistance);
     887         1832 :     } else if (key == "occupiedTime") {
     888            0 :         return toString(STEPS2TIME(myOccupiedTime));
     889         1832 :     } else if (key == "state") {
     890         1753 :         return toString(myState);
     891           79 :     } else if (key == "currentCustomers") {
     892           15 :         return joinNamedToStringSorting(myCustomers, " ");
     893           64 :     } else if (key == "pickUpDuration") {
     894           64 :         return myHolder.getStringParam("device.taxi.pickUpDuration", false, "0");
     895           32 :     } else if (key == "dropOffDuration") {
     896           64 :         return myHolder.getStringParam("device.taxi.dropOffDuration", false, "60");
     897              :     }
     898            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     899              : }
     900              : 
     901              : 
     902              : void
     903           20 : MSDevice_Taxi::setParameter(const std::string& key, const std::string& value) {
     904              :     double doubleValue;
     905              :     try {
     906           20 :         doubleValue = StringUtils::toDouble(value);
     907            0 :     } catch (NumberFormatException&) {
     908            0 :         throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
     909            0 :     }
     910           20 :     if (key == "pickUpDuration" || key == "dropOffDuration") {
     911              :         // store as generic vehicle parameters
     912           20 :         ((SUMOVehicleParameter&)myHolder.getParameter()).setParameter("device.taxi." + key, value);
     913              :     } else {
     914              :         UNUSED_PARAMETER(doubleValue);
     915            0 :         throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     916              :     }
     917           20 : }
     918              : 
     919              : bool
     920        61982 : MSDevice_Taxi::compatibleLine(const std::string& taxiLine, const std::string& rideLine) {
     921        70104 :     return ((taxiLine == rideLine && StringUtils::startsWith(rideLine, "taxi") && StringUtils::startsWith(taxiLine, "taxi"))
     922       115842 :             || (taxiLine == TAXI_SERVICE && StringUtils::startsWith(rideLine, "taxi:"))
     923       188115 :             || (rideLine == TAXI_SERVICE && StringUtils::startsWith(taxiLine, "taxi:")));
     924              : }
     925              : 
     926              : bool
     927         3570 : MSDevice_Taxi::compatibleLine(const Reservation* res) {
     928         3570 :     return compatibleLine(myHolder.getParameter().line, res->line);
     929              : }
     930              : 
     931              : 
     932              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1