LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Taxi.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 335 372 90.1 %
Date: 2024-04-30 15:40:33 Functions: 34 34 100.0 %

          Line data    Source code
       1             : /****************************************************************************/
       2             : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3             : // Copyright (C) 2013-2024 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             : 
      53             : //#define DEBUG_COND (myHolder.isSelected())
      54             : #define DEBUG_COND (true)
      55             : 
      56             : // ===========================================================================
      57             : // static member variables
      58             : // ===========================================================================
      59             : SUMOTime MSDevice_Taxi::myDispatchPeriod(0);
      60             : /// @brief the dispatch algorithm
      61             : MSDispatch* MSDevice_Taxi::myDispatcher(nullptr);
      62             : /// @brief The repeated call to the dispatcher
      63             : Command* MSDevice_Taxi::myDispatchCommand(nullptr);
      64             : // @brief the list of available taxis
      65             : std::vector<MSDevice_Taxi*> MSDevice_Taxi::myFleet;
      66             : int MSDevice_Taxi::myMaxCapacity(0);
      67             : int MSDevice_Taxi::myMaxContainerCapacity(0);
      68             : 
      69             : #define TAXI_SERVICE "taxi"
      70             : #define TAXI_SERVICE_PREFIX "taxi:"
      71             : 
      72             : // ===========================================================================
      73             : // method definitions
      74             : // ===========================================================================
      75             : // ---------------------------------------------------------------------------
      76             : // static initialisation methods
      77             : // ---------------------------------------------------------------------------
      78             : void
      79       36322 : MSDevice_Taxi::insertOptions(OptionsCont& oc) {
      80       36322 :     oc.addOptionSubTopic("Taxi Device");
      81       72644 :     insertDefaultAssignmentOptions("taxi", "Taxi Device", oc);
      82             : 
      83       72644 :     oc.doRegister("device.taxi.dispatch-algorithm", new Option_String("greedy"));
      84       72644 :     oc.addDescription("device.taxi.dispatch-algorithm", "Taxi Device", TL("The dispatch algorithm [greedy|greedyClosest|greedyShared|routeExtension|traci]"));
      85             : 
      86       72644 :     oc.doRegister("device.taxi.dispatch-algorithm.output", new Option_FileName());
      87       72644 :     oc.addDescription("device.taxi.dispatch-algorithm.output", "Taxi Device", TL("Write information from the dispatch algorithm to FILE"));
      88             : 
      89       72644 :     oc.doRegister("device.taxi.dispatch-algorithm.params", new Option_String(""));
      90       72644 :     oc.addDescription("device.taxi.dispatch-algorithm.params", "Taxi Device", TL("Load dispatch algorithm parameters in format KEY1:VALUE1[,KEY2:VALUE]"));
      91             : 
      92       72644 :     oc.doRegister("device.taxi.dispatch-period", new Option_String("60", "TIME"));
      93       72644 :     oc.addDescription("device.taxi.dispatch-period", "Taxi Device", TL("The period between successive calls to the dispatcher"));
      94             : 
      95       72644 :     oc.doRegister("device.taxi.idle-algorithm", new Option_String("stop"));
      96       72644 :     oc.addDescription("device.taxi.idle-algorithm", "Taxi Device", TL("The behavior of idle taxis [stop|randomCircling]"));
      97             : 
      98       72644 :     oc.doRegister("device.taxi.idle-algorithm.output", new Option_FileName());
      99       72644 :     oc.addDescription("device.taxi.idle-algorithm.output", "Taxi Device", TL("Write information from the idling algorithm to FILE"));
     100       36322 : }
     101             : 
     102             : 
     103             : void
     104     4655769 : MSDevice_Taxi::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
     105     4655769 :     OptionsCont& oc = OptionsCont::getOptions();
     106     9311538 :     if (equippedByDefaultAssignmentOptions(oc, "taxi", v, false)) {
     107             :         // build the device
     108       12759 :         MSDevice_Taxi* device = new MSDevice_Taxi(v, "taxi_" + v.getID());
     109       12753 :         into.push_back(device);
     110       12753 :         myFleet.push_back(device);
     111       12753 :         if (v.getParameter().line == "") {
     112             :             // automatically set the line so that persons are willing to enter
     113             :             // (see MSStageDriving::isWaitingFor)
     114       12633 :             const_cast<SUMOVehicleParameter&>(v.getParameter()).line = TAXI_SERVICE;
     115             :         }
     116       12753 :         if (v.getVClass() != SVC_TAXI) {
     117         126 :             WRITE_WARNINGF(TL("Vehicle '%' with device.taxi should have vClass taxi instead of '%'."), v.getID(), toString(v.getVClass()));
     118             :         }
     119       12753 :         const int personCapacity = v.getVehicleType().getPersonCapacity();
     120       12753 :         const int containerCapacity = v.getVehicleType().getContainerCapacity();
     121       12753 :         myMaxCapacity = MAX2(myMaxCapacity, personCapacity);
     122       12753 :         myMaxContainerCapacity = MAX2(myMaxContainerCapacity, containerCapacity);
     123       12753 :         if (personCapacity < 1 && containerCapacity < 1) {
     124           0 :             WRITE_WARNINGF(TL("Vehicle '%' with personCapacity % and containerCapacity % is not usable as taxi."), v.getID(), toString(personCapacity), toString(containerCapacity));
     125             :         }
     126             :     }
     127     4655763 : }
     128             : 
     129             : 
     130             : void
     131         607 : MSDevice_Taxi::initDispatch() {
     132         607 :     OptionsCont& oc = OptionsCont::getOptions();
     133         607 :     myDispatchPeriod = string2time(oc.getString("device.taxi.dispatch-period"));
     134             :     // init dispatch algorithm
     135         607 :     std::string algo = oc.getString("device.taxi.dispatch-algorithm");
     136         607 :     Parameterised params;
     137        1821 :     params.setParametersStr(OptionsCont::getOptions().getString("device.taxi.dispatch-algorithm.params"), ":", ",");
     138         607 :     if (algo == "greedy") {
     139         351 :         myDispatcher = new MSDispatch_Greedy(params.getParametersMap());
     140         256 :     } else if (algo == "greedyClosest") {
     141          36 :         myDispatcher = new MSDispatch_GreedyClosest(params.getParametersMap());
     142         238 :     } else if (algo == "greedyShared") {
     143          96 :         myDispatcher = new MSDispatch_GreedyShared(params.getParametersMap());
     144         142 :     } else if (algo == "routeExtension") {
     145          28 :         myDispatcher = new MSDispatch_RouteExtension(params.getParametersMap());
     146         128 :     } else if (algo == "traci") {
     147         256 :         myDispatcher = new MSDispatch_TraCI(params.getParametersMap());
     148             :     } else {
     149           0 :         throw ProcessError(TLF("Dispatch algorithm '%' is not known", algo));
     150             :     }
     151         607 :     myDispatchCommand = new StaticCommand<MSDevice_Taxi>(&MSDevice_Taxi::triggerDispatch);
     152             :     // round to next multiple of myDispatchPeriod
     153         607 :     const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
     154        1214 :     const SUMOTime begin = string2time(oc.getString("begin"));
     155         607 :     const SUMOTime delay = (myDispatchPeriod - ((now - begin) % myDispatchPeriod)) % myDispatchPeriod;
     156         607 :     MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myDispatchCommand, now + delay);
     157        1214 : }
     158             : 
     159             : bool
     160       42917 : MSDevice_Taxi::isReservation(const std::set<std::string>& lines) {
     161       85820 :     return lines.size() == 1 && (
     162             :                *lines.begin() == TAXI_SERVICE
     163       80156 :                || StringUtils::startsWith(*lines.begin(), TAXI_SERVICE_PREFIX));
     164             : }
     165             : 
     166             : void
     167        2952 : MSDevice_Taxi::addReservation(MSTransportable* person,
     168             :                               const std::set<std::string>& lines,
     169             :                               SUMOTime reservationTime,
     170             :                               SUMOTime pickupTime,
     171             :                               SUMOTime earliestPickupTime,
     172             :                               const MSEdge* from, double fromPos,
     173             :                               const MSStoppingPlace* fromStop,
     174             :                               const MSEdge* to, double toPos,
     175             :                               const MSStoppingPlace* toStop,
     176             :                               const std::string& group) {
     177        2952 :     if (!isReservation(lines)) {
     178             :         return;
     179             :     }
     180        2952 :     if ((to->getPermissions() & SVC_TAXI) == 0) {
     181           0 :         throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
     182           0 :                            + " '" + person->getID() + "' because destination edge '" + to->getID() + "'"
     183           0 :                            + " does not permit taxi access");
     184             :     }
     185        2952 :     if ((from->getPermissions() & SVC_TAXI) == 0) {
     186           0 :         throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
     187           0 :                            + " '" + person->getID() + "' because origin edge '" + from->getID() + "'"
     188           0 :                            + " does not permit taxi access");
     189             :     }
     190        2952 :     if (myDispatchCommand == nullptr) {
     191         607 :         initDispatch();
     192             :     }
     193        5904 :     myDispatcher->addReservation(person, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, *lines.begin(), myMaxCapacity, myMaxContainerCapacity);
     194             : }
     195             : 
     196             : void
     197         135 : MSDevice_Taxi::removeReservation(MSTransportable* person,
     198             :                                  const std::set<std::string>& lines,
     199             :                                  const MSEdge* from, double fromPos,
     200             :                                  const MSEdge* to, double toPos,
     201             :                                  const std::string& group) {
     202         254 :     if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
     203         238 :         myDispatcher->removeReservation(person, from, fromPos, to, toPos, group);
     204             :     }
     205         135 : }
     206             : 
     207             : void
     208          78 : MSDevice_Taxi::updateReservationFromPos(MSTransportable* person,
     209             :                                         const std::set<std::string>& lines,
     210             :                                         const MSEdge* from, double fromPos,
     211             :                                         const MSEdge* to, double toPos,
     212             :                                         const std::string& group, double newFromPos) {
     213         144 :     if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
     214         132 :         myDispatcher->updateReservationFromPos(person, from, fromPos, to, toPos, group, newFromPos);
     215             :     }
     216          78 : }
     217             : 
     218             : 
     219             : SUMOTime
     220        6035 : MSDevice_Taxi::triggerDispatch(SUMOTime currentTime) {
     221             :     std::vector<MSDevice_Taxi*> active;
     222       14930 :     for (MSDevice_Taxi* taxi : myFleet) {
     223        8895 :         if (taxi->getHolder().hasDeparted()) {
     224        8799 :             active.push_back(taxi);
     225             :         }
     226             :     }
     227        6035 :     myDispatcher->computeDispatch(currentTime, active);
     228       11733 :     return myDispatchPeriod;
     229             : }
     230             : 
     231             : bool
     232       41140 : MSDevice_Taxi::hasServableReservations() {
     233       41140 :     return myDispatcher != nullptr && myDispatcher->hasServableReservations();
     234             : }
     235             : 
     236             : void
     237       35160 : MSDevice_Taxi::cleanup() {
     238       35160 :     if (myDispatcher != nullptr) {
     239         607 :         delete myDispatcher;
     240         607 :         myDispatcher = nullptr;
     241             :     }
     242       35160 :     myDispatchCommand = nullptr;
     243       35160 : }
     244             : 
     245             : // ---------------------------------------------------------------------------
     246             : // MSDevice_Taxi-methods
     247             : // ---------------------------------------------------------------------------
     248       12759 : MSDevice_Taxi::MSDevice_Taxi(SUMOVehicle& holder, const std::string& id) :
     249       12759 :     MSVehicleDevice(holder, id) {
     250       12765 :     std::string defaultServiceEnd = toString(1e15);
     251       25524 :     const std::string algo = getStringParam(holder, OptionsCont::getOptions(), "taxi.idle-algorithm", "", false);
     252       12759 :     if (algo == "stop") {
     253       12625 :         myIdleAlgorithm = new MSIdling_Stop();
     254         134 :     } else if (algo == "randomCircling") {
     255          74 :         myIdleAlgorithm = new MSIdling_RandomCircling();
     256             :         // make sure simulation terminates
     257          74 :         defaultServiceEnd = toString(STEPS2TIME(
     258             :                                          myHolder.getParameter().departProcedure == DepartDefinition::GIVEN
     259             :                                          ? myHolder.getParameter().depart
     260         148 :                                          : MSNet::getInstance()->getCurrentTimeStep()) + (3600 * 8));
     261          60 :     } else if (algo == "taxistand") {
     262         108 :         const std::string rerouterID = getStringParam(holder, OptionsCont::getOptions(), "taxi.stands-rerouter", "", false);
     263          54 :         if (rerouterID.empty()) {
     264           0 :             throw ProcessError("Idle algorithm '" + algo + "' requires a rerouter id to be defined using device param 'stands-rerouter' for vehicle '" + myHolder.getID() + "'");
     265             :         }
     266             :         if (MSTriggeredRerouter::getInstances().count(rerouterID) == 0) {
     267           0 :             throw ProcessError("Unknown rerouter '" + rerouterID + "' when loading taxi stands for vehicle '" + myHolder.getID() + "'");
     268             :         }
     269          54 :         MSTriggeredRerouter* rerouter = MSTriggeredRerouter::getInstances().find(rerouterID)->second;
     270          54 :         myIdleAlgorithm = new MSIdling_TaxiStand(rerouter);
     271             :     } else {
     272          12 :         throw ProcessError("Idle algorithm '" + algo + "' is not known for vehicle '" + myHolder.getID() + "'");
     273             :     }
     274       25390 :     myServiceEnd = string2time(getStringParam(holder, OptionsCont::getOptions(), "taxi.end", defaultServiceEnd, false));
     275       12753 :     myRoutingDevice = static_cast<MSDevice_Routing*>(myHolder.getDevice(typeid(MSDevice_Routing)));
     276       12759 : }
     277             : 
     278             : 
     279       25506 : MSDevice_Taxi::~MSDevice_Taxi() {
     280       12753 :     myFleet.erase(std::find(myFleet.begin(), myFleet.end(), this));
     281             :     // recompute myMaxCapacity
     282       12753 :     myMaxCapacity = 0;
     283       12753 :     myMaxContainerCapacity = 0;
     284       34262 :     for (MSDevice_Taxi* taxi : myFleet) {
     285       21509 :         myMaxCapacity = MAX2(myMaxCapacity, taxi->getHolder().getVehicleType().getPersonCapacity());
     286       21509 :         myMaxContainerCapacity = MAX2(myMaxContainerCapacity, taxi->getHolder().getVehicleType().getContainerCapacity());
     287             :     }
     288       12753 :     delete myIdleAlgorithm;
     289       25506 : }
     290             : 
     291             : 
     292             : SUMOVehicle*
     293        3398 : MSDevice_Taxi::getTaxi() {
     294        3398 :     if (myFleet.size() > 0) {
     295         150 :         return &myFleet[0]->getHolder();
     296             :     } else {
     297             :         return nullptr;
     298             :     }
     299             : }
     300             : 
     301             : 
     302             : void
     303         873 : MSDevice_Taxi::dispatch(const Reservation& res) {
     304         873 :     dispatchShared({&res, &res});
     305         871 : }
     306             : 
     307             : 
     308             : void
     309        1217 : MSDevice_Taxi::dispatchShared(std::vector<const Reservation*> reservations) {
     310             : #ifdef DEBUG_DISPATCH
     311             :     if (DEBUG_COND) {
     312             :         std::cout << SIMTIME << " taxi=" << myHolder.getID() << " dispatch:\n";
     313             :         for (const Reservation* res : reservations) {
     314             :             std::cout << "   persons=" << toString(res->persons) << "\n";
     315             :         }
     316             :     }
     317             : #endif
     318        1217 :     myLastDispatch = reservations;
     319             :     ConstMSEdgeVector tmpEdges;
     320             :     std::vector<SUMOVehicleParameter::Stop> stops;
     321        1217 :     double lastPos = myHolder.getPositionOnLane();
     322        1217 :     const MSEdge* rerouteOrigin = *myHolder.getRerouteOrigin();
     323        1217 :     if (isEmpty()) {
     324             :         // start fresh from the current edge
     325        2126 :         while (myHolder.hasStops()) {
     326             :             // in meso there might be more than 1 stop at this point
     327        1023 :             myHolder.abortNextStop();
     328             :         }
     329             :         assert(!myHolder.hasStops());
     330        1103 :         tmpEdges.push_back(myHolder.getEdge());
     331        1103 :         if (myHolder.getEdge() != rerouteOrigin) {
     332           8 :             tmpEdges.push_back(rerouteOrigin);
     333             :         }
     334             :     } else {
     335             :         assert(myHolder.hasStops());
     336             :         // check how often existing customers appear in the new reservations
     337             :         std::map<const MSTransportable*, int> nOccur;
     338         782 :         for (const Reservation* res : reservations) {
     339        1336 :             for (const MSTransportable* person : res->persons) {
     340             :                 if (myCustomers.count(person) != 0) {
     341         326 :                     nOccur[person] += 1;
     342             :                     if (myCurrentReservations.count(res) == 0) {
     343           0 :                         throw ProcessError(TLF("Invalid Re-dispatch for existing customer '%' with a new reservation", person->getID()));
     344             :                     }
     345             :                 }
     346             :             }
     347             :         }
     348             : #ifdef DEBUG_DISPATCH
     349             :         if (DEBUG_COND) {
     350             :             for (auto item : nOccur) {
     351             :                 std::cout << "   previousCustomer=" << item.first->getID() << " occurs=" << item.second << "\n";
     352             :             }
     353             :         }
     354             : #endif
     355         114 :         if (nOccur.size() == 0) {
     356             :             // no overlap with existing customers - extend route
     357          12 :             tmpEdges = myHolder.getRoute().getEdges();
     358          12 :             lastPos = myHolder.getStops().back().pars.endPos;
     359             : #ifdef DEBUG_DISPATCH
     360             :             if (DEBUG_COND) {
     361             :                 std::cout << " re-dispatch with route-extension\n";
     362             :             }
     363             : #endif
     364         102 :         } else if (nOccur.size() == myCustomers.size()) {
     365             :             // redefine route (verify correct number of mentions)
     366             :             std::set<const MSTransportable*> onBoard;
     367         102 :             const std::vector<MSTransportable*>& onBoardP = myHolder.getPersons();
     368         102 :             const std::vector<MSTransportable*>& onBoardC = myHolder.getContainers();
     369             :             onBoard.insert(onBoardP.begin(), onBoardP.end());
     370             :             onBoard.insert(onBoardC.begin(), onBoardC.end());
     371             :             std::set<const MSTransportable*> redundantPickup;
     372         295 :             for (auto item : nOccur) {
     373         198 :                 if (item.second == 1) {
     374             :                     // customers must already be on board
     375             :                     if (onBoard.count(item.first) == 0) {
     376          20 :                         throw ProcessError(TLF("Re-dispatch did not mention pickup for existing customer '%'", item.first->getID()));
     377             :                     }
     378         128 :                 } else if (item.second == 2) {
     379             :                     if (onBoard.count(item.first) == 0) {
     380             :                         // treat like a new customer
     381             :                         // TODO: need to be checked
     382             :                         myCustomers.erase(item.first);
     383             :                     } else {
     384             :                         redundantPickup.insert(item.first);
     385             :                     }
     386             :                 } else {
     387           0 :                     throw ProcessError("Re-dispatch mentions existing customer '" + item.first->getID() + "' " + toString(item.second) + " times");
     388             :                 }
     389             :             }
     390             :             // remove redundancy
     391          97 :             if (!redundantPickup.empty()) {
     392          95 :                 for (auto it = reservations.begin(); it != reservations.end();) {
     393             :                     bool isRedundant = false;
     394         138 :                     for (const MSTransportable* person : (*it)->persons) {
     395             :                         if (redundantPickup.count(person) != 0) {
     396             :                             isRedundant = true;
     397             :                             break;
     398             :                         }
     399             :                     }
     400          78 :                     if (isRedundant) {
     401          36 :                         for (const MSTransportable* person : (*it)->persons) {
     402             :                             redundantPickup.erase(person);
     403             :                         }
     404             :                         it = reservations.erase(it);
     405             :                     } else {
     406             :                         it++;
     407             :                     }
     408             :                 }
     409             :             }
     410         407 :             while (myHolder.hasStops()) {
     411         310 :                 myHolder.abortNextStop();
     412             :             }
     413         102 :             tmpEdges.push_back(myHolder.getEdge());
     414          97 :             if (myHolder.getEdge() != rerouteOrigin) {
     415          11 :                 tmpEdges.push_back(rerouteOrigin);
     416             :             }
     417             : #ifdef DEBUG_DISPATCH
     418             :             if (DEBUG_COND) {
     419             :                 std::cout << " re-dispatch from scratch\n";
     420             :             }
     421             : #endif
     422             :         } else {
     423             :             // inconsistent re-dispatch
     424             :             std::vector<std::string> missing;
     425           0 :             for (const MSTransportable* c : myCustomers) {
     426             :                 if (nOccur.count(c) == 0) {
     427           0 :                     missing.push_back(c->getID());
     428             :                 }
     429             :             }
     430           0 :             throw ProcessError("Re-dispatch did mention some customers but failed to mention " + joinToStringSorting(missing, " "));
     431           0 :         }
     432             :     }
     433             : 
     434        1212 :     const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
     435             :     bool hasPickup = false;
     436        5623 :     for (const Reservation* res : reservations) {
     437             :         myCurrentReservations.insert(res);
     438             :         bool isPickup = false;
     439        9026 :         for (const MSTransportable* person : res->persons) {
     440             :             if (myCustomers.count(person) == 0) {
     441             :                 myCustomers.insert(person);
     442             :                 isPickup = true;
     443             :                 hasPickup = true;
     444             :             }
     445             :         }
     446        4411 :         if (isPickup) {
     447        4328 :             prepareStop(tmpEdges, stops, lastPos, res->from, res->fromPos, res->fromStop, "pickup " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
     448        4430 :             for (const MSTransportable* const transportable : res->persons) {
     449        2266 :                 if (transportable->isPerson()) {
     450        2224 :                     stops.back().triggered = true;
     451             :                 } else {
     452          42 :                     stops.back().containerTriggered = true;
     453             :                 }
     454             :                 stops.back().permitted.insert(transportable->getID());
     455             :             }
     456             :             // proof this lines: Is needed for pre-booking?
     457        2164 :             std::set<const MSTransportable*> persons = res->persons;
     458        4430 :             for (auto itr = persons.begin(); itr != persons.end(); itr++) {
     459        2266 :                 stops.back().awaitedPersons.insert((*itr)->getID());
     460             :             }
     461             :             
     462        2164 :             stops.back().parametersSet |= STOP_PERMITTED_SET;
     463        2164 :             if (stops.back().duration == -1) {
     464             :                 // keep dropOffDuration if the stop is dropOff and pickUp
     465        4932 :                 stops.back().duration = TIME2STEPS(getFloatParam(myHolder, OptionsCont::getOptions(), "taxi.pickUpDuration", 0, false));
     466             :             }
     467             :         } else {
     468        4494 :             prepareStop(tmpEdges, stops, lastPos, res->to, res->toPos, res->toStop, "dropOff " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
     469        6741 :             stops.back().duration = TIME2STEPS(getFloatParam(myHolder, OptionsCont::getOptions(), "taxi.dropOffDuration", 60, false)); // pay and collect bags
     470             :         }
     471             :     }
     472             : #ifdef DEBUG_DISPATCH
     473             :     if (DEBUG_COND) {
     474             :         std::cout << "   tmpEdges=" << toString(tmpEdges) << "\n";
     475             :     }
     476             : #endif
     477        2424 :     if (!myHolder.replaceRouteEdges(tmpEdges, -1, 0, "taxi:prepare_dispatch", false, false, false)) {
     478           0 :         throw ProcessError("Route replacement for taxi dispatch failed for vehicle '" + myHolder.getID()
     479           0 :                            + "' at time=" + time2string(t) + ".");
     480             :     }
     481             : #ifdef DEBUG_DISPATCH
     482             :     if (DEBUG_COND) std::cout << "   replacedRoute=" << toString(tmpEdges)
     483             :                                   << "\n     actualRoute=" << toString(myHolder.getRoute().getEdges()) << "\n";
     484             : #endif
     485        4360 :     for (SUMOVehicleParameter::Stop& stop : stops) {
     486             :         std::string error;
     487        3148 :         myHolder.addStop(stop, error);
     488        3148 :         if (error != "") {
     489           0 :             WRITE_WARNINGF(TL("Could not add taxi stop for vehicle '%' to %. time=% error=%."), myHolder.getID(), stop.actType, time2string(t), error)
     490             :         }
     491             :     }
     492        1212 :     SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
     493             :     // SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myHolder.getInfluencer().getRouterTT(veh->getRNGIndex())
     494        1212 :     myHolder.reroute(t, "taxi:dispatch", router, false);
     495             : #ifdef DEBUG_DISPATCH
     496             :     if (DEBUG_COND) {
     497             :         std::cout << "\n      finalRoute=" << toString(myHolder.getRoute().getEdges()) << " routeIndex=" << myHolder.getRoutePosition() << "\n";
     498             :     }
     499             : #endif
     500        1210 :     if (hasPickup) {
     501        1194 :         myState |= PICKUP;
     502             :     }
     503        2427 : }
     504             : 
     505             : 
     506             : void
     507         100 : MSDevice_Taxi::cancelCurrentCustomers() {
     508             :     // check if taxi has stopped
     509         100 :     if (myHolder.getNextStopParameter() == nullptr) {
     510           0 :         return;
     511             :     }
     512             :     // find customers of the current stop
     513             :     std::set<const MSTransportable*> customersToBeRemoved;
     514         112 :     for (std::string tID : myHolder.getNextStopParameter()->permitted) {
     515             :         //for (const MSTransportable* t : myCustomers) {
     516          24 :         for (auto t : myCustomers) {
     517             :             //bool removed = false;
     518             :             //if (t->getID() == tID) {
     519          12 :             if (t->getID() == tID) {
     520             :                 //cancelCustomer(t);
     521             :                 customersToBeRemoved.insert(t);
     522             :                 //removed = cancelCustomer((*tIt));
     523             :                 
     524             :             }
     525             :             //if (!removed) {
     526             :             //    tIt++;
     527             :             //}
     528             :         }
     529             :     }
     530         112 :     for (auto t : customersToBeRemoved) {
     531          12 :         cancelCustomer(t);
     532             :     }
     533             : }
     534             : 
     535             : 
     536             : bool
     537          12 : MSDevice_Taxi::cancelCustomer(const MSTransportable* t) {
     538             :     // is the given transportable a customer of the reservations?
     539             :     if (myCustomers.count(t) == 0) {
     540             :         return false;
     541             :     }
     542             :     myCustomers.erase(t);
     543             :     // check whether a single reservation has been fulfilled or another customer is part of the reservation
     544          24 :     for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
     545             :         bool fulfilled = false;
     546          12 :         if ((*resIt)->persons.size() == 1 && (*resIt)->persons.count(t) != 0) {
     547             :             // the reservation contains only the customer
     548             :             fulfilled = true;
     549             :         }
     550             :         if (fulfilled) {
     551             :             // delete the reservation
     552          12 :             myDispatcher->fulfilledReservation(*resIt);
     553             :             // remove reservation from the current dispatch
     554          36 :             for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
     555          24 :                 if (*it == *resIt)
     556          24 :                     it = myLastDispatch.erase(it);
     557             :                 else
     558             :                     ++it;
     559             :             }
     560             :             // remove reservation from the served reservations
     561             :             resIt = myCurrentReservations.erase(resIt);
     562             :         }
     563             :         else {
     564             :             ++resIt;
     565             :         }
     566             :     }
     567          12 :     myState &= ~PICKUP;  // remove state PICKUP
     568          12 :     for (const Reservation* res : myCurrentReservations) {
     569             :         // if there is another pickup in the dispatch left, add the state PICKUP
     570           0 :         if (std::count(myLastDispatch.begin(), myLastDispatch.end(), res) == 2) {
     571           0 :             myState |= PICKUP;  // add state PICKUP
     572             :         }
     573             :     }
     574             :     // if there are reservations left, go on with the dispatch
     575             :     // in meso, wait for the next dispatch cycle to avoid updating stops in this stage
     576          12 :     if (!MSGlobals::gUseMesoSim) {
     577          16 :         dispatchShared(myLastDispatch);
     578             :     }
     579             :     return true;
     580             : }
     581             : 
     582             : 
     583             : void
     584        4411 : MSDevice_Taxi::prepareStop(ConstMSEdgeVector& edges,
     585             :                            std::vector<SUMOVehicleParameter::Stop>& stops,
     586             :                            double& lastPos, const MSEdge* stopEdge, double stopPos,
     587             :                            const MSStoppingPlace* stopPlace,
     588             :                            const std::string& action, const Reservation* res, const bool isPickup) {
     589             :     assert(!edges.empty());
     590        4411 :     if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
     591         822 :         stopPos = stopPlace->getEndLanePosition();
     592             :     }
     593        4411 :     if (stopPos < lastPos && stopPos + NUMERICAL_EPS >= lastPos) {
     594             :         stopPos = lastPos;
     595             :     }
     596             : 
     597        4411 :     if (stops.empty()) {
     598             :         // check brakeGap
     599        1204 :         double distToStop = stopPos - lastPos;
     600        1204 :         const double brakeGap = myHolder.getBrakeGap();
     601        1204 :         if (myHolder.getLane() != nullptr && myHolder.getLane()->isInternal()) {
     602           6 :             distToStop += myHolder.getLane()->getLength();
     603             :         }
     604        1204 :         if (stopEdge != edges.back()) {
     605        1133 :             distToStop += edges.back()->getLength();
     606        1133 :             if (distToStop < brakeGap) {
     607             :                 // the distance between current edge and stop edge may be small
     608          12 :                 SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
     609             :                 ConstMSEdgeVector toFirstStop;
     610           6 :                 router.compute(edges.back(), stopEdge, &myHolder, SIMSTEP, toFirstStop, true);
     611          30 :                 for (int i = 1; i < (int)toFirstStop.size() - 1; i++) {
     612          24 :                     distToStop += toFirstStop[i]->getLength();
     613             :                 }
     614             :             }
     615             :         }
     616        1204 :         if (distToStop < brakeGap) {
     617             :             // circle back to stopEdge
     618             :             //std::cout << SIMTIME << " taxi=" << getID() << " brakeGap=" << brakeGap << " distToStop=" << distToStop << "\n";
     619          31 :             edges.push_back(stopEdge);
     620             :         }
     621             :     }
     622             : 
     623        4411 :     if (stopEdge == edges.back() && !stops.empty()) {
     624        1315 :         if (stopPos >= lastPos && stopPos <= stops.back().endPos) {
     625             :             // no new stop and no adaption needed
     626        1263 :             stops.back().actType += "," + action;
     627        1263 :             return;
     628             :         }
     629          52 :         if (stopPos >= lastPos && stopPos <= lastPos + myHolder.getVehicleType().getLength()) {
     630             :             // stop length adaption needed
     631           0 :             stops.back().endPos = MIN2(lastPos + myHolder.getVehicleType().getLength(), stopEdge->getLength());
     632           0 :             stops.back().actType += "," + action;
     633           0 :             return;
     634             :         }
     635             :     }
     636        3148 :     if (stopEdge != edges.back() || stopPos < lastPos) {
     637        3076 :         edges.push_back(stopEdge);
     638             :     }
     639        3148 :     lastPos = stopPos;
     640        3148 :     SUMOVehicleParameter::Stop stop;
     641        3148 :     stop.lane = getStopLane(stopEdge, action)->getID();
     642        3148 :     if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
     643         364 :         stop.startPos = stopPlace->getBeginLanePosition();
     644         364 :         stop.endPos = stopPlace->getEndLanePosition();
     645             :     } else {
     646        2784 :         stop.startPos = stopPos;
     647        5568 :         stop.endPos = MAX2(stopPos, MIN2(myHolder.getVehicleType().getLength(), stopEdge->getLength()));
     648             :     }
     649        3148 :     stop.parking = SUMOVehicleParameter::parseParkingType(getStringParam(myHolder, OptionsCont::getOptions(), "taxi.parking", "true", false));
     650             :     stop.actType = action;
     651        3148 :     stop.index = STOP_INDEX_END;
     652             :     // In case of prebooking if person is not there/ comes to late for pickup set maximum waiting time:
     653        3148 :     SUMOTime earliestPickupTime = res->earliestPickupTime;
     654        3148 :     if (isPickup && earliestPickupTime >= 0) {
     655          78 :         stop.waitUntil = earliestPickupTime;
     656             :         // TODO: replace hard coded extension with parameter
     657          78 :         stop.extension = static_cast<SUMOTime>(3 * 60 * 1000);  // 3mins
     658             :     }
     659        3148 :     stops.push_back(stop);
     660        3148 : }
     661             : 
     662             : 
     663             : MSLane*
     664        3148 : MSDevice_Taxi::getStopLane(const MSEdge* edge, const std::string& action) {
     665        3148 :     const std::vector<MSLane*>* allowedLanes = edge->allowedLanes(myHolder.getVClass());
     666        3148 :     if (allowedLanes == nullptr) {
     667           0 :         throw ProcessError("Taxi vehicle '" + myHolder.getID() + "' cannot stop on edge '" + edge->getID() + "' (" + action + ")");
     668             :     }
     669        3148 :     return allowedLanes->front();
     670             : }
     671             : 
     672             : bool
     673      273855 : MSDevice_Taxi::isEmpty() {
     674      273855 :     return myState == EMPTY;
     675             : }
     676             : 
     677             : 
     678             : bool
     679        4859 : MSDevice_Taxi::allowsBoarding(const MSTransportable* t) const {
     680        4859 :     return myCustomers.count(t) != 0;
     681             : }
     682             : 
     683             : 
     684             : void
     685      226316 : MSDevice_Taxi::updateMove(const SUMOTime traveltime, const double travelledDist) {
     686      226316 :     if (myHolder.getPersonNumber() > 0 || myHolder.getContainerNumber() > 0) {
     687       85265 :         myOccupiedDistance += travelledDist;
     688       85265 :         myOccupiedTime += traveltime;
     689             :     }
     690      226316 :     if (isEmpty()) {
     691      107704 :         if (MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     692      106352 :             myIdleAlgorithm->idle(this);
     693      106352 :             if (myRoutingDevice != nullptr) {
     694             :                 // prevent rerouting during idling (#11079)
     695             :                 myRoutingDevice->setActive(false);
     696             :             }
     697        1352 :         } else if (!myReachedServiceEnd) {
     698         180 :             WRITE_WARNINGF(TL("Taxi '%' reaches scheduled end of service at time=%."), myHolder.getID(), time2string(SIMSTEP));
     699          60 :             myReachedServiceEnd = true;
     700             :         }
     701      118612 :     } else if (myRoutingDevice != nullptr) {
     702             :         myRoutingDevice->setActive(true);
     703             :     }
     704      226316 :     if (myHolder.isStopped()) {
     705        5444 :         if (!myIsStopped) {
     706             :             // limit duration of stop
     707             :             // @note: stops are not yet added to the vehicle so we can change the loaded parameters. Stops added from a route are not affected
     708             :             // @note: that's not true for pre-booking, so check whether endBoarding is already set
     709             :             //myHolder.getNextStop().endBoarding = myServiceEnd;
     710        3224 :             if (myHolder.getNextStop().endBoarding > myServiceEnd) {
     711        3146 :                 myHolder.getNextStop().endBoarding = myServiceEnd;
     712             :             }
     713             :         }
     714             :     }
     715             : #ifdef DEBUG_DISPATCH
     716             :     if (DEBUG_COND && myIsStopped != myHolder.isStopped()) {
     717             :         std::cout << SIMTIME << " updateMove veh=" << myHolder.getID() << " myIsStopped=" << myIsStopped << " myHolderStopped=" << myHolder.isStopped() << " myState=" << myState << "\n";
     718             :     }
     719             : #endif
     720      226316 :     myIsStopped = myHolder.isStopped();
     721      226316 : }
     722             : 
     723             : 
     724             : bool
     725      217138 : MSDevice_Taxi::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
     726             :                           double newPos, double /*newSpeed*/) {
     727      217138 :     updateMove(DELTA_T, newPos - oldPos);
     728      217138 :     return true; // keep the device
     729             : }
     730             : 
     731             : 
     732             : void
     733        9178 : MSDevice_Taxi::notifyMoveInternal(const SUMOTrafficObject& /* veh */,
     734             :                                   const double /* frontOnLane */,
     735             :                                   const double timeOnLane,
     736             :                                   const double /* meanSpeedFrontOnLane */,
     737             :                                   const double /* meanSpeedVehicleOnLane */,
     738             :                                   const double travelledDistanceFrontOnLane,
     739             :                                   const double /* travelledDistanceVehicleOnLane */,
     740             :                                   const double /* meanLengthOnLane */) {
     741        9178 :     updateMove(TIME2STEPS(timeOnLane), travelledDistanceFrontOnLane);
     742        9178 : }
     743             : 
     744             : 
     745             : bool
     746       37642 : MSDevice_Taxi::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
     747       37642 :     if (isEmpty() && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     748       18516 :         myIdleAlgorithm->idle(this);
     749             :     }
     750       37642 :     return true; // keep the device
     751             : }
     752             : 
     753             : 
     754             : void
     755        2108 : MSDevice_Taxi::customerEntered(const MSTransportable* t) {
     756        2108 :     myState |= OCCUPIED;
     757        2108 :     if (!hasFuturePickup()) {
     758        1408 :         myState &= ~PICKUP;
     759             :     }
     760       17002 :     for (const Reservation* res : myCurrentReservations) {
     761       27872 :         for (const MSTransportable* cand : res->persons) {
     762       15086 :             if (cand == t) {
     763        2108 :                 const_cast<Reservation*>(res)->state = Reservation::ONBOARD;
     764        2108 :                 break;
     765             :             }
     766             :         }
     767             :     }
     768        2108 : }
     769             : 
     770             : 
     771             : void
     772        2108 : MSDevice_Taxi::customerArrived(const MSTransportable* person) {
     773        2108 :     myCustomersServed++;
     774             :     myCustomers.erase(person);
     775        2108 :     if (myHolder.getPersonNumber() == 0 && myHolder.getContainerNumber() == 0) {
     776        1111 :         myState &= ~OCCUPIED;
     777        1111 :         if (myHolder.getStops().size() > 1 && (myState & PICKUP) == 0) {
     778           0 :             WRITE_WARNINGF(TL("All customers left vehicle '%' at time=% but there are % remaining stops"),
     779             :                            myHolder.getID(), time2string(SIMSTEP), myHolder.getStops().size() - 1);
     780           0 :             while (myHolder.getStops().size() > 1) {
     781           0 :                 myHolder.abortNextStop(1);
     782             :             }
     783             :         }
     784             :     }
     785        2108 :     if (isEmpty()) {
     786             :         // cleanup
     787        2128 :         for (const Reservation* res : myCurrentReservations) {
     788        1064 :             myDispatcher->fulfilledReservation(res);
     789             :         }
     790             :         myCurrentReservations.clear();
     791        1064 :         if (MSGlobals::gUseMesoSim && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
     792         302 :             myIdleAlgorithm->idle(this);
     793             :         }
     794             :     } else {
     795             :         // check whether a single reservation has been fulfilled
     796        8601 :         for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
     797             :             bool fulfilled = true;
     798        8687 :             for (const MSTransportable* t : (*resIt)->persons) {
     799             :                 if (myCustomers.count(t) != 0) {
     800             :                     fulfilled = false;
     801             :                     break;
     802             :                 }
     803             :             }
     804        7557 :             if (fulfilled) {
     805         942 :                 myDispatcher->fulfilledReservation(*resIt);
     806             :                 resIt = myCurrentReservations.erase(resIt);
     807             :             } else {
     808             :                 ++resIt;
     809             :             }
     810             :         }
     811             :     }
     812        2108 : }
     813             : 
     814             : 
     815             : bool
     816        2108 : MSDevice_Taxi::hasFuturePickup() {
     817        5818 :     for (const auto& stop : myHolder.getStops()) {
     818        4410 :         if (stop.reached) {
     819        2110 :             continue;
     820             :         }
     821        2300 :         if (stop.pars.permitted.size() > 0) {
     822             :             return true;
     823             :         }
     824             :     }
     825             :     return false;
     826             : }
     827             : 
     828             : void
     829         693 : MSDevice_Taxi::generateOutput(OutputDevice* tripinfoOut) const {
     830         693 :     if (tripinfoOut != nullptr) {
     831         693 :         tripinfoOut->openTag("taxi");
     832        1386 :         tripinfoOut->writeAttr("customers", toString(myCustomersServed));
     833        1386 :         tripinfoOut->writeAttr("occupiedDistance", toString(myOccupiedDistance));
     834        1386 :         tripinfoOut->writeAttr("occupiedTime", time2string(myOccupiedTime));
     835        1386 :         tripinfoOut->closeTag();
     836             :     }
     837         693 : }
     838             : 
     839             : std::string
     840        1424 : MSDevice_Taxi::getParameter(const std::string& key) const {
     841        1424 :     if (key == "customers") {
     842           0 :         return toString(myCustomersServed);
     843        1424 :     } else if (key == "occupiedDistance") {
     844           0 :         return toString(myOccupiedDistance);
     845        1424 :     } else if (key == "occupiedTime") {
     846           0 :         return toString(STEPS2TIME(myOccupiedTime));
     847        1424 :     } else if (key == "state") {
     848        1288 :         return toString(myState);
     849         136 :     } else if (key == "currentCustomers") {
     850          18 :         return joinNamedToStringSorting(myCustomers, " ");
     851         118 :     } else if (key == "pickUpDuration") {
     852         118 :         return getStringParam(myHolder, OptionsCont::getOptions(), "taxi.pickUpDuration", "0", false);
     853          59 :     } else if (key == "dropOffDuration") {
     854         118 :         return getStringParam(myHolder, OptionsCont::getOptions(), "taxi.dropOffDuration", "60", false);
     855             :     }
     856           0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     857             : }
     858             : 
     859             : 
     860             : void
     861          12 : MSDevice_Taxi::setParameter(const std::string& key, const std::string& value) {
     862             :     double doubleValue;
     863             :     try {
     864          12 :         doubleValue = StringUtils::toDouble(value);
     865           0 :     } catch (NumberFormatException&) {
     866           0 :         throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
     867           0 :     }
     868          18 :     if (key == "pickUpDuration" || key == "dropOffDuration") {
     869             :         // store as generic vehicle parameters
     870          12 :         ((SUMOVehicleParameter&)myHolder.getParameter()).setParameter("device.taxi." + key, value);
     871             :     } else {
     872             :         UNUSED_PARAMETER(doubleValue);
     873           0 :         throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     874             :     }
     875          12 : }
     876             : 
     877             : bool
     878       58376 : MSDevice_Taxi::compatibleLine(const std::string& taxiLine, const std::string& rideLine) {
     879       62100 :     return ((taxiLine == rideLine && StringUtils::startsWith(rideLine, "taxi") && StringUtils::startsWith(taxiLine, "taxi"))
     880      113793 :             || (taxiLine == TAXI_SERVICE && StringUtils::startsWith(rideLine, "taxi:"))
     881      176940 :             || (rideLine == TAXI_SERVICE && StringUtils::startsWith(taxiLine, "taxi:")));
     882             : }
     883             : 
     884             : bool
     885        2533 : MSDevice_Taxi::compatibleLine(const Reservation* res) {
     886        2533 :     return compatibleLine(myHolder.getParameter().line, res->line);
     887             : }
     888             : 
     889             : 
     890             : /****************************************************************************/

Generated by: LCOV version 1.14