LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Transportable.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 96.9 % 194 188
Test Date: 2026-03-02 16:00:03 Functions: 94.1 % 17 16

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2001-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_Transportable.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @author  Laura Bieker
      19              : /// @author  Melanie Weber
      20              : /// @author  Andreas Kendziorra
      21              : /// @date    Fri, 30.01.2009
      22              : ///
      23              : // A device which is used to keep track of persons and containers riding with a vehicle
      24              : /****************************************************************************/
      25              : #include <config.h>
      26              : 
      27              : #include <utils/xml/SUMOSAXAttributes.h>
      28              : #include <microsim/output/MSStopOut.h>
      29              : #include <microsim/MSNet.h>
      30              : #include <microsim/MSEdge.h>
      31              : #include <microsim/MSStop.h>
      32              : #include <microsim/MSStoppingPlace.h>
      33              : #include <microsim/transportables/MSPerson.h>
      34              : #include <microsim/transportables/MSTransportableControl.h>
      35              : #include <microsim/transportables/MSStageDriving.h>
      36              : #include "MSDevice_Transportable.h"
      37              : #include "MSDevice_Taxi.h"
      38              : 
      39              : 
      40              : // ===========================================================================
      41              : // method definitions
      42              : // ===========================================================================
      43              : // ---------------------------------------------------------------------------
      44              : // static initialisation methods
      45              : // ---------------------------------------------------------------------------
      46              : MSDevice_Transportable*
      47      4317724 : MSDevice_Transportable::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into, const bool isContainer) {
      48      8630431 :     MSDevice_Transportable* device = new MSDevice_Transportable(v, isContainer ? "container_" + v.getID() : "person_" + v.getID(), isContainer);
      49         5017 :     into.push_back(device);
      50         5017 :     return device;
      51              : }
      52              : 
      53              : 
      54              : // ---------------------------------------------------------------------------
      55              : // MSDevice_Transportable-methods
      56              : // ---------------------------------------------------------------------------
      57      4317724 : MSDevice_Transportable::MSDevice_Transportable(SUMOVehicle& holder, const std::string& id, const bool isContainer) :
      58              :     MSVehicleDevice(holder, id),
      59      4317724 :     myAmContainer(isContainer),
      60              :     myTransportables(),
      61      4317724 :     myStopped(holder.isStopped()),
      62      4317724 :     myOriginalType(&holder.getVehicleType()),
      63      8635448 :     myLoadedType(nullptr) {
      64      4317724 :     const std::string key = "device." + deviceName() + ".loadedType";
      65     12948155 :     const std::string loadedTypeID = holder.getStringParam(key);
      66      4317724 :     if (loadedTypeID != "") {
      67      4312719 :         myLoadedType = MSNet::getInstance()->getVehicleControl().getVType(loadedTypeID, getEquipmentRNG());
      68      4312719 :         if (myLoadedType == nullptr) {
      69     12938121 :             throw InvalidArgument(TLF("Vehicle type '%' in parameter '%' of vehicle '%' is not known.", loadedTypeID, key, holder.getID()));
      70              :         }
      71              :     }
      72      8630431 : }
      73              : 
      74              : 
      75        10034 : MSDevice_Transportable::~MSDevice_Transportable() {
      76              :     // flush any unfortunate riders still remaining
      77         5027 :     for (auto it = myTransportables.begin(); it != myTransportables.end();) {
      78           10 :         MSTransportable* transportable = *it;
      79           40 :         WRITE_WARNING((myAmContainer ? "Removing container '" : "Removing person '") + transportable->getID() +
      80              :                       "' at removal of vehicle '" + myHolder.getID() + "'");
      81           10 :         MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(transportable->getCurrentStage());
      82           10 :         if (stage != nullptr) {
      83           10 :             stage->setVehicle(nullptr);
      84              :         }
      85           10 :         if (myAmContainer) {
      86            0 :             MSNet::getInstance()->getContainerControl().erase(transportable);
      87              :         } else {
      88           10 :             MSNet::getInstance()->getPersonControl().erase(transportable);
      89              :         }
      90           10 :         it = myTransportables.erase(it);
      91              :     }
      92        10034 : }
      93              : 
      94              : void
      95        24739 : MSDevice_Transportable::notifyMoveInternal(const SUMOTrafficObject& veh,
      96              :         const double /* frontOnLane */,
      97              :         const double /* timeOnLane */,
      98              :         const double /* meanSpeedFrontOnLane */,
      99              :         const double /* meanSpeedVehicleOnLane */,
     100              :         const double travelledDistanceFrontOnLane,
     101              :         const double /* travelledDistanceVehicleOnLane */,
     102              :         const double /* meanLengthOnLane */) {
     103        24739 :     notifyMove(const_cast<SUMOTrafficObject&>(veh), -1, travelledDistanceFrontOnLane, veh.getEdge()->getVehicleMaxSpeed(&veh));
     104        24739 : }
     105              : 
     106              : bool
     107           44 : MSDevice_Transportable::anyLeavingAtStop(const MSStop& stop) const {
     108           54 :     for (const MSTransportable* t : myTransportables) {
     109           44 :         MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(t->getCurrentStage());
     110           44 :         if (stage->canLeaveVehicle(t, myHolder, stop)) {
     111              :             return true;
     112              :         }
     113              :     }
     114              :     return false;
     115              : }
     116              : 
     117              : 
     118              : void
     119           12 : MSDevice_Transportable::transferAtSplitOrJoin(MSBaseVehicle* otherVeh) {
     120           12 :     const MSStop& stop = myHolder.getNextStop();
     121           30 :     for (auto it = myTransportables.begin(); it != myTransportables.end();) {
     122           18 :         MSTransportable* t = *it;
     123           18 :         if (t->getNumRemainingStages() > 1) {
     124           12 :             MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(t->getCurrentStage());
     125           12 :             if (stage->canLeaveVehicle(t, myHolder, stop)) {
     126           12 :                 MSStageDriving* const stage2 = dynamic_cast<MSStageDriving*>(t->getNextStage(1));
     127           12 :                 if (stage2 && stage2->isWaitingFor(otherVeh)) {
     128           12 :                     auto locker = myHolder.getScopeLock();
     129           12 :                     it = myTransportables.erase(it);
     130              :                     // proceeding registers t as waiting on edge
     131           12 :                     t->proceed(MSNet::getInstance(), SIMSTEP);
     132           12 :                     MSTransportableControl& tc = (t->isPerson() ?
     133            3 :                                                   MSNet::getInstance()->getPersonControl() :
     134            9 :                                                   MSNet::getInstance()->getContainerControl());
     135           12 :                     tc.abortWaitingForVehicle(t);
     136           12 :                     t->getEdge()->removeTransportable(t);
     137           12 :                     otherVeh->addTransportable(t);
     138           12 :                     stage2->setVehicle(otherVeh);
     139              :                     continue;
     140              :                 }
     141              :             }
     142              :         }
     143              :         it++;
     144              :     }
     145           12 : }
     146              : 
     147              : 
     148              : bool
     149        84471 : MSDevice_Transportable::willTransferAtJoin(const MSTransportable* t, const MSBaseVehicle* joinVeh) {
     150        84471 :     if (joinVeh && t->getNumRemainingStages() > 1) {
     151            6 :         MSStageDriving* const stage2 = dynamic_cast<MSStageDriving*>(t->getNextStage(1));
     152            6 :         return stage2->isWaitingFor(joinVeh);
     153              :     }
     154              :     return false;
     155              : }
     156              : 
     157              : 
     158              : bool
     159      1634043 : MSDevice_Transportable::notifyMove(SUMOTrafficObject& /*tObject*/, double /*oldPos*/, double newPos, double newSpeed) {
     160      1634043 :     SUMOVehicle& veh = myHolder;
     161      1634043 :     const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     162      1634043 :     if (myStopped) {
     163       943753 :         if (!veh.isStopped()) {
     164         7285 :             const SUMOTime freeFlowTimeCorrection = MSGlobals::gUseMesoSim ? TIME2STEPS(newPos / newSpeed) : 0;
     165        19826 :             for (MSTransportable* const transportable : myTransportables) {
     166        12541 :                 transportable->setDeparted(currentTime - freeFlowTimeCorrection);
     167              :             }
     168         7285 :             myStopped = false;
     169              :         }
     170              :     } else {
     171       690290 :         if (veh.isStopped()) {
     172        80853 :             myStopped = true;
     173        80853 :             MSStop& stop = veh.getNextStopMutable();
     174        80853 :             const MSVehicle* joinVeh = dynamic_cast<MSVehicle*>(MSNet::getInstance()->getVehicleControl().getVehicle(stop.pars.join));
     175        80853 :             const SUMOTime boardingDuration = veh.getVehicleType().getLoadingDuration(!myAmContainer);
     176              :             int numUnloaded = 0;
     177        98300 :             for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
     178        92450 :                 MSTransportable* transportable = *i;
     179        92450 :                 MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(transportable->getCurrentStage());
     180        92450 :                 if (stage->canLeaveVehicle(transportable, myHolder, stop) && !willTransferAtJoin(transportable, joinVeh)) {
     181        84465 :                     SUMOTime& timeForNext = myAmContainer ? stop.timeToLoadNextContainer : stop.timeToBoardNextPerson;
     182        84465 :                     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     183        71322 :                     if (taxiDevice != nullptr && timeForNext == 0 && !MSGlobals::gUseMesoSim) {
     184              :                         // taxi passengers must leave at the end of the stop duration
     185         1154 :                         timeForNext = stop.pars.started + stop.pars.duration;
     186              :                     }
     187        84465 :                     if (timeForNext - DELTA_T > currentTime) {
     188              :                         // try deboarding again in the next step
     189        75003 :                         myStopped = false;
     190        75003 :                         break;
     191              :                     }
     192         9462 :                     if (stage->getDestinationStop() != nullptr) {
     193         4905 :                         stage->getDestinationStop()->addTransportable(transportable);
     194              :                     }
     195              : 
     196              :                     SUMOTime arrivalTime = currentTime;
     197         9462 :                     if (MSGlobals::gUseMesoSim) {
     198              :                         // no boarding / unboarding time in meso
     199         1766 :                         arrivalTime += 1;
     200              :                     } else {
     201         7696 :                         const SUMOTime boardingTime = (SUMOTime)((double)boardingDuration * transportable->getVehicleType().getBoardingFactor());
     202         7696 :                         if (timeForNext > currentTime - DELTA_T) {
     203         6377 :                             timeForNext += boardingTime;
     204              :                         } else {
     205         1319 :                             timeForNext = currentTime + boardingTime;
     206              :                         }
     207              :                     }
     208              :                     //ensure that vehicle stops long enough for deboarding
     209         9462 :                     stop.duration = MAX2(stop.duration, timeForNext - currentTime);
     210              : 
     211         9462 :                     veh.removeTransportableMass(transportable);
     212         9462 :                     auto locker = myHolder.getScopeLock();
     213         9462 :                     i = myTransportables.erase(i); // erase first in case proceed throws an exception
     214         9462 :                     numUnloaded++;
     215         9462 :                     if (taxiDevice != nullptr) {
     216         2294 :                         taxiDevice->customerArrived(transportable);
     217              :                     }
     218         9462 :                     if (!transportable->proceed(MSNet::getInstance(), arrivalTime)) {
     219         4960 :                         if (myAmContainer) {
     220          369 :                             MSNet::getInstance()->getContainerControl().erase(transportable);
     221              :                         } else {
     222         4591 :                             MSNet::getInstance()->getPersonControl().erase(transportable);
     223              :                         }
     224              :                     }
     225         9462 :                     if (MSStopOut::active()) {
     226              :                         SUMOVehicle* vehicle = dynamic_cast<SUMOVehicle*>(&veh);
     227         1900 :                         if (myAmContainer) {
     228          118 :                             MSStopOut::getInstance()->unloadedContainers(vehicle, 1);
     229              :                         } else {
     230         1782 :                             MSStopOut::getInstance()->unloadedPersons(vehicle, 1);
     231              :                         }
     232              :                     }
     233              :                     continue;
     234              :                 }
     235              :                 ++i;
     236              :             }
     237        80853 :             if (numUnloaded != 0) {
     238         5814 :                 changeAttached();
     239              :             }
     240              :         }
     241              :     }
     242      1634043 :     return true;
     243              : }
     244              : 
     245              : 
     246              : bool
     247        81941 : MSDevice_Transportable::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     248        81941 :     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
     249         2525 :         const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     250         5490 :         for (MSTransportable* const transportable : myTransportables) {
     251         2965 :             transportable->setDeparted(currentTime);
     252              :         }
     253              :     }
     254        81941 :     if (MSGlobals::gUseMesoSim) {
     255              :         // to trigger vehicle leaving
     256        25226 :         notifyMove(veh, -1., -1., -1.);
     257              :     }
     258        81941 :     return true;
     259              : }
     260              : 
     261              : 
     262              : bool
     263        83128 : MSDevice_Transportable::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
     264              :                                     MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     265        83128 :     if (reason >= MSMoveReminder::NOTIFICATION_ARRIVED) {
     266         4050 :         auto locker = myHolder.getScopeLock();
     267         8260 :         for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
     268         4210 :             MSTransportableControl& tc = myAmContainer ? MSNet::getInstance()->getContainerControl() : MSNet::getInstance()->getPersonControl();
     269         4210 :             MSTransportable* transportable = *i;
     270         4210 :             if (transportable->getDestination() != veh.getEdge()) {
     271          182 :                 WRITE_WARNINGF("Teleporting % '%' from vehicle destination edge '%' to intended destination edge '%' time=%",
     272              :                                myAmContainer ? "container" : "person", transportable->getID(), veh.getEdge()->getID(),
     273              :                                transportable->getDestination()->getID(), time2string(SIMSTEP));
     274              :                 tc.registerTeleportWrongDest();
     275              :             }
     276         4210 :             if (!transportable->proceed(MSNet::getInstance(), MSNet::getInstance()->getCurrentTimeStep(), true)) {
     277         3987 :                 tc.erase(transportable);
     278              :             }
     279         4210 :             i = myTransportables.erase(i);
     280              :         }
     281              :     }
     282        83128 :     return true;
     283              : }
     284              : 
     285              : 
     286              : void
     287        13900 : MSDevice_Transportable::addTransportable(MSTransportable* transportable) {
     288        13900 :     auto locker = myHolder.getScopeLock();
     289        13900 :     if (myTransportables.empty()) {
     290         5738 :         myOriginalType = &myHolder.getVehicleType();
     291              :     }
     292        13900 :     myTransportables.push_back(transportable);
     293        13900 :     if (MSStopOut::active()) {
     294         2902 :         if (myAmContainer) {
     295          187 :             MSStopOut::getInstance()->loadedContainers(&myHolder, 1);
     296              :         } else {
     297         2715 :             MSStopOut::getInstance()->loadedPersons(&myHolder, 1);
     298              :         }
     299              :     }
     300        13900 :     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     301              :     if (taxiDevice != nullptr) {
     302         2309 :         taxiDevice->customerEntered(transportable);
     303              :     }
     304        13900 :     changeAttached();
     305        13900 : }
     306              : 
     307              : 
     308              : void
     309          206 : MSDevice_Transportable::removeTransportable(MSTransportable* transportable) {
     310          206 :     auto it = std::find(myTransportables.begin(), myTransportables.end(), transportable);
     311          206 :     if (it != myTransportables.end()) {
     312          206 :         auto locker = myHolder.getScopeLock();
     313          206 :         myTransportables.erase(it);
     314          206 :         if (MSStopOut::active() && myHolder.isStopped()) {
     315           55 :             if (myAmContainer) {
     316           20 :                 MSStopOut::getInstance()->unloadedContainers(&myHolder, 1);
     317              :             } else {
     318           35 :                 MSStopOut::getInstance()->unloadedPersons(&myHolder, 1);
     319              :             }
     320              :         }
     321          206 :         MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     322              :         if (taxiDevice != nullptr) {
     323           15 :             taxiDevice->customerArrived(transportable);
     324              :         }
     325          206 :         changeAttached();
     326              :     }
     327          206 : }
     328              : 
     329              : 
     330              : void
     331        19920 : MSDevice_Transportable::changeAttached() {
     332        19920 :     if (myLoadedType != nullptr) {
     333          168 :         int perAttached = myAmContainer ? myLoadedType->getContainerCapacity() : myLoadedType->getPersonCapacity();
     334          168 :         if (perAttached > 0) {
     335          168 :             MSBaseVehicle& veh = dynamic_cast<MSBaseVehicle&>(myHolder);
     336          168 :             SUMOVehicleClass oldVC = myHolder.getVClass();
     337          168 :             const double numAttached = ceil(myTransportables.size() / perAttached);
     338          168 :             if (numAttached > 0.) {
     339          148 :                 MSVehicleType* stype = &veh.getSingularType();
     340          148 :                 stype->setVClass(myLoadedType->getVehicleClass());
     341          148 :                 stype->setGUIShape(myLoadedType->getGuiShape());
     342          148 :                 stype->setLength(myOriginalType->getLength() + numAttached * myLoadedType->getLength());
     343          148 :                 stype->setMass(myOriginalType->getMass() + numAttached * myLoadedType->getMass());
     344              :                 SUMOVTypeParameter& sparam = const_cast<SUMOVTypeParameter&>(stype->getParameter());
     345          148 :                 sparam.carriageLength = myLoadedType->getParameter().carriageLength;
     346          148 :                 sparam.locomotiveLength = myLoadedType->getParameter().locomotiveLength;
     347          148 :                 sparam.carriageGap = myLoadedType->getParameter().carriageGap;
     348              :             } else {
     349           20 :                 myHolder.replaceVehicleType(myOriginalType);
     350              :             }
     351          168 :             if (oldVC != myHolder.getVClass()) {
     352           24 :                 veh.reroute(SIMSTEP, "device." + deviceName() + ".loadedType", veh.getRouterTT());
     353              :             }
     354              :         }
     355              :     }
     356        19920 : }
     357              : 
     358              : 
     359              : void
     360            9 : MSDevice_Transportable::saveState(OutputDevice& out) const {
     361            9 :     out.openTag(SUMO_TAG_DEVICE);
     362            9 :     out.writeAttr(SUMO_ATTR_ID, getID());
     363              :     std::vector<std::string> internals;
     364            9 :     internals.push_back(toString(myStopped));
     365            9 :     out.writeAttr(SUMO_ATTR_STATE, toString(internals));
     366            9 :     out.closeTag();
     367            9 : }
     368              : 
     369              : 
     370              : void
     371            0 : MSDevice_Transportable::loadState(const SUMOSAXAttributes& attrs) {
     372            0 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
     373            0 :     bis >> myStopped;
     374            0 : }
     375              : 
     376              : 
     377              : std::string
     378            4 : MSDevice_Transportable::getParameter(const std::string& key) const {
     379            4 :     if (key == "IDList") {
     380              :         std::vector<std::string> ids;
     381            8 :         for (const MSTransportable* t : myTransportables) {
     382            4 :             ids.push_back(t->getID());
     383              :         }
     384            8 :         return toString(ids);
     385            4 :     }
     386            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     387              : }
     388              : 
     389              : 
     390              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1