LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDispatch.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.7 % 150 145
Test Date: 2026-03-26 16:31:35 Functions: 93.8 % 16 15

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2007-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    MSDispatch.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    16.12.2019
      17              : ///
      18              : // An algorithm that performs dispatch for the taxi device
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <limits>
      23              : #include <microsim/MSNet.h>
      24              : #include <microsim/MSEdge.h>
      25              : #include <microsim/MSGlobals.h>
      26              : #include <microsim/transportables/MSTransportable.h>
      27              : #include "MSRoutingEngine.h"
      28              : #include "MSDispatch.h"
      29              : 
      30              : //#define DEBUG_RESERVATION
      31              : //#define DEBUG_DETOUR
      32              : //#define DEBUG_COND2(obj) (obj->getID() == "p0")
      33              : #define DEBUG_COND2(obj) (true)
      34              : 
      35              : 
      36              : // ===========================================================================
      37              : // Reservation methods
      38              : // ===========================================================================
      39              : 
      40              : // ===========================================================================
      41              : // MSDispatch methods
      42              : // ===========================================================================
      43              : 
      44          812 : MSDispatch::MSDispatch(const Parameterised::Map& params) :
      45              :     Parameterised(params),
      46          812 :     myOutput(nullptr),
      47          812 :     myReservationCount(0),
      48         1624 :     myRoutingMode(StringUtils::toInt(getParameter("routingMode", "1"))) {
      49          812 :     const std::string opt = "device.taxi.dispatch-algorithm.output";
      50          812 :     if (OptionsCont::getOptions().isSet(opt)) {
      51          336 :         OutputDevice::createDeviceByOption(opt, "DispatchInfo");
      52          168 :         myOutput = &OutputDevice::getDeviceByOption(opt);
      53              :     }
      54         1624 :     myKeepUnreachableResTime = string2time(OptionsCont::getOptions().getString("device.taxi.dispatch-keep-unreachable"));
      55          812 : }
      56              : 
      57          812 : MSDispatch::~MSDispatch() {
      58         1187 :     for (auto item : myGroupReservations) {
      59          750 :         for (Reservation* res : item.second) {
      60          375 :             delete res;
      61              :         }
      62              :     }
      63              :     myGroupReservations.clear();
      64          812 : }
      65              : 
      66              : 
      67              : Reservation*
      68         2841 : MSDispatch::addReservation(MSTransportable* person,
      69              :                            SUMOTime reservationTime,
      70              :                            SUMOTime pickupTime,
      71              :                            SUMOTime earliestPickupTime,
      72              :                            const MSEdge* from, double fromPos,
      73              :                            const MSStoppingPlace* fromStop,
      74              :                            const MSEdge* to, double toPos,
      75              :                            const MSStoppingPlace* toStop,
      76              :                            std::string group,
      77              :                            const std::string& line,
      78              :                            int maxCapacity,
      79              :                            int maxContainerCapacity) {
      80              :     // no new reservation nedded if the person can be added to an existing group
      81         2841 :     if (group == "") {
      82              :         // the default empty group implies, no grouping is wanted (and
      83              :         // transportable ids are unique)
      84         2577 :         group = person->getID();
      85              :     } else {
      86              :         auto it2 = myRunningReservations.find(group);
      87          264 :         if (it2 != myRunningReservations.end()) {
      88           62 :             for (auto item : it2->second) {
      89              :                 Reservation* res = const_cast<Reservation*>(item.first);
      90              :                 if (res->persons.count(person) == 0
      91           60 :                         && res->from == from
      92           60 :                         && res->to == to
      93           60 :                         && res->fromPos == fromPos
      94           60 :                         && res->toPos == toPos) {
      95              :                     MSDevice_Taxi* taxi = item.second;
      96              :                     if (taxi->getState() == taxi->PICKUP
      97           58 :                             && remainingCapacity(taxi, res) > 0
      98          118 :                             && taxi->compatibleLine(taxi->getHolder().getParameter().line, line)) {
      99              :                         //std::cout << SIMTIME << " addPerson=" << person->getID() << " extendRes=" << toString(res->persons) << " taxi=" << taxi->getHolder().getID() << " state=" << taxi->getState() << "\n";
     100              :                         res->persons.insert(person);
     101           58 :                         taxi->addCustomer(person, res);
     102              :                         return res;
     103              :                     }
     104              :                 }
     105              :             }
     106              :         }
     107              :     }
     108              :     Reservation* result = nullptr;
     109              :     bool added = false;
     110              :     auto it = myGroupReservations.find(group);
     111         2783 :     if (it != myGroupReservations.end()) {
     112              :         // try to add to existing reservation
     113          140 :         for (Reservation* res : it->second) {
     114              :             if (res->persons.count(person) == 0
     115          128 :                     && res->from == from
     116          128 :                     && res->to == to
     117          128 :                     && res->fromPos == fromPos
     118          128 :                     && res->toPos == toPos) {
     119          128 :                 if (res->persons.size() > 0 && (*res->persons.begin())->isPerson() != person->isPerson()) {
     120            0 :                     WRITE_WARNINGF(TL("Mixing reservations of persons and containers with the same group is not supported for % and %"),
     121              :                                    (*res->persons.begin())->getID(), person->getID());
     122              :                 }
     123          250 :                 if ((person->isPerson() && (int)res->persons.size() >= maxCapacity) ||
     124          140 :                         (!person->isPerson() && (int)res->persons.size() >= maxContainerCapacity)) {
     125              :                     // split group to ensure that at least one taxi is capable of delivering group size.
     126           12 :                     continue;
     127              :                 }
     128              :                 res->persons.insert(person);
     129              :                 result = res;
     130              :                 added = true;
     131          116 :                 break;
     132              :             }
     133              :         }
     134              :     }
     135         2783 :     if (!added) {
     136         2667 :         Reservation* newRes = new Reservation(toString(myReservationCount++), {person}, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, line);
     137         2667 :         myGroupReservations[group].push_back(newRes);
     138              :         result = newRes;
     139              :     }
     140         2783 :     myHasServableReservations = true;
     141              : #ifdef DEBUG_RESERVATION
     142              :     if (DEBUG_COND2(person)) std::cout << SIMTIME
     143              :                                            << " addReservation p=" << person->getID()
     144              :                                            << " rT=" << time2string(reservationTime)
     145              :                                            << " pT=" << time2string(pickupTime)
     146              :                                            << " from=" << from->getID() << " fromPos=" << fromPos
     147              :                                            << " to=" << to->getID() << " toPos=" << toPos
     148              :                                            << " group=" << group
     149              :                                            << " added=" << added
     150              :                                            << "\n";
     151              : #endif
     152         2783 :     return result;
     153              : }
     154              : 
     155              : 
     156              : std::string
     157           87 : MSDispatch::removeReservation(MSTransportable* person,
     158              :                               const MSEdge* from, double fromPos,
     159              :                               const MSEdge* to, double toPos,
     160              :                               std::string group) {
     161           87 :     if (group == "") {
     162              :         // the default empty group implies, no grouping is wanted (and
     163              :         // transportable ids are unique)
     164           77 :         group = person->getID();
     165              :     }
     166           87 :     std::string removedID = "";
     167              :     auto it = myGroupReservations.find(group);
     168           87 :     if (it != myGroupReservations.end()) {
     169           77 :         for (auto itRes = it->second.begin(); itRes != it->second.end(); itRes++) {
     170           77 :             Reservation* res = *itRes;
     171              :             if (res->persons.count(person) != 0
     172           77 :                     && res->from == from
     173           77 :                     && res->to == to
     174           77 :                     && res->fromPos == fromPos
     175           77 :                     && res->toPos == toPos) {
     176              :                 res->persons.erase(person);
     177           77 :                 if (res->persons.empty()) {
     178           77 :                     removedID = res->id;
     179           77 :                     it->second.erase(itRes);
     180              :                     // cleans up MSDispatch_Greedy
     181           77 :                     fulfilledReservation(res);
     182           77 :                     if (it->second.empty()) {
     183              :                         myGroupReservations.erase(it);
     184              :                     }
     185              :                 }
     186              :                 break;
     187              :             }
     188              :         }
     189              :     } else {
     190              :         auto it2 = myRunningReservations.find(group);
     191           10 :         if (it2 != myRunningReservations.end()) {
     192           10 :             for (auto item : it2->second) {
     193              :                 const Reservation* const res = item.first;
     194              :                 if (res->persons.count(person) != 0
     195           10 :                         && res->from == from
     196           10 :                         && res->to == to
     197           10 :                         && res->fromPos == fromPos
     198           10 :                         && res->toPos == toPos) {
     199           10 :                     if (res->persons.size() == 1) {
     200           10 :                         removedID = res->id;
     201              :                     }
     202           10 :                     item.second->cancelCustomer(person);  // will delete res via fulfilledReservation if necessary
     203              :                     break;
     204              :                 }
     205              :             }
     206              :         }
     207              :     }
     208           87 :     myHasServableReservations = myGroupReservations.size() > 0;
     209              : #ifdef DEBUG_RESERVATION
     210              :     if (DEBUG_COND2(person)) std::cout << SIMTIME
     211              :                                            << " removeReservation p=" << person->getID()
     212              :                                            << " from=" << from->getID() << " fromPos=" << fromPos
     213              :                                            << " to=" << to->getID() << " toPos=" << toPos
     214              :                                            << " group=" << group
     215              :                                            << " removedID=" << removedID
     216              :                                            << " hasServable=" << myHasServableReservations
     217              :                                            << "\n";
     218              : #endif
     219           87 :     return removedID;
     220              : }
     221              : 
     222              : 
     223              : Reservation*
     224          150 : MSDispatch::updateReservationFromPos(MSTransportable* person,
     225              :                                      const MSEdge* from, double fromPos,
     226              :                                      const MSEdge* to, double toPos,
     227              :                                      std::string group, double newFromPos) {
     228          150 :     if (group == "") {
     229              :         // the default empty group implies, no grouping is wanted (and
     230              :         // transportable ids are unique)
     231              :         group = person->getID();
     232              :     }
     233              :     Reservation* result = nullptr;
     234          150 :     std::string updatedID = "";
     235              :     auto it = myGroupReservations.find(group);
     236          150 :     if (it != myGroupReservations.end()) {
     237           66 :         for (auto itRes = it->second.begin(); itRes != it->second.end(); itRes++) {
     238           66 :             Reservation* res = *itRes;
     239              :             // TODO: if there is already a reservation with the newFromPos, add to this reservation
     240              :             // TODO: if there are other persons in this reservation, create a new reservation for the updated one
     241              :             if (res->persons.count(person) != 0
     242           66 :                     && res->from == from
     243           66 :                     && res->to == to
     244           66 :                     && res->fromPos == fromPos
     245           66 :                     && res->toPos == toPos) {
     246              :                 // update fromPos
     247           66 :                 res->fromPos = newFromPos;
     248              :                 result = res;
     249           66 :                 updatedID = res->id;
     250              :                 break;
     251              :             }
     252              :         }
     253              :     }
     254              : #ifdef DEBUG_RESERVATION
     255              :     if (DEBUG_COND2(person)) std::cout << SIMTIME
     256              :                                            << " updateReservationFromPos p=" << person->getID()
     257              :                                            << " from=" << from->getID() << " fromPos=" << fromPos
     258              :                                            << " to=" << to->getID() << " toPos=" << toPos
     259              :                                            << " group=" << group
     260              :                                            << " newFromPos=" << newFromPos
     261              :                                            << " updatedID=" << updatedID
     262              :                                            << "\n";
     263              : #endif
     264          150 :     return result;
     265              : }
     266              : 
     267              : 
     268              : std::vector<Reservation*>
     269        19526 : MSDispatch::getReservations() {
     270              :     std::vector<Reservation*> reservations;
     271        38876 :     for (const auto& it : myGroupReservations) {
     272        19350 :         reservations.insert(reservations.end(), it.second.begin(), it.second.end());
     273              :     }
     274        19526 :     return reservations;
     275            0 : }
     276              : 
     277              : 
     278              : std::vector<const Reservation*>
     279         4214 : MSDispatch::getRunningReservations() {
     280              :     std::vector<const Reservation*> result;
     281         9551 :     for (auto item : myRunningReservations) {
     282        10674 :         for (auto item2 : item.second) {
     283         5337 :             result.push_back(item2.first);
     284              :         }
     285              :     }
     286         4214 :     return result;
     287            0 : }
     288              : 
     289              : 
     290              : void
     291         2344 : MSDispatch::servedReservation(const Reservation* res, MSDevice_Taxi* taxi) {
     292         2344 :     auto itR = myRunningReservations.find(res->group);
     293         2344 :     if (itR != myRunningReservations.end() && itR->second.count(res) != 0) {
     294              :         return; // was redispatch
     295              :     }
     296         2220 :     auto it = myGroupReservations.find(res->group);
     297         2220 :     if (it == myGroupReservations.end()) {
     298            0 :         throw ProcessError(TL("Inconsistent group reservations."));
     299              :     }
     300         2220 :     auto it2 = std::find(it->second.begin(), it->second.end(), res);
     301         2220 :     if (it2 == it->second.end()) {
     302            0 :         throw ProcessError(TL("Inconsistent group reservations (2)."));
     303              :     }
     304         2220 :     myRunningReservations[res->group][res] = taxi;
     305         2220 :     const_cast<Reservation*>(*it2)->state = Reservation::ASSIGNED;
     306         2220 :     it->second.erase(it2);
     307         2220 :     if (it->second.empty()) {
     308              :         myGroupReservations.erase(it);
     309              :     }
     310              : }
     311              : 
     312              : 
     313              : void
     314            4 : MSDispatch::swappedRunning(const Reservation* res, MSDevice_Taxi* taxi) {
     315            4 :     myRunningReservations[res->group][res] = taxi;
     316            4 : }
     317              : 
     318              : 
     319              : void
     320         2268 : MSDispatch::fulfilledReservation(const Reservation* res) {
     321         2268 :     myRunningReservations[res->group].erase(res);
     322         2268 :     if (myRunningReservations[res->group].empty()) {
     323              :         myRunningReservations.erase(res->group);
     324              :     }
     325         2268 :     delete res;
     326         2268 : }
     327              : 
     328              : 
     329              : SUMOAbstractRouter<MSEdge, SUMOVehicle>&
     330        14313 : MSDispatch::getRouter() const {
     331        28626 :     return myRoutingMode == 1 ? MSRoutingEngine::getRouterTT(0, SVC_TAXI) : MSNet::getInstance()->getRouterTT(0);
     332              : }
     333              : 
     334              : 
     335              : SUMOTime
     336         3489 : MSDispatch::computePickupTime(SUMOTime t, const MSDevice_Taxi* taxi, const Reservation& res, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router) {
     337              :     ConstMSEdgeVector edges;
     338         3489 :     double fromPos = taxi->getHolder().getPositionOnLane() - NUMERICAL_EPS;
     339         3489 :     const MSEdge* from = *taxi->getHolder().getRerouteOrigin();
     340         3489 :     const bool originDiffers = from != taxi->getHolder().getEdge();
     341         6945 :     router.compute(from, originDiffers ? 0 : fromPos, res.from, res.fromPos, &taxi->getHolder(), t, edges, true);
     342         3489 :     if (edges.empty()) {
     343              :         return SUMOTime_MAX;
     344              :     } else {
     345         2581 :         if (originDiffers) {
     346              :             assert(from == *(taxi->getHolder().getCurrentRouteEdge() + 1));
     347           33 :             edges.insert(edges.begin(), taxi->getHolder().getEdge());
     348              :         }
     349         2581 :         return TIME2STEPS(router.recomputeCostsPos(edges, &taxi->getHolder(), fromPos, res.fromPos, t));
     350              :     }
     351         3489 : }
     352              : 
     353              : 
     354              : bool
     355         3093 : MSDispatch::isReachable(SUMOTime t, const MSDevice_Taxi* taxi, const Reservation& res, SUMOAbstractRouter<MSEdge, SUMOVehicle>& router) {
     356              :     ConstMSEdgeVector edges;
     357         3093 :     router.compute(res.from, res.fromPos, res.to, res.toPos, &taxi->getHolder(), t, edges, true);
     358         3093 :     return !edges.empty();
     359         3093 : }
     360              : 
     361              : 
     362              : double
     363          440 : MSDispatch::computeDetourTime(SUMOTime t, SUMOTime viaTime, const MSDevice_Taxi* taxi,
     364              :                               const MSEdge* from, double fromPos,
     365              :                               const MSEdge* via, double viaPos,
     366              :                               const MSEdge* to, double toPos,
     367              :                               SUMOAbstractRouter<MSEdge, SUMOVehicle>& router,
     368              :                               double& timeDirect) {
     369              :     ConstMSEdgeVector edges;
     370          440 :     if (timeDirect < 0) {
     371          384 :         router.compute(from, fromPos, to, toPos, &taxi->getHolder(), t, edges, true);
     372          384 :         timeDirect = router.recomputeCostsPos(edges, &taxi->getHolder(), fromPos, toPos, t);
     373              :         edges.clear();
     374              :     }
     375              : 
     376          440 :     router.compute(from, fromPos, via, viaPos, &taxi->getHolder(), t, edges, true);
     377          440 :     const double start = STEPS2TIME(t);
     378          440 :     const double leg1 = router.recomputeCostsPos(edges, &taxi->getHolder(), fromPos, viaPos, t);
     379              : #ifdef DEBUG_DETOUR
     380              :     std::cout << "        leg1=" << toString(edges) << " startPos=" << fromPos << " toPos=" << viaPos << " time=" << leg1 << "\n";
     381              : #endif
     382          440 :     const double wait = MAX2(0.0, STEPS2TIME(viaTime) - (start + leg1));
     383              :     edges.clear();
     384          440 :     const SUMOTime timeContinue = TIME2STEPS(start + leg1 + wait);
     385          440 :     router.compute(via, viaPos, to, toPos, &taxi->getHolder(), timeContinue, edges, true);
     386          440 :     const double leg2 = router.recomputeCostsPos(edges, &taxi->getHolder(), viaPos, toPos, timeContinue);
     387          440 :     const double timeDetour = leg1 + wait + leg2;
     388              : #ifdef DEBUG_DETOUR
     389              :     std::cout << "        leg2=" << toString(edges) << " startPos=" << viaPos << " toPos=" << toPos << " time=" << leg2 << "\n";
     390              :     std::cout << "    t=" << STEPS2TIME(t) << " vt=" << STEPS2TIME(viaTime)
     391              :               << " from=" << from->getID() << " to=" << to->getID() << " via=" << via->getID()
     392              :               << " direct=" << timeDirect << " detour=" << timeDetour << " wait=" << wait << "\n";
     393              : #endif
     394          440 :     return timeDetour;
     395          440 : }
     396              : 
     397              : 
     398              : int
     399         3996 : MSDispatch::remainingCapacity(const MSDevice_Taxi* taxi, const Reservation* res) {
     400              :     assert(res->persons.size() > 0);
     401         3996 :     return ((*res->persons.begin())->isPerson()
     402         3996 :             ? taxi->getHolder().getVehicleType().getPersonCapacity()
     403          192 :             : taxi->getHolder().getVehicleType().getContainerCapacity()) - (int)res->persons.size();
     404              : }
     405              : 
     406              : 
     407              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1