LCOV - code coverage report
Current view: top level - src/microsim/devices - MSDevice_Transportable.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 99.0 % 203 201
Test Date: 2026-04-16 16:39:47 Functions: 100.0 % 17 17

            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      2146217 : MSDevice_Transportable::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into, const bool isContainer) {
      48      4287366 :     MSDevice_Transportable* device = new MSDevice_Transportable(v, isContainer ? "container_" + v.getID() : "person_" + v.getID(), isContainer);
      49         5068 :     into.push_back(device);
      50         5068 :     return device;
      51              : }
      52              : 
      53              : 
      54              : // ---------------------------------------------------------------------------
      55              : // MSDevice_Transportable-methods
      56              : // ---------------------------------------------------------------------------
      57      2146217 : MSDevice_Transportable::MSDevice_Transportable(SUMOVehicle& holder, const std::string& id, const bool isContainer) :
      58              :     MSVehicleDevice(holder, id),
      59      2146217 :     myAmContainer(isContainer),
      60              :     myTransportables(),
      61      2146217 :     myStopped(holder.isStopped()),
      62      2146217 :     myOriginalType(&holder.getVehicleType()),
      63      4292434 :     myLoadedType(nullptr) {
      64      2146217 :     const std::string key = "device." + deviceName() + ".loadedType";
      65      6433583 :     const std::string loadedTypeID = holder.getStringParam(key);
      66      2146217 :     if (loadedTypeID != "") {
      67      2141161 :         myLoadedType = MSNet::getInstance()->getVehicleControl().getVType(loadedTypeID, getEquipmentRNG());
      68      2141161 :         if (myLoadedType == nullptr) {
      69      6423447 :             throw InvalidArgument(TLF("Vehicle type '%' in parameter '%' of vehicle '%' is not known.", loadedTypeID, key, holder.getID()));
      70              :         }
      71              :     }
      72      4287366 : }
      73              : 
      74              : 
      75        10136 : MSDevice_Transportable::~MSDevice_Transportable() {
      76              :     // flush any unfortunate riders still remaining
      77         5078 :     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        10136 : }
      93              : 
      94              : void
      95        27976 : 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        27976 :     notifyMove(const_cast<SUMOTrafficObject&>(veh), -1, travelledDistanceFrontOnLane, veh.getEdge()->getVehicleMaxSpeed(&veh));
     104        27976 : }
     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        93563 : MSDevice_Transportable::willTransferAtJoin(const MSTransportable* t, const MSBaseVehicle* joinVeh) {
     150        93563 :     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      1675226 : MSDevice_Transportable::notifyMove(SUMOTrafficObject& /*tObject*/, double /*oldPos*/, double newPos, double newSpeed) {
     160      1675226 :     SUMOVehicle& veh = myHolder;
     161      1675226 :     const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     162      1675226 :     if (myStopped) {
     163       951441 :         if (!veh.isStopped()) {
     164         7710 :             const SUMOTime freeFlowTimeCorrection = MSGlobals::gUseMesoSim ? TIME2STEPS(newPos / newSpeed) : 0;
     165        20463 :             for (MSTransportable* const transportable : myTransportables) {
     166        12753 :                 transportable->setDeparted(currentTime - freeFlowTimeCorrection);
     167              :             }
     168         7710 :             myStopped = false;
     169              :         }
     170              :     } else {
     171       723785 :         if (veh.isStopped()) {
     172        90147 :             myStopped = true;
     173        90147 :             MSStop& stop = veh.getNextStopMutable();
     174        90147 :             const MSVehicle* joinVeh = dynamic_cast<MSVehicle*>(MSNet::getInstance()->getVehicleControl().getVehicle(stop.pars.join));
     175        90147 :             const SUMOTime boardingDuration = veh.getVehicleType().getLoadingDuration(!myAmContainer);
     176              :             int numUnloaded = 0;
     177       107924 :             for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
     178       101636 :                 MSTransportable* transportable = *i;
     179       101636 :                 MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(transportable->getCurrentStage());
     180       101636 :                 if (stage->canLeaveVehicle(transportable, myHolder, stop) && !willTransferAtJoin(transportable, joinVeh)) {
     181        93557 :                     SUMOTime& timeForNext = myAmContainer ? stop.timeToLoadNextContainer : stop.timeToBoardNextPerson;
     182        93557 :                     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     183        80461 :                     if (taxiDevice != nullptr && timeForNext == 0 && !MSGlobals::gUseMesoSim) {
     184              :                         // taxi passengers must leave at the end of the stop duration
     185         1303 :                         timeForNext = stop.pars.started + stop.pars.duration;
     186              :                     }
     187        93557 :                     if (timeForNext - DELTA_T > currentTime) {
     188              :                         // try deboarding again in the next step
     189        83859 :                         myStopped = false;
     190        83859 :                         break;
     191              :                     }
     192         9698 :                     if (stage->getDestinationStop() != nullptr) {
     193         4865 :                         stage->getDestinationStop()->addTransportable(transportable);
     194              :                     }
     195              : 
     196              :                     SUMOTime arrivalTime = currentTime;
     197         9698 :                     if (MSGlobals::gUseMesoSim) {
     198              :                         // no boarding / unboarding time in meso
     199         1822 :                         arrivalTime += 1;
     200              :                     } else {
     201         7876 :                         const SUMOTime boardingTime = (SUMOTime)((double)boardingDuration * transportable->getVehicleType().getBoardingFactor());
     202         7876 :                         if (timeForNext > currentTime - DELTA_T) {
     203         6558 :                             timeForNext += boardingTime;
     204              :                         } else {
     205         1318 :                             timeForNext = currentTime + boardingTime;
     206              :                         }
     207              :                     }
     208              :                     //ensure that vehicle stops long enough for deboarding
     209         9698 :                     stop.duration = MAX2(stop.duration, timeForNext - currentTime);
     210              : 
     211         9698 :                     veh.removeTransportableMass(transportable);
     212         9698 :                     auto locker = myHolder.getScopeLock();
     213         9698 :                     i = myTransportables.erase(i); // erase first in case proceed throws an exception
     214         9698 :                     numUnloaded++;
     215         9698 :                     if (taxiDevice != nullptr) {
     216         2582 :                         taxiDevice->customerArrived(transportable);
     217              :                     }
     218         9698 :                     if (!transportable->proceed(MSNet::getInstance(), arrivalTime)) {
     219         5247 :                         if (myAmContainer) {
     220          369 :                             MSNet::getInstance()->getContainerControl().erase(transportable);
     221              :                         } else {
     222         4878 :                             MSNet::getInstance()->getPersonControl().erase(transportable);
     223              :                         }
     224              :                     }
     225         9698 :                     if (MSStopOut::active()) {
     226              :                         SUMOVehicle* vehicle = dynamic_cast<SUMOVehicle*>(&veh);
     227         1906 :                         if (myAmContainer) {
     228          118 :                             MSStopOut::getInstance()->unloadedContainers(vehicle, 1);
     229              :                         } else {
     230         1788 :                             MSStopOut::getInstance()->unloadedPersons(vehicle, 1);
     231              :                         }
     232              :                     }
     233              :                     continue;
     234              :                 }
     235              :                 ++i;
     236              :             }
     237        90147 :             if (numUnloaded != 0) {
     238         6088 :                 changeAttached();
     239              :             }
     240              :         }
     241              :     }
     242      1675226 :     return true;
     243              : }
     244              : 
     245              : 
     246              : bool
     247        87603 : MSDevice_Transportable::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     248        87603 :     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
     249         2503 :         const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     250         5437 :         for (MSTransportable* const transportable : myTransportables) {
     251         2934 :             transportable->setDeparted(currentTime);
     252              :         }
     253              :     }
     254        87603 :     if (MSGlobals::gUseMesoSim) {
     255              :         // to trigger vehicle leaving
     256        28676 :         notifyMove(veh, -1., -1., -1.);
     257              :     }
     258        87603 :     return true;
     259              : }
     260              : 
     261              : 
     262              : bool
     263        88624 : MSDevice_Transportable::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
     264              :                                     MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     265        88624 :     if (reason >= MSMoveReminder::NOTIFICATION_ARRIVED) {
     266         4023 :         auto locker = myHolder.getScopeLock();
     267         8213 :         for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
     268         4190 :             MSTransportableControl& tc = myAmContainer ? MSNet::getInstance()->getContainerControl() : MSNet::getInstance()->getPersonControl();
     269         4190 :             MSTransportable* transportable = *i;
     270         4190 :             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         4190 :             if (!transportable->proceed(MSNet::getInstance(), MSNet::getInstance()->getCurrentTimeStep(), true)) {
     277         3968 :                 tc.erase(transportable);
     278              :             }
     279         4190 :             i = myTransportables.erase(i);
     280              :         }
     281              :     }
     282        88624 :     return true;
     283              : }
     284              : 
     285              : 
     286              : void
     287        14116 : MSDevice_Transportable::addTransportable(MSTransportable* transportable) {
     288        14116 :     auto locker = myHolder.getScopeLock();
     289        14116 :     if (myTransportables.empty()) {
     290         5973 :         myOriginalType = &myHolder.getVehicleType();
     291              :     }
     292        14116 :     myTransportables.push_back(transportable);
     293        14116 :     if (MSStopOut::active()) {
     294         2908 :         if (myAmContainer) {
     295          187 :             MSStopOut::getInstance()->loadedContainers(&myHolder, 1);
     296              :         } else {
     297         2721 :             MSStopOut::getInstance()->loadedPersons(&myHolder, 1);
     298              :         }
     299              :     }
     300        14116 :     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     301              :     if (taxiDevice != nullptr) {
     302         2597 :         taxiDevice->customerEntered(transportable);
     303              :     }
     304        14116 :     changeAttached();
     305        14116 : }
     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        20410 : MSDevice_Transportable::changeAttached() {
     332        20410 :     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        20410 : }
     357              : 
     358              : 
     359              : void
     360           17 : MSDevice_Transportable::saveState(OutputDevice& out) const {
     361           17 :     out.openTag(SUMO_TAG_DEVICE);
     362           17 :     out.writeAttr(SUMO_ATTR_ID, getID());
     363              :     SUMOTime nextPerson = 0;
     364              :     SUMOTime nextContainer = 0;
     365           17 :     if (myHolder.isStopped()) {
     366            4 :         nextPerson = myHolder.getNextStop().timeToBoardNextPerson;
     367            4 :         nextContainer = myHolder.getNextStop().timeToLoadNextContainer;
     368              :     }
     369           17 :     std::ostringstream internals;
     370           17 :     internals << myStopped
     371              :         << " " << nextPerson
     372              :         << " " << nextContainer;
     373           17 :     out.writeAttr(SUMO_ATTR_STATE, internals.str());
     374           17 :     out.closeTag();
     375           17 : }
     376              : 
     377              : 
     378              : void
     379           17 : MSDevice_Transportable::loadState(const SUMOSAXAttributes& attrs) {
     380              :     SUMOTime nextPerson;
     381              :     SUMOTime nextContainer;
     382           17 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
     383           17 :     bis >> myStopped;
     384              :     bis >> nextPerson;
     385              :     bis >> nextContainer;
     386           17 :     if (nextPerson != 0 || nextContainer !=0) {
     387              :         assert(myHolder.hasStops());
     388            3 :         MSStop& stop = myHolder.getNextStopMutable();
     389            3 :         stop.timeToBoardNextPerson = nextPerson;
     390            3 :         stop.timeToLoadNextContainer = nextContainer;
     391            3 :         stop.duration = MAX2(stop.duration, MAX2(nextPerson, nextContainer) - SIMSTEP);
     392              :     }
     393           17 : }
     394              : 
     395              : 
     396              : std::string
     397            5 : MSDevice_Transportable::getParameter(const std::string& key) const {
     398            5 :     if (key == "IDList") {
     399              :         std::vector<std::string> ids;
     400           10 :         for (const MSTransportable* t : myTransportables) {
     401            5 :             ids.push_back(t->getID());
     402              :         }
     403           10 :         return toString(ids);
     404            5 :     }
     405            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     406              : }
     407              : 
     408              : 
     409              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1