LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Taxi.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.0 % 538 484
Test Date: 2026-04-16 16:39:47 Functions: 100.0 % 42 42

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2026 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/transportables/MSTransportableControl.h>
      31              : #include <microsim/MSEventControl.h>
      32              : #include <microsim/MSGlobals.h>
      33              : #include <microsim/MSVehicle.h>
      34              : #include <microsim/MSEdge.h>
      35              : #include <microsim/MSLane.h>
      36              : #include <microsim/MSStop.h>
      37              : #include <microsim/MSStoppingPlace.h>
      38              : #include <microsim/trigger/MSTriggeredRerouter.h>
      39              : 
      40              : #include "MSDispatch.h"
      41              : #include "MSDispatch_Greedy.h"
      42              : #include "MSDispatch_GreedyShared.h"
      43              : #include "MSDispatch_RouteExtension.h"
      44              : #include "MSDispatch_TraCI.h"
      45              : 
      46              : #include "MSIdling.h"
      47              : 
      48              : #include "MSRoutingEngine.h"
      49              : #include "MSDevice_Routing.h"
      50              : #include "MSDevice_Taxi.h"
      51              : 
      52              : //#define DEBUG_DISPATCH
      53              : //#define DEBUG_CANCEL
      54              : 
      55              : //#define DEBUG_COND (myHolder.isSelected())
      56              : #define DEBUG_COND (true)
      57              : 
      58              : // ===========================================================================
      59              : // static member variables
      60              : // ===========================================================================
      61              : SUMOTime MSDevice_Taxi::myDispatchPeriod(0);
      62              : /// @brief the dispatch algorithm
      63              : MSDispatch* MSDevice_Taxi::myDispatcher(nullptr);
      64              : /// @brief The repeated call to the dispatcher
      65              : Command* MSDevice_Taxi::myDispatchCommand(nullptr);
      66              : // @brief the list of available taxis
      67              : std::vector<MSDevice_Taxi*> MSDevice_Taxi::myFleet;
      68              : int MSDevice_Taxi::myMaxCapacity(0);
      69              : int MSDevice_Taxi::myMaxContainerCapacity(0);
      70              : std::map<SUMOVehicleClass, std::string> MSDevice_Taxi::myTaxiTypes;
      71              : SUMOTime MSDevice_Taxi::myNextDispatchTime(-1);
      72              : std::map<std::string, MSDevice_Taxi*> MSDevice_Taxi::myStateLoadedCustomers;
      73              : std::map<std::string, MSDevice_Taxi*> MSDevice_Taxi::myStateLoadedReservations;
      74              : 
      75              : #define TAXI_SERVICE "taxi"
      76              : #define TAXI_SERVICE_PREFIX "taxi:"
      77              : #define SWAP_THRESHOLD 5
      78              : 
      79              : // ===========================================================================
      80              : // method definitions
      81              : // ===========================================================================
      82              : // ---------------------------------------------------------------------------
      83              : // static initialisation methods
      84              : // ---------------------------------------------------------------------------
      85              : void
      86        44603 : MSDevice_Taxi::insertOptions(OptionsCont& oc) {
      87        44603 :     oc.addOptionSubTopic("Taxi Device");
      88        89206 :     insertDefaultAssignmentOptions("taxi", "Taxi Device", oc);
      89              : 
      90        89206 :     oc.doRegister("device.taxi.dispatch-algorithm", new Option_String("greedy"));
      91        89206 :     oc.addDescription("device.taxi.dispatch-algorithm", "Taxi Device", TL("The dispatch algorithm [greedy|greedyClosest|greedyShared|routeExtension|traci]"));
      92              : 
      93        44603 :     oc.doRegister("device.taxi.dispatch-algorithm.output", new Option_FileName());
      94        89206 :     oc.addDescription("device.taxi.dispatch-algorithm.output", "Taxi Device", TL("Write information from the dispatch algorithm to FILE"));
      95              : 
      96        89206 :     oc.doRegister("device.taxi.dispatch-algorithm.params", new Option_String(""));
      97        89206 :     oc.addDescription("device.taxi.dispatch-algorithm.params", "Taxi Device", TL("Load dispatch algorithm parameters in format KEY1:VALUE1[,KEY2:VALUE]"));
      98              : 
      99        89206 :     oc.doRegister("device.taxi.dispatch-period", new Option_String("60", "TIME"));
     100        89206 :     oc.addDescription("device.taxi.dispatch-period", "Taxi Device", TL("The period between successive calls to the dispatcher"));
     101              : 
     102        89206 :     oc.doRegister("device.taxi.dispatch-keep-unreachable", new Option_String("3600", "TIME"));
     103        89206 :     oc.addDescription("device.taxi.dispatch-keep-unreachable", "Taxi Device", TL("The time before aborting unreachable reservations"));
     104              : 
     105        89206 :     oc.doRegister("device.taxi.idle-algorithm", new Option_String("stop"));
     106        89206 :     oc.addDescription("device.taxi.idle-algorithm", "Taxi Device", TL("The behavior of idle taxis [stop|randomCircling|taxistand]"));
     107              : 
     108        44603 :     oc.doRegister("device.taxi.idle-algorithm.output", new Option_FileName());
     109        89206 :     oc.addDescription("device.taxi.idle-algorithm.output", "Taxi Device", TL("Write information from the idling algorithm to FILE"));
     110              : 
     111        89206 :     oc.doRegister("device.taxi.vclasses", new Option_StringVector({"taxi"}));
     112        89206 :     oc.addSynonyme("device.taxi.vclasses", "taxi.vclasses");
     113        89206 :     oc.addDescription("device.taxi.vclasses", "Taxi Device", TL("Network permissions that can be accessed by taxis"));
     114        44603 : }
     115              : 
     116              : 
     117              : void
     118      5255340 : MSDevice_Taxi::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
     119      5255340 :     OptionsCont& oc = OptionsCont::getOptions();
     120     10510680 :     if (equippedByDefaultAssignmentOptions(oc, "taxi", v, false)) {
     121              :         // build the device
     122         6939 :         MSDevice_Taxi* device = new MSDevice_Taxi(v, "taxi_" + v.getID());
     123         6927 :         into.push_back(device);
     124         6927 :         myFleet.push_back(device);
     125         6927 :         if (v.getParameter().line == "") {
     126              :             // automatically set the line so that persons are willing to enter
     127              :             // (see MSStageDriving::isWaitingFor)
     128         6673 :             const_cast<SUMOVehicleParameter&>(v.getParameter()).line = TAXI_SERVICE;
     129              :         }
     130         6927 :         const int personCapacity = v.getVehicleType().getPersonCapacity();
     131         6927 :         const int containerCapacity = v.getVehicleType().getContainerCapacity();
     132         6927 :         myMaxCapacity = MAX2(myMaxCapacity, personCapacity);
     133         6927 :         myMaxContainerCapacity = MAX2(myMaxContainerCapacity, containerCapacity);
     134         6927 :         if (myTaxiTypes[v.getVClass()] == "") {
     135         1666 :             myTaxiTypes[v.getVClass()] = v.getVehicleType().getID();
     136              :         }
     137         6927 :         if ((gTaxiClasses & v.getVClass()) == 0) {
     138           71 :             gTaxiClasses |= v.getVClass();
     139           71 :             MSNet::getInstance()->resetIntermodalRouter();
     140              :         }
     141         6927 :         if (personCapacity < 1 && containerCapacity < 1) {
     142            0 :             WRITE_WARNINGF(TL("Vehicle '%' with personCapacity % and containerCapacity % is not usable as taxi."), v.getID(), toString(personCapacity), toString(containerCapacity));
     143              :         }
     144              :     }
     145      5255334 : }
     146              : 
     147              : SUMOTime
     148           10 : MSDevice_Taxi::getNextDispatchTime() {
     149           10 :     return myNextDispatchTime;
     150              : }
     151              : 
     152              : void
     153          866 : MSDevice_Taxi::initDispatch(SUMOTime next) {
     154          866 :     OptionsCont& oc = OptionsCont::getOptions();
     155          866 :     myDispatchPeriod = string2time(oc.getString("device.taxi.dispatch-period"));
     156              :     // init dispatch algorithm
     157          866 :     std::string algo = oc.getString("device.taxi.dispatch-algorithm");
     158          866 :     Parameterised params;
     159         1732 :     params.setParametersStr(OptionsCont::getOptions().getString("device.taxi.dispatch-algorithm.params"), ":", ",");
     160          866 :     if (algo == "greedy") {
     161          562 :         myDispatcher = new MSDispatch_Greedy(params.getParametersMap());
     162          304 :     } else if (algo == "greedyClosest") {
     163           36 :         myDispatcher = new MSDispatch_GreedyClosest(params.getParametersMap());
     164          286 :     } else if (algo == "greedyShared") {
     165          114 :         myDispatcher = new MSDispatch_GreedyShared(params.getParametersMap());
     166          172 :     } else if (algo == "routeExtension") {
     167          122 :         myDispatcher = new MSDispatch_RouteExtension(params.getParametersMap());
     168          111 :     } else if (algo == "traci") {
     169          222 :         myDispatcher = new MSDispatch_TraCI(params.getParametersMap());
     170              :     } else {
     171            0 :         throw ProcessError(TLF("Dispatch algorithm '%' is not known", algo));
     172              :     }
     173          866 :     myDispatchCommand = new StaticCommand<MSDevice_Taxi>(&MSDevice_Taxi::triggerDispatch);
     174              :     // round to next multiple of myDispatchPeriod
     175          866 :     if (next < 0) {
     176          856 :         const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
     177          856 :         const SUMOTime begin = string2time(oc.getString("begin"));
     178          856 :         const SUMOTime delay = (myDispatchPeriod - ((now - begin) % myDispatchPeriod)) % myDispatchPeriod;
     179          856 :         next = now + delay;
     180              :     }
     181          866 :     myNextDispatchTime = next;
     182          866 :     MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myDispatchCommand, next);
     183         1732 : }
     184              : 
     185              : bool
     186        78529 : MSDevice_Taxi::isReservation(const std::set<std::string>& lines) {
     187        78529 :     return lines.size() == 1 && (
     188        78515 :                *lines.begin() == TAXI_SERVICE
     189       150864 :                || StringUtils::startsWith(*lines.begin(), TAXI_SERVICE_PREFIX));
     190              : }
     191              : 
     192              : void
     193         3150 : MSDevice_Taxi::addReservation(MSTransportable* person,
     194              :                               const std::set<std::string>& lines,
     195              :                               SUMOTime reservationTime,
     196              :                               SUMOTime pickupTime,
     197              :                               SUMOTime earliestPickupTime,
     198              :                               const MSEdge* from, double fromPos,
     199              :                               const MSStoppingPlace* fromStop,
     200              :                               const MSEdge* to, double toPos,
     201              :                               const MSStoppingPlace* toStop,
     202              :                               const std::string& group) {
     203         3150 :     if (!isReservation(lines)) {
     204              :         return;
     205              :     }
     206         3150 :     if ((to->getPermissions() & gTaxiClasses) == 0) {
     207            0 :         throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
     208            0 :                            + " '" + person->getID() + "' because destination edge '" + to->getID() + "'"
     209            0 :                            + " does not permit taxi access");
     210              :     }
     211         3150 :     if ((from->getPermissions() & gTaxiClasses) == 0) {
     212            0 :         throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
     213            0 :                            + " '" + person->getID() + "' because origin edge '" + from->getID() + "'"
     214            0 :                            + " does not permit taxi access");
     215              :     }
     216         3150 :     if (myDispatchCommand == nullptr) {
     217          856 :         initDispatch();
     218              :     }
     219         3150 :     if (fromStop != nullptr && &fromStop->getLane().getEdge() == from) {
     220              :         // pickup position should be at the stop-endPos
     221          928 :         fromPos = fromStop->getEndLanePosition();
     222              :     }
     223         6300 :     Reservation* res = myDispatcher->addReservation(person, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, *lines.begin(), myMaxCapacity, myMaxContainerCapacity);
     224         3150 :     if (myStateLoadedCustomers.size() > 0) {
     225              :         auto it = myStateLoadedCustomers.find(person->getID());
     226           62 :         if (it != myStateLoadedCustomers.end()) {
     227              :             //std::cout << SIMTIME << " loadedServed p=" << person->getID() << " res=" << res->getID() << " taxi=" << it->second->getID() << "\n";
     228           14 :             myDispatcher->servedReservation(res, it->second);
     229              :         }
     230              :     }
     231              : }
     232              : 
     233              : void
     234           93 : MSDevice_Taxi::removeReservation(MSTransportable* person,
     235              :                                  const std::set<std::string>& lines,
     236              :                                  const MSEdge* from, double fromPos,
     237              :                                  const MSEdge* to, double toPos,
     238              :                                  const std::string& group) {
     239           93 :     if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
     240          154 :         myDispatcher->removeReservation(person, from, fromPos, to, toPos, group);
     241              :     }
     242           93 : }
     243              : 
     244              : void
     245          162 : MSDevice_Taxi::updateReservationFromPos(MSTransportable* person,
     246              :                                         const std::set<std::string>& lines,
     247              :                                         const MSEdge* from, double fromPos,
     248              :                                         const MSEdge* to, double toPos,
     249              :                                         const std::string& group, double newFromPos) {
     250          162 :     if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
     251          300 :         myDispatcher->updateReservationFromPos(person, from, fromPos, to, toPos, group, newFromPos);
     252              :     }
     253          162 : }
     254              : 
     255              : 
     256              : SUMOTime
     257        15289 : MSDevice_Taxi::triggerDispatch(SUMOTime currentTime) {
     258              :     std::vector<MSDevice_Taxi*> active;
     259        44629 :     for (MSDevice_Taxi* taxi : myFleet) {
     260        29340 :         if (taxi->getHolder().hasDeparted()) {
     261        29240 :             active.push_back(taxi);
     262              :         }
     263              :     }
     264        15289 :     myDispatcher->computeDispatch(currentTime, active);
     265        15287 :     myNextDispatchTime = currentTime + myDispatchPeriod;
     266        15287 :     return myDispatchPeriod;
     267        15289 : }
     268              : 
     269              : bool
     270      8555696 : MSDevice_Taxi::hasServableReservations() {
     271      8555696 :     return myDispatcher != nullptr && myDispatcher->hasServableReservations();
     272              : }
     273              : 
     274              : void
     275        41448 : MSDevice_Taxi::cleanup() {
     276        41448 :     if (myDispatcher != nullptr) {
     277          866 :         delete myDispatcher;
     278          866 :         myDispatcher = nullptr;
     279              :     }
     280        41448 :     myDispatchCommand = nullptr;
     281              :     myTaxiTypes.clear();
     282        41448 : }
     283              : 
     284              : 
     285              : void
     286          614 : MSDevice_Taxi::allCustomersErased() {
     287         1552 :     for (MSDevice_Taxi* taxi : myFleet) {
     288              :         // disable taskSwap
     289          938 :         taxi->myState = EMPTY;
     290              :     }
     291          614 : }
     292              : 
     293              : 
     294              : const std::map<SUMOVehicleClass, std::string>&
     295          211 : MSDevice_Taxi::getTaxiTypes() {
     296          211 :     const int numClasses = std::bitset<64>(gTaxiClasses).count();
     297          211 :     if ((int)myTaxiTypes.size() < numClasses) {
     298          219 :         for (const std::string& vClassName : OptionsCont::getOptions().getStringVector("device.taxi.vclasses")) {
     299           73 :             SUMOVehicleClass svc = (SUMOVehicleClass)parseVehicleClasses(vClassName);
     300           73 :             if (myTaxiTypes[svc] == "") {
     301           73 :                 switch(svc) {
     302              :                     // @see MSVehicleControl::initDefaultTypes()
     303           73 :                     case SVC_TAXI:
     304           73 :                         myTaxiTypes[svc] = DEFAULT_TAXITYPE_ID;
     305              :                         break;
     306            0 :                     case SVC_RAIL:
     307            0 :                         myTaxiTypes[svc] = DEFAULT_RAILTYPE_ID;
     308              :                         break;
     309            0 :                     case SVC_BICYCLE:
     310            0 :                         myTaxiTypes[svc] = DEFAULT_BIKETYPE_ID;
     311              :                         break;
     312            0 :                     case SVC_PASSENGER:
     313            0 :                         myTaxiTypes[svc] = DEFAULT_VTYPE_ID;
     314              :                         break;
     315            0 :                     default: {
     316            0 :                         const std::string typeID = "DEFAULT_" + StringUtils::to_upper_case(vClassName) + "TYPE";
     317            0 :                         MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     318            0 :                         if (!vc.hasVType(typeID)) {
     319            0 :                             SUMOVTypeParameter tp(typeID, svc);
     320            0 :                             MSVehicleType* t = MSVehicleType::build(tp);
     321            0 :                             vc.addVType(t);
     322            0 :                         }
     323            0 :                         myTaxiTypes[svc] = typeID;;
     324              :                     }
     325              :                 }
     326              :             }
     327              :         }
     328              :     }
     329          211 :     return myTaxiTypes;
     330              : }
     331              : 
     332              : 
     333              : // ---------------------------------------------------------------------------
     334              : // MSDevice_Taxi-methods
     335              : // ---------------------------------------------------------------------------
     336         6933 : MSDevice_Taxi::MSDevice_Taxi(SUMOVehicle& holder, const std::string& id) :
     337         6933 :     MSVehicleDevice(holder, id) {
     338         6933 :     std::string defaultServiceEnd = toString(1e15);
     339        13872 :     const std::string algo = holder.getStringParam("device.taxi.idle-algorithm");
     340         6933 :     if (algo == "stop") {
     341         6631 :         myIdleAlgorithm = new MSIdling_Stop();
     342          302 :     } else if (algo == "randomCircling") {
     343           97 :         myIdleAlgorithm = new MSIdling_RandomCircling();
     344              :         // make sure simulation terminates
     345           97 :         defaultServiceEnd = toString(STEPS2TIME(
     346              :                                          myHolder.getParameter().departProcedure == DepartDefinition::GIVEN
     347              :                                          ? myHolder.getParameter().depart
     348          194 :                                          : MSNet::getInstance()->getCurrentTimeStep()) + (3600 * 8));
     349          205 :     } else if (algo == "taxistand") {
     350          398 :         const std::string rerouterID = holder.getStringParam("device.taxi.stands-rerouter");
     351          199 :         if (rerouterID.empty()) {
     352            0 :             throw ProcessError("Idle algorithm '" + algo + "' requires a rerouter id to be defined using device param 'stands-rerouter' for vehicle '" + myHolder.getID() + "'");
     353              :         }
     354              :         if (MSTriggeredRerouter::getInstances().count(rerouterID) == 0) {
     355            0 :             throw ProcessError("Unknown rerouter '" + rerouterID + "' when loading taxi stands for vehicle '" + myHolder.getID() + "'");
     356              :         }
     357          199 :         MSTriggeredRerouter* rerouter = MSTriggeredRerouter::getInstances().find(rerouterID)->second;
     358          199 :         myIdleAlgorithm = new MSIdling_TaxiStand(rerouter);
     359              :     } else {
     360           18 :         throw ProcessError("Idle algorithm '" + algo + "' is not known for vehicle '" + myHolder.getID() + "'");
     361              :     }
     362         6933 :     myServiceEnd = string2time(holder.getStringParam("device.taxi.end", false, defaultServiceEnd));
     363         6927 :     myRoutingDevice = static_cast<MSDevice_Routing*>(myHolder.getDevice(typeid(MSDevice_Routing)));
     364         6933 : }
     365              : 
     366              : 
     367        13854 : MSDevice_Taxi::~MSDevice_Taxi() {
     368         6927 :     myFleet.erase(std::find(myFleet.begin(), myFleet.end(), this));
     369              :     // recompute myMaxCapacity
     370         6927 :     myMaxCapacity = 0;
     371         6927 :     myMaxContainerCapacity = 0;
     372        17716 :     for (MSDevice_Taxi* taxi : myFleet) {
     373        10789 :         myMaxCapacity = MAX2(myMaxCapacity, taxi->getHolder().getVehicleType().getPersonCapacity());
     374        10789 :         myMaxContainerCapacity = MAX2(myMaxContainerCapacity, taxi->getHolder().getVehicleType().getContainerCapacity());
     375              :     }
     376         6927 :     delete myIdleAlgorithm;
     377        13854 : }
     378              : 
     379              : 
     380              : bool
     381        11528 : MSDevice_Taxi::hasFleet() {
     382        11528 :     return myFleet.size() > 0;;
     383              : }
     384              : 
     385              : 
     386              : void
     387         1559 : MSDevice_Taxi::dispatch(const Reservation& res) {
     388         1559 :     dispatchShared({&res, &res});
     389         1557 : }
     390              : 
     391              : 
     392              : void
     393         1904 : MSDevice_Taxi::dispatchShared(std::vector<const Reservation*> reservations) {
     394              : #ifdef DEBUG_DISPATCH
     395              :     if (DEBUG_COND) {
     396              :         std::cout << SIMTIME << " taxi=" << myHolder.getID() << " dispatch:\n";
     397              :         for (const Reservation* res : reservations) {
     398              :             std::cout << "   res=" << res->getID();
     399              :             std::cout << " persons=" << toString(res->persons) << "\n";
     400              :         }
     401              :     }
     402              : #endif
     403         1904 :     myLastDispatch = reservations;
     404              :     ConstMSEdgeVector tmpEdges;
     405              :     StopParVector stops;
     406         1904 :     double lastPos = myHolder.getPositionOnLane();
     407         1904 :     const MSEdge* rerouteOrigin = *myHolder.getRerouteOrigin();
     408         1904 :     if (isEmpty()) {
     409              :         // start fresh from the current edge
     410         1813 :         if (myHolder.isStoppedParking()) {
     411              :             // parking stop must be ended normally
     412         1087 :             MSStop& stop = myHolder.getNextStopMutable();
     413         1087 :             stop.duration = 0;
     414         1087 :             lastPos = stop.pars.endPos;
     415         1087 :             if (myHolder.isStoppedTriggered()) {
     416          975 :                 stop.triggered = false;
     417          975 :                 stop.containerTriggered = false;
     418          975 :                 stop.joinTriggered = false;
     419          975 :                 myHolder.unregisterWaiting();
     420              :             }
     421              :             // prevent unauthorized/premature entry
     422         1087 :             const_cast<SUMOVehicleParameter::Stop&>(stop.pars).permitted.insert("");
     423         1111 :             while (myHolder.getStops().size() > 1) {
     424           24 :                 myHolder.abortNextStop(1);
     425              :             }
     426              :         } else {
     427         1370 :             while (myHolder.hasStops()) {
     428              :                 // in meso there might be more than 1 stop at this point
     429          644 :                 myHolder.abortNextStop();
     430              :             }
     431              :             assert(!myHolder.hasStops());
     432              :         }
     433         1813 :         tmpEdges.push_back(myHolder.getEdge());
     434         1813 :         if (myHolder.getEdge() != rerouteOrigin) {
     435            8 :             tmpEdges.push_back(rerouteOrigin);
     436              :         }
     437              :     } else {
     438              :         assert(myHolder.hasStops());
     439              :         // check how often existing customers appear in the new reservations
     440              :         std::map<const MSTransportable*, int> nOccur;
     441          629 :         for (const Reservation* res : reservations) {
     442         1076 :             for (const MSTransportable* person : res->persons) {
     443              :                 if (myCustomers.count(person) != 0) {
     444          298 :                     nOccur[person] += 1;
     445              :                     if (myCurrentReservations.count(res) == 0) {
     446            0 :                         throw ProcessError(TLF("Invalid Re-dispatch for existing customer '%' with a new reservation", person->getID()));
     447              :                     }
     448              :                 }
     449              :             }
     450              :         }
     451              : #ifdef DEBUG_DISPATCH
     452              :         if (DEBUG_COND) {
     453              :             for (auto item : nOccur) {
     454              :                 std::cout << "   previousCustomer=" << item.first->getID() << " occurs=" << item.second << "\n";
     455              :             }
     456              :         }
     457              : #endif
     458           91 :         if (nOccur.size() == 0) {
     459              :             // no overlap with existing customers - extend route
     460           10 :             tmpEdges = myHolder.getRoute().getEdges();
     461           10 :             lastPos = myHolder.getStops().back().pars.endPos;
     462              : #ifdef DEBUG_DISPATCH
     463              :             if (DEBUG_COND) {
     464              :                 std::cout << " re-dispatch with route-extension\n";
     465              :             }
     466              : #endif
     467           81 :         } else if (nOccur.size() == myCustomers.size()) {
     468              :             // redefine route (verify correct number of mentions)
     469              :             std::set<const MSTransportable*> onBoard;
     470           81 :             const std::vector<MSTransportable*>& onBoardP = myHolder.getPersons();
     471           81 :             const std::vector<MSTransportable*>& onBoardC = myHolder.getContainers();
     472              :             onBoard.insert(onBoardP.begin(), onBoardP.end());
     473              :             onBoard.insert(onBoardC.begin(), onBoardC.end());
     474              :             std::set<const MSTransportable*> redundantPickup;
     475          244 :             for (auto item : nOccur) {
     476          167 :                 if (item.second == 1) {
     477              :                     // customers must already be on board
     478              :                     if (onBoard.count(item.first) == 0) {
     479           12 :                         throw ProcessError(TLF("Re-dispatch did not mention pickup for existing customer '%'", item.first->getID()));
     480              :                     }
     481          131 :                 } else if (item.second == 2) {
     482              :                     if (onBoard.count(item.first) == 0) {
     483              :                         // treat like a new customer
     484              :                         // TODO: need to be checked
     485              :                         myCustomers.erase(item.first);
     486              :                     } else {
     487              :                         redundantPickup.insert(item.first);
     488              :                     }
     489              :                 } else {
     490            0 :                     throw ProcessError("Re-dispatch mentions existing customer '" + item.first->getID() + "' " + toString(item.second) + " times");
     491              :                 }
     492              :             }
     493              :             // remove redundancy
     494           77 :             if (!redundantPickup.empty()) {
     495          183 :                 for (auto it = reservations.begin(); it != reservations.end();) {
     496              :                     bool isRedundant = false;
     497          262 :                     for (const MSTransportable* person : (*it)->persons) {
     498              :                         if (redundantPickup.count(person) != 0) {
     499              :                             isRedundant = true;
     500              :                             break;
     501              :                         }
     502              :                     }
     503          154 :                     if (isRedundant) {
     504           92 :                         for (const MSTransportable* person : (*it)->persons) {
     505              :                             redundantPickup.erase(person);
     506              :                         }
     507              :                         it = reservations.erase(it);
     508              :                     } else {
     509              :                         it++;
     510              :                     }
     511              :                 }
     512              :             }
     513          317 :             while (myHolder.hasStops()) {
     514          240 :                 myHolder.abortNextStop();
     515              :             }
     516           81 :             tmpEdges.push_back(myHolder.getEdge());
     517           77 :             if (myHolder.getEdge() != rerouteOrigin) {
     518           10 :                 tmpEdges.push_back(rerouteOrigin);
     519              :             }
     520              : #ifdef DEBUG_DISPATCH
     521              :             if (DEBUG_COND) {
     522              :                 std::cout << " re-dispatch from scratch\n";
     523              :             }
     524              : #endif
     525              :         } else {
     526              :             // inconsistent re-dispatch
     527              :             std::vector<std::string> missing;
     528            0 :             for (const MSTransportable* c : myCustomers) {
     529              :                 if (nOccur.count(c) == 0) {
     530            0 :                     missing.push_back(c->getID());
     531              :                 }
     532              :             }
     533            0 :             throw ProcessError("Re-dispatch did mention some customers but failed to mention " + joinToStringSorting(missing, " "));
     534            0 :         }
     535              :     }
     536              : 
     537         1900 :     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
     538              :     bool hasPickup = false;
     539         7106 :     for (const Reservation* res : reservations) {
     540              :         myCurrentReservations.insert(res);
     541              :         bool isPickup = false;
     542        10694 :         for (const MSTransportable* person : res->persons) {
     543              :             if (myCustomers.count(person) == 0) {
     544              :                 myCustomers.insert(person);
     545              :                 isPickup = true;
     546              :                 hasPickup = true;
     547              :             }
     548              :         }
     549         5206 :         if (isPickup) {
     550         7692 :             prepareStop(tmpEdges, stops, lastPos, res->from, res->fromPos, res->fromStop, "pickup " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
     551         5269 :             for (const MSTransportable* const transportable : res->persons) {
     552         2705 :                 if (transportable->isPerson()) {
     553         2531 :                     stops.back().triggered = true;
     554              :                 } else {
     555          174 :                     stops.back().containerTriggered = true;
     556              :                 }
     557              :                 stops.back().permitted.insert(transportable->getID());
     558         2705 :                 stops.back().parametersSet |= STOP_PERMITTED_SET | STOP_TRIGGER_SET;
     559              :             }
     560              :             // proof this lines: Is needed for pre-booking?
     561              :             std::set<const MSTransportable*> persons = res->persons;
     562         5269 :             for (auto itr = persons.begin(); itr != persons.end(); itr++) {
     563         2705 :                 stops.back().awaitedPersons.insert((*itr)->getID());
     564              :             }
     565              : 
     566         2564 :             if (stops.back().duration == -1) {
     567              :                 // keep dropOffDuration if the stop is dropOff and pickUp
     568         2210 :                 stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.pickUpDuration", false, 0));
     569              :             }
     570              :         } else {
     571         5284 :             prepareStop(tmpEdges, stops, lastPos, res->to, res->toPos, res->toStop, "dropOff " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
     572         2642 :             stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.dropOffDuration", false, 60)); // pay and collect bags
     573              :         }
     574         5206 :         stops.back().parametersSet |= STOP_DURATION_SET | STOP_PARKING_SET;
     575              :     }
     576              : #ifdef DEBUG_DISPATCH
     577              :     if (DEBUG_COND) {
     578              :         std::cout << "   tmpEdges=" << toString(tmpEdges) << "\n";
     579              :     }
     580              : #endif
     581         3800 :     if (!myHolder.replaceRouteEdges(tmpEdges, -1, 0, "taxi:prepare_dispatch", false, false, false)) {
     582            0 :         throw ProcessError("Route replacement for taxi dispatch failed for vehicle '" + myHolder.getID()
     583            0 :                            + "' at time=" + time2string(t) + ".");
     584              :     }
     585              : #ifdef DEBUG_DISPATCH
     586              :     if (DEBUG_COND) std::cout << "   replacedRoute=" << toString(tmpEdges)
     587              :                                   << "\n     actualRoute=" << toString(myHolder.getRoute().getEdges()) << "\n";
     588              : #endif
     589         6251 :     for (SUMOVehicleParameter::Stop& stop : stops) {
     590              :         std::string error;
     591         4351 :         if (!myHolder.addStop(stop, error)) {
     592            0 :             WRITE_WARNINGF(TL("Could not add taxi stop for %, desc=% time=% error=%"), myHolder.getID(), stop.actType, time2string(t), error)
     593              :         }
     594              :     }
     595         1900 :     SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
     596              :     // SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myHolder.getInfluencer().getRouterTT(veh->getRNGIndex())
     597         1900 :     myHolder.reroute(t, "taxi:dispatch", router, false);
     598              : #ifdef DEBUG_DISPATCH
     599              :     if (DEBUG_COND) {
     600              :         std::cout << "\n      finalRoute=" << toString(myHolder.getRoute().getEdges()) << " routeIndex=" << myHolder.getRoutePosition() << "\n";
     601              :     }
     602              : #endif
     603         1898 :     if (hasPickup) {
     604         1866 :         myState |= PICKUP;
     605              :     }
     606         1910 : }
     607              : 
     608              : 
     609              : void
     610           87 : MSDevice_Taxi::cancelCurrentCustomers() {
     611              :     // check if taxi has stopped
     612           87 :     if (myHolder.getNextStopParameter() == nullptr) {
     613            0 :         return;
     614              :     }
     615              :     // find customers of the current stop
     616              :     std::set<const MSTransportable*> customersToBeRemoved;
     617              :     std::set<const MSTransportable*> onBoard;
     618           87 :     onBoard.insert(myHolder.getPersons().begin(), myHolder.getPersons().end());
     619           87 :     onBoard.insert(myHolder.getContainers().begin(), myHolder.getContainers().end());
     620          141 :     for (std::string tID : myHolder.getNextStopParameter()->permitted) {
     621          220 :         for (auto t : myCustomers) {
     622          166 :             if (t->getID() == tID && onBoard.count(t) == 0) {
     623              :                 customersToBeRemoved.insert(t);
     624              :             }
     625              :         }
     626              :     }
     627           87 :     if (!customersToBeRemoved.empty()) {
     628           90 :         WRITE_WARNINGF(TL("Taxi '%' aborts waiting for customers: % at time=%."),
     629              :                        myHolder.getID(), toString(customersToBeRemoved), time2string(SIMSTEP));
     630              :     }
     631          121 :     for (auto t : customersToBeRemoved) {
     632           34 :         cancelCustomer(t);
     633              :     }
     634              : }
     635              : 
     636              : 
     637              : bool
     638           44 : MSDevice_Taxi::cancelCustomer(const MSTransportable* t) {
     639              : #ifdef DEBUG_CANCEL
     640              :     if (DEBUG_COND) {
     641              :         std::cout << SIMTIME << " taxi=" << myHolder.getID() << " cancelCustomer " << t->getID() << "\n";
     642              :     }
     643              : #endif
     644              : 
     645              :     // is the given transportable a customer of the reservations?
     646              :     if (myCustomers.count(t) == 0) {
     647              :         return false;
     648              :     }
     649              :     myCustomers.erase(t);
     650              :     // check whether a single reservation has been fulfilled or another customer is part of the reservation
     651          132 :     for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
     652              :         bool fulfilled = false;
     653           88 :         if ((*resIt)->persons.size() == 1 && (*resIt)->persons.count(t) != 0) {
     654              :             // the reservation contains only the customer
     655              :             fulfilled = true;
     656              :         }
     657              :         if (fulfilled) {
     658              :             const Reservation* res = *resIt;
     659              :             // remove reservation from the current dispatch
     660          220 :             for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
     661          176 :                 if (*it == res) {
     662           88 :                     it = myLastDispatch.erase(it);
     663              :                 } else {
     664              :                     ++it;
     665              :                 }
     666              :             }
     667              :             // remove reservation from the served reservations
     668              :             resIt = myCurrentReservations.erase(resIt);
     669              :             // delete the reservation
     670           44 :             myDispatcher->fulfilledReservation(res);
     671              :         } else {
     672              :             ++resIt;
     673              :         }
     674              :     }
     675           44 :     myState &= ~PICKUP;  // remove state PICKUP
     676           88 :     for (const Reservation* res : myCurrentReservations) {
     677              :         // if there is another pickup in the dispatch left, add the state PICKUP
     678           44 :         if (std::count(myLastDispatch.begin(), myLastDispatch.end(), res) == 2) {
     679           44 :             myState |= PICKUP;  // add state PICKUP
     680              :         }
     681              :     }
     682              :     // we also have to clean reservations from myLastDispatch where the customers arrived in the meantime
     683          132 :     for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
     684              :         if (myCurrentReservations.count(*it) == 0) {
     685            0 :             it = myLastDispatch.erase(it);
     686              :         } else {
     687              :             ++it;
     688              :         }
     689              :     }
     690              :     // if there are reservations left, go on with the dispatch
     691              :     // in meso, wait for the next dispatch cycle to avoid updating stops in this stage
     692           44 :     if (!MSGlobals::gUseMesoSim) {
     693           36 :         dispatchShared(myLastDispatch);
     694              :     }
     695              :     return true;
     696              : }
     697              : 
     698              : 
     699              : void
     700           60 : MSDevice_Taxi::addCustomer(const MSTransportable* t, const Reservation* res) {
     701              :     myCustomers.insert(t);
     702           60 :     MSBaseVehicle& veh = dynamic_cast<MSBaseVehicle&>(myHolder);
     703          122 :     for (const MSStop& stop : veh.getStops()) {
     704              :         SUMOVehicleParameter::Stop& pars = const_cast<SUMOVehicleParameter::Stop&>(stop.pars);
     705              :         //std::cout << " sE=" << (*stop.edge)->getID() << " sStart=" << pars.startPos << " sEnd=" << pars.endPos << " rFrom=" <<
     706              :         //    res->from->getID() << " rTo=" << res->to->getID() << " rFromPos=" << res->fromPos << " resToPos=" << res->toPos << "\n";
     707          122 :         if (*stop.edge == res->from
     708           60 :                 && pars.startPos <= res->fromPos
     709          182 :                 && pars.endPos >= res->fromPos) {
     710           60 :             pars.awaitedPersons.insert(t->getID());
     711              :             pars.permitted.insert(t->getID());
     712          120 :             pars.actType += " +" + t->getID();
     713           62 :         } else if (*stop.edge == res->to
     714           60 :                 && pars.startPos <= res->toPos
     715          122 :                 && pars.endPos >= res->toPos) {
     716           60 :             pars.actType += " +" + t->getID();
     717           60 :             break;
     718              :         }
     719              :     }
     720           60 : }
     721              : 
     722              : 
     723              : void
     724         5206 : MSDevice_Taxi::prepareStop(ConstMSEdgeVector& edges,
     725              :                            StopParVector& stops,
     726              :                            double& lastPos, const MSEdge* stopEdge, double stopPos,
     727              :                            const MSStoppingPlace* stopPlace,
     728              :                            const std::string& action, const Reservation* res, const bool isPickup) {
     729              :     assert(!edges.empty());
     730         5206 :     if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
     731          894 :         stopPos = stopPlace->getEndLanePosition();
     732              :     }
     733         5206 :     if (stopPos < lastPos && stopPos + NUMERICAL_EPS >= lastPos) {
     734              :         stopPos = lastPos;
     735              :     }
     736              :     bool addedEdge = false;
     737              : 
     738         5206 :     if (stops.empty()) {
     739              :         // check brakeGap
     740         1880 :         double distToStop = stopPos - lastPos;
     741         1880 :         const double brakeGap = myHolder.getBrakeGap();
     742         1880 :         if (myHolder.getLane() != nullptr && myHolder.getLane()->isInternal()) {
     743            4 :             distToStop += myHolder.getLane()->getLength();
     744              :         }
     745         1880 :         if (stopEdge != edges.back()) {
     746         1616 :             distToStop += edges.back()->getLength();
     747         1616 :             if (distToStop < brakeGap) {
     748              :                 // the distance between current edge and stop edge may be small
     749           14 :                 SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
     750              :                 ConstMSEdgeVector toFirstStop;
     751            7 :                 router.compute(edges.back(), stopEdge, &myHolder, SIMSTEP, toFirstStop, true);
     752           31 :                 for (int i = 1; i < (int)toFirstStop.size() - 1; i++) {
     753           24 :                     distToStop += toFirstStop[i]->getLength();
     754              :                 }
     755            7 :             }
     756              :         }
     757         1880 :         if (distToStop < brakeGap) {
     758              :             // circle back to stopEdge
     759              :             //std::cout << SIMTIME << " taxi=" << getID() << " brakeGap=" << brakeGap << " distToStop=" << distToStop << "\n";
     760          187 :             edges.push_back(stopEdge);
     761              :             addedEdge = true;
     762              :         }
     763              :     }
     764              : 
     765         5206 :     if (stopEdge == edges.back() && !stops.empty()) {
     766         1026 :         if (stopPos >= lastPos && stopPos <= stops.back().endPos) {
     767              :             // no new stop and no adaption needed
     768          855 :             stops.back().actType += "," + action;
     769          855 :             return;
     770              :         }
     771          171 :         if (stopPos >= lastPos && stopPos <= lastPos + myHolder.getVehicleType().getLength()) {
     772              :             // stop length adaption needed
     773            0 :             stops.back().endPos = MIN2(lastPos + myHolder.getVehicleType().getLength(), stopEdge->getLength());
     774            0 :             stops.back().actType += "," + action;
     775            0 :             return;
     776              :         }
     777              :     }
     778         4351 :     if (!addedEdge && (stopEdge != edges.back() || stopPos < lastPos)) {
     779              :         //std::cout << SIMTIME << " stopPos=" << stopPos << " lastPos=" << lastPos << "\n";
     780         3934 :         edges.push_back(stopEdge);
     781              :     }
     782         4351 :     lastPos = stopPos;
     783         4351 :     SUMOVehicleParameter::Stop stop;
     784         4351 :     stop.lane = getStopLane(stopEdge, action)->getID();
     785         4351 :     if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
     786          659 :         stop.startPos = stopPlace->getBeginLanePosition();
     787          659 :         stop.endPos = stopPlace->getEndLanePosition();
     788              :         const SumoXMLTag tag = stopPlace->getElement();
     789          659 :         if (tag == SUMO_TAG_BUS_STOP || tag == SUMO_TAG_TRAIN_STOP) {
     790              :             stop.busstop = stopPlace->getID();
     791          245 :         } else if (tag == SUMO_TAG_PARKING_AREA) {
     792              :             stop.parkingarea = stopPlace->getID();
     793          240 :         } else if (tag == SUMO_TAG_CONTAINER_STOP) {
     794              :             stop.containerstop = stopPlace->getID();
     795              :         }
     796              :     } else {
     797         3692 :         stop.startPos = stopPos;
     798         7384 :         stop.endPos = MAX2(stopPos, MIN2(myHolder.getVehicleType().getLength(), stopEdge->getLength()));
     799              :     }
     800         8702 :     stop.parking = SUMOVehicleParameter::parseParkingType(myHolder.getStringParam("device.taxi.parking", false, "true"));
     801              :     stop.actType = action;
     802         4351 :     stop.index = STOP_INDEX_END;
     803              :     // In case of prebooking if person is not there/ comes to late for pickup set maximum waiting time:
     804         4351 :     SUMOTime earliestPickupTime = res->earliestPickupTime;
     805         4351 :     if (isPickup && earliestPickupTime >= 0) {
     806          138 :         stop.waitUntil = earliestPickupTime;
     807              :         // TODO: replace hard coded extension with parameter
     808          138 :         stop.extension = static_cast<SUMOTime>(3 * 60 * 1000);  // 3mins
     809              :     }
     810         4351 :     stops.push_back(stop);
     811         4351 : }
     812              : 
     813              : 
     814              : MSLane*
     815         4351 : MSDevice_Taxi::getStopLane(const MSEdge* edge, const std::string& action) {
     816         4351 :     const std::vector<MSLane*>* allowedLanes = edge->allowedLanes(myHolder.getVClass());
     817         4351 :     if (allowedLanes == nullptr) {
     818            0 :         throw ProcessError("Taxi vehicle '" + myHolder.getID() + "' cannot stop on edge '" + edge->getID() + "' (" + action + ")");
     819              :     }
     820         4351 :     return allowedLanes->front();
     821              : }
     822              : 
     823              : bool
     824      1109794 : MSDevice_Taxi::isEmpty() {
     825      1109794 :     return myState == EMPTY;
     826              : }
     827              : 
     828              : 
     829              : bool
     830        28611 : MSDevice_Taxi::allowsBoarding(const MSTransportable* t) const {
     831        28611 :     return myCustomers.count(t) != 0;
     832              : }
     833              : 
     834              : 
     835              : void
     836       722933 : MSDevice_Taxi::updateMove(const SUMOTime traveltime, const double travelledDist) {
     837       722933 :     if (myHolder.getPersonNumber() > 0 || myHolder.getContainerNumber() > 0) {
     838       134876 :         myOccupiedDistance += travelledDist;
     839       134876 :         myOccupiedTime += traveltime;
     840              :     }
     841       722933 :     if (isEmpty()) {
     842       529784 :         if (MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     843       527921 :             myIdleAlgorithm->idle(this);
     844       527921 :             if (myRoutingDevice != nullptr) {
     845              :                 // prevent rerouting during idling (#11079)
     846              :                 myRoutingDevice->setActive(false);
     847              :             }
     848         1863 :         } else if (!myReachedServiceEnd) {
     849          222 :             WRITE_WARNINGF(TL("Taxi '%' reaches scheduled end of service at time=%."), myHolder.getID(), time2string(SIMSTEP));
     850           74 :             myReachedServiceEnd = true;
     851              :         }
     852       193149 :     } else if (myRoutingDevice != nullptr) {
     853              :         myRoutingDevice->setActive(true);
     854              :     }
     855       722933 :     if (myHolder.isStopped() && (isEmpty() || MSGlobals::gUseMesoSim) && myHolder.getNextStop().endBoarding > myServiceEnd) {
     856              :         // limit duration of stop (but only for idling-related stops)
     857         3655 :         myHolder.getNextStopMutable().endBoarding = myServiceEnd;
     858              :     }
     859              : #ifdef DEBUG_DISPATCH
     860              :     if (DEBUG_COND && myIsStopped != myHolder.isStopped()) {
     861              :         std::cout << SIMTIME << " updateMove veh=" << myHolder.getID() << " myIsStopped=" << myIsStopped << " myHolderStopped=" << myHolder.isStopped() << " myState=" << myState << "\n";
     862              :     }
     863              : #endif
     864       722933 :     myIsStopped = myHolder.isStopped();
     865       722933 : }
     866              : 
     867              : 
     868              : bool
     869       697801 : MSDevice_Taxi::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
     870              :                           double newPos, double /*newSpeed*/) {
     871       697801 :     updateMove(DELTA_T, newPos - oldPos);
     872       697801 :     return true; // keep the device
     873              : }
     874              : 
     875              : 
     876              : void
     877        25132 : MSDevice_Taxi::notifyMoveInternal(const SUMOTrafficObject& /* veh */,
     878              :                                   const double /* frontOnLane */,
     879              :                                   const double timeOnLane,
     880              :                                   const double /* meanSpeedFrontOnLane */,
     881              :                                   const double /* meanSpeedVehicleOnLane */,
     882              :                                   const double travelledDistanceFrontOnLane,
     883              :                                   const double /* travelledDistanceVehicleOnLane */,
     884              :                                   const double /* meanLengthOnLane */) {
     885        25132 :     updateMove(TIME2STEPS(timeOnLane), travelledDistanceFrontOnLane);
     886        25132 : }
     887              : 
     888              : 
     889              : bool
     890        74786 : MSDevice_Taxi::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
     891        74786 :     if (isEmpty() && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     892        43968 :         myIdleAlgorithm->idle(this);
     893              :     }
     894        74786 :     return true; // keep the device
     895              : }
     896              : 
     897              : 
     898              : void
     899         2597 : MSDevice_Taxi::customerEntered(const MSTransportable* t) {
     900         2597 :     myState |= OCCUPIED;
     901         2597 :     if (!hasFuturePickup()) {
     902         2129 :         myState &= ~PICKUP;
     903              :     }
     904        12138 :     for (const Reservation* res : myCurrentReservations) {
     905        16998 :         for (const MSTransportable* cand : res->persons) {
     906        10048 :             if (cand == t) {
     907         2591 :                 const_cast<Reservation*>(res)->state = Reservation::ONBOARD;
     908         2591 :                 break;
     909              :             }
     910              :         }
     911              :     }
     912         2597 : }
     913              : 
     914              : 
     915              : void
     916         2597 : MSDevice_Taxi::customerArrived(const MSTransportable* person) {
     917         2597 :     myCustomersServed++;
     918              :     myCustomers.erase(person);
     919         2597 :     if (myHolder.getPersonNumber() == 0 && myHolder.getContainerNumber() == 0) {
     920         1785 :         myState &= ~OCCUPIED;
     921         1785 :         if (myHolder.getStops().size() > 1 && (myState & PICKUP) == 0) {
     922            0 :             WRITE_WARNINGF(TL("All customers left vehicle '%' at time=% but there are % remaining stops"),
     923              :                            myHolder.getID(), time2string(SIMSTEP), myHolder.getStops().size() - 1);
     924            0 :             while (myHolder.getStops().size() > 1) {
     925            0 :                 myHolder.abortNextStop(1);
     926              :             }
     927              :         }
     928              :     }
     929         2597 :     if (isEmpty()) {
     930              :         // cleanup
     931         3508 :         for (const Reservation* res : myCurrentReservations) {
     932         1760 :             myDispatcher->fulfilledReservation(res);
     933              :         }
     934              :         myCurrentReservations.clear();
     935         1748 :         checkTaskSwap();
     936         1748 :         if (isEmpty()) {
     937         1744 :             if (MSGlobals::gUseMesoSim && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     938          560 :                 myIdleAlgorithm->idle(this);
     939              :             }
     940              :         }
     941              :     } else {
     942              :         // check whether a single reservation has been fulfilled
     943         5233 :         for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
     944              :             bool fulfilled = true;
     945         5497 :             for (const MSTransportable* t : (*resIt)->persons) {
     946              :                 if (myCustomers.count(t) != 0) {
     947              :                     fulfilled = false;
     948              :                     break;
     949              :                 }
     950              :             }
     951         4384 :             if (fulfilled) {
     952          648 :                 myDispatcher->fulfilledReservation(*resIt);
     953              :                 resIt = myCurrentReservations.erase(resIt);
     954              :             } else {
     955              :                 ++resIt;
     956              :             }
     957              :         }
     958              :     }
     959         2597 : }
     960              : 
     961              : 
     962              : void
     963         1748 : MSDevice_Taxi::checkTaskSwap() {
     964         3496 :     const std::string swapGroup = myHolder.getStringParam("device.taxi.swapGroup", false, "");
     965         1748 :     if (swapGroup != "") {
     966           12 :         SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myDispatcher->getRouter();
     967           12 :         const double stopTime = myHolder.isStopped() ? MAX2(0.0, STEPS2TIME(myHolder.getNextStop().duration)) : 0;
     968              :         double maxSaving = 0;
     969              :         MSDevice_Taxi* bestSwap = nullptr;
     970           36 :         for (MSDevice_Taxi* taxi : myFleet) {
     971           48 :             if (taxi->getHolder().hasDeparted() && taxi->getState() == PICKUP
     972           60 :                     && taxi->getHolder().getStringParam("device.taxi.swapGroup", false, "") == swapGroup) {
     973              :                 SUMOVehicle& veh = taxi->getHolder();
     974            4 :                 const MSStop& stop = veh.getNextStop();
     975            4 :                 ConstMSEdgeVector toPickup(veh.getCurrentRouteEdge(), stop.edge + 1);
     976            4 :                 const double cost = router.recomputeCostsPos(toPickup, &veh, veh.getPositionOnLane(), stop.pars.endPos, SIMSTEP);
     977              :                 ConstMSEdgeVector toPickup2;
     978            4 :                 router.compute(myHolder.getEdge(), myHolder.getPositionOnLane(), *stop.edge, stop.pars.endPos, &myHolder, SIMSTEP, toPickup2, true);
     979            4 :                 if (!toPickup2.empty()) {
     980            4 :                     const double cost2 = router.recomputeCostsPos(toPickup2, &myHolder, myHolder.getPositionOnLane(), stop.pars.endPos, SIMSTEP);
     981            4 :                     const double saving = cost - cost2 - stopTime;
     982              :                     //std::cout << SIMTIME << " taxi=" << getID() << " other=" << veh.getID() << " cost=" << cost << " cost2=" << cost2 << " stopTime=" << stopTime << " saving=" << saving << " route1=" << toString(toPickup) << " route2=" << toString(toPickup2) << "\n";
     983            4 :                     if (saving > maxSaving) {
     984              :                         maxSaving = saving;
     985              :                         bestSwap = taxi;
     986              :                     }
     987              :                 }
     988            4 :             }
     989              :         }
     990           12 :         if (maxSaving > SWAP_THRESHOLD) {
     991              : #ifdef DEBUG_DISPATCH
     992              :             if (DEBUG_COND) {
     993              :                 std::cout << SIMTIME << " taxi=" << myHolder.getID() << " swapWith=" << bestSwap->getHolder().getID() << " saving=" << maxSaving << " lastDispatch=";
     994              :                 for (const Reservation* res : bestSwap->myLastDispatch) {
     995              :                     std::cout << toString(res->persons) << "; ";
     996              :                 }
     997              :                 std::cout << "\n";
     998              :             }
     999              : #endif
    1000            4 :             dispatchShared(bestSwap->myLastDispatch);
    1001              :             bestSwap->myCurrentReservations.clear();
    1002              :             bestSwap->myCustomers.clear();
    1003            4 :             bestSwap->myState = EMPTY;
    1004           12 :             while (bestSwap->getHolder().hasStops()) {
    1005            8 :                 bestSwap->getHolder().abortNextStop();
    1006              :             }
    1007            8 :             for (const Reservation* res : myCurrentReservations) {
    1008            4 :                 myDispatcher->swappedRunning(res, this);
    1009              :             }
    1010              :         }
    1011              :     }
    1012         1748 : }
    1013              : 
    1014              : 
    1015              : bool
    1016         2597 : MSDevice_Taxi::hasFuturePickup() {
    1017         7505 :     for (const auto& stop : myHolder.getStops()) {
    1018         5376 :         if (stop.reached) {
    1019         2593 :             continue;
    1020              :         }
    1021         2783 :         if (stop.pars.permitted.size() > 0) {
    1022              :             return true;
    1023              :         }
    1024              :     }
    1025              :     return false;
    1026              : }
    1027              : 
    1028              : void
    1029         1047 : MSDevice_Taxi::generateOutput(OutputDevice* tripinfoOut) const {
    1030         1047 :     if (tripinfoOut != nullptr) {
    1031         1025 :         tripinfoOut->openTag("taxi");
    1032         1025 :         tripinfoOut->writeAttr("customers", toString(myCustomersServed));
    1033         1025 :         tripinfoOut->writeAttr("occupiedDistance", toString(myOccupiedDistance));
    1034         1025 :         tripinfoOut->writeAttr("occupiedTime", time2string(myOccupiedTime));
    1035         2050 :         tripinfoOut->closeTag();
    1036              :     }
    1037         1047 : }
    1038              : 
    1039              : std::string
    1040         1898 : MSDevice_Taxi::getParameter(const std::string& key) const {
    1041         1898 :     if (key == "customers") {
    1042            0 :         return toString(myCustomersServed);
    1043         1898 :     } else if (key == "occupiedDistance") {
    1044            0 :         return toString(myOccupiedDistance);
    1045         1898 :     } else if (key == "occupiedTime") {
    1046            0 :         return toString(STEPS2TIME(myOccupiedTime));
    1047         1898 :     } else if (key == "state") {
    1048         1819 :         return toString(myState);
    1049           79 :     } else if (key == "currentCustomers") {
    1050           15 :         return joinNamedToStringSorting(myCustomers, " ");
    1051           64 :     } else if (key == "pickUpDuration") {
    1052           64 :         return myHolder.getStringParam("device.taxi.pickUpDuration", false, "0");
    1053           32 :     } else if (key == "dropOffDuration") {
    1054           64 :         return myHolder.getStringParam("device.taxi.dropOffDuration", false, "60");
    1055              :     }
    1056            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
    1057              : }
    1058              : 
    1059              : 
    1060              : void
    1061           20 : MSDevice_Taxi::setParameter(const std::string& key, const std::string& value) {
    1062              :     double doubleValue;
    1063              :     try {
    1064           20 :         doubleValue = StringUtils::toDouble(value);
    1065            0 :     } catch (NumberFormatException&) {
    1066            0 :         throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
    1067            0 :     }
    1068           20 :     if (key == "pickUpDuration" || key == "dropOffDuration") {
    1069              :         // store as generic vehicle parameters
    1070           20 :         ((SUMOVehicleParameter&)myHolder.getParameter()).setParameter("device.taxi." + key, value);
    1071              :     } else {
    1072              :         UNUSED_PARAMETER(doubleValue);
    1073            0 :         throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
    1074              :     }
    1075           20 : }
    1076              : 
    1077              : 
    1078              : void
    1079           20 : MSDevice_Taxi::saveState(OutputDevice& out) const {
    1080           20 :     out.openTag(SUMO_TAG_DEVICE);
    1081           20 :     out.writeAttr(SUMO_ATTR_ID, getID());
    1082           20 :     std::ostringstream internals;
    1083           20 :     internals << myState
    1084           20 :         << " " << myCustomersServed
    1085           40 :         << " " << myOccupiedDistance
    1086           20 :         << " " << myOccupiedTime;
    1087           20 :     out.writeAttr(SUMO_ATTR_STATE, internals.str());
    1088           20 :     if (myCustomers.size() > 0) {
    1089           14 :         out.writeAttr(SUMO_ATTR_CUSTOMERS, joinNamedToStringSorting(myCustomers, " "));
    1090              :     }
    1091           20 :     if (myCurrentReservations.size() > 0) {
    1092           14 :         out.writeAttr(SUMO_ATTR_RESERVATIONS, joinNamedToStringSorting(myCurrentReservations, " "));
    1093              :     }
    1094           20 :     out.closeTag();
    1095           20 : }
    1096              : 
    1097              : 
    1098              : void
    1099           20 : MSDevice_Taxi::loadState(const SUMOSAXAttributes& attrs) {
    1100           20 :     bool ok = true;
    1101           20 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
    1102           20 :     bis >> myState;
    1103           20 :     bis >> myCustomersServed;
    1104           20 :     bis >> myOccupiedDistance;
    1105           20 :     bis >> myOccupiedTime;
    1106           20 :     if (attrs.hasAttribute(SUMO_ATTR_CUSTOMERS)) {
    1107           28 :         for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_CUSTOMERS, getID().c_str(), ok)) {
    1108           14 :             myStateLoadedCustomers[id] = this;
    1109           14 :         }
    1110              :     }
    1111           20 :     if (attrs.hasAttribute(SUMO_ATTR_RESERVATIONS)) {
    1112           28 :         for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_RESERVATIONS, getID().c_str(), ok)) {
    1113           14 :             myStateLoadedReservations[id] = this;
    1114           14 :         }
    1115              :     }
    1116           20 : }
    1117              : 
    1118              : 
    1119              : void
    1120          422 : MSDevice_Taxi::finalizeLoadState() {
    1121          422 :     if (myStateLoadedCustomers.size() > 0) {
    1122           10 :         MSNet* net = MSNet::getInstance();
    1123           10 :         MSTransportableControl* pc = net->hasPersons() ? &net->getPersonControl() : nullptr;
    1124           10 :         MSTransportableControl* cc = net->hasContainers() ? &net->getContainerControl() : nullptr;
    1125           24 :         for (auto item : myStateLoadedCustomers) {
    1126           14 :             MSTransportable* t = nullptr;
    1127           14 :             if (pc != nullptr) {
    1128           14 :                 t = pc->get(item.first);
    1129              :             }
    1130           14 :             if (t == nullptr && cc != nullptr) {
    1131            0 :                 t = cc->get(item.first);
    1132              :             }
    1133           14 :             if (t == nullptr) {
    1134            0 :                 WRITE_ERRORF("Could not find taxi customer '%'. Ensure state contains transportables", item.first);
    1135              :             } else {
    1136           14 :                 item.second->myCustomers.insert(t);
    1137              :             }
    1138              :         }
    1139              :     }
    1140          422 :     if (myStateLoadedReservations.size() > 0) {
    1141              :         assert(myDispatcher != nullptr);
    1142              :         std::map<std::string, const Reservation*> resLookup;
    1143           58 :         for (const Reservation* res : myDispatcher->getReservations()) {
    1144           48 :             resLookup[res->getID()] = res;
    1145           10 :         }
    1146           24 :         for (const Reservation* res : myDispatcher->getRunningReservations()) {
    1147           14 :             resLookup[res->getID()] = res;
    1148           10 :         }
    1149           24 :         for (auto item : myStateLoadedReservations) {
    1150              :             auto it = resLookup.find(item.first);
    1151           14 :             if (it == resLookup.end()) {
    1152            0 :                 WRITE_ERRORF("Could not find taxi reservation '%'.", item.first);
    1153              :             } else {
    1154           14 :                 item.second->myCurrentReservations.insert(it->second);
    1155              :             }
    1156              :         }
    1157              :     }
    1158              :     myStateLoadedCustomers.clear();
    1159              :     myStateLoadedReservations.clear();
    1160          422 : }
    1161              : 
    1162              : 
    1163              : bool
    1164        63843 : MSDevice_Taxi::compatibleLine(const std::string& taxiLine, const std::string& rideLine) {
    1165        74145 :     return ((taxiLine == rideLine && StringUtils::startsWith(rideLine, "taxi") && StringUtils::startsWith(taxiLine, "taxi"))
    1166       117384 :             || (taxiLine == TAXI_SERVICE && StringUtils::startsWith(rideLine, "taxi:"))
    1167       193710 :             || (rideLine == TAXI_SERVICE && StringUtils::startsWith(taxiLine, "taxi:")));
    1168              : }
    1169              : 
    1170              : bool
    1171         4608 : MSDevice_Taxi::compatibleLine(const Reservation* res) {
    1172         4608 :     return compatibleLine(myHolder.getParameter().line, res->line);
    1173              : }
    1174              : 
    1175              : 
    1176              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1