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 % 193 187
Test Date: 2025-11-13 15:38:19 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-2025 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      1327339 : MSDevice_Transportable::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into, const bool isContainer) {
      48      2649782 :     MSDevice_Transportable* device = new MSDevice_Transportable(v, isContainer ? "container_" + v.getID() : "person_" + v.getID(), isContainer);
      49         4896 :     into.push_back(device);
      50         4896 :     return device;
      51              : }
      52              : 
      53              : 
      54              : // ---------------------------------------------------------------------------
      55              : // MSDevice_Transportable-methods
      56              : // ---------------------------------------------------------------------------
      57      1327339 : MSDevice_Transportable::MSDevice_Transportable(SUMOVehicle& holder, const std::string& id, const bool isContainer) :
      58              :     MSVehicleDevice(holder, id),
      59      1327339 :     myAmContainer(isContainer),
      60              :     myTransportables(),
      61      1327339 :     myStopped(holder.isStopped()),
      62      1327339 :     myOriginalType(&holder.getVehicleType()),
      63      2654678 :     myLoadedType(nullptr) {
      64      1327339 :     const std::string key = "device." + deviceName() + ".loadedType";
      65      3977121 :     const std::string loadedTypeID = holder.getStringParam(key);
      66      1327339 :     if (loadedTypeID != "") {
      67      1322455 :         myLoadedType = MSNet::getInstance()->getVehicleControl().getVType(loadedTypeID, getEquipmentRNG());
      68      1322455 :         if (myLoadedType == nullptr) {
      69      3967329 :             throw InvalidArgument(TLF("Vehicle type '%' in parameter '%' of vehicle '%' is not known.", loadedTypeID, key, holder.getID()));
      70              :         }
      71              :     }
      72      2649782 : }
      73              : 
      74              : 
      75         9792 : MSDevice_Transportable::~MSDevice_Transportable() {
      76              :     // flush any unfortunate riders still remaining
      77         4906 :     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         9792 : }
      93              : 
      94              : void
      95        23506 : 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        23506 :     notifyMove(const_cast<SUMOTrafficObject&>(veh), -1, travelledDistanceFrontOnLane, veh.getEdge()->getVehicleMaxSpeed(&veh));
     104        23506 : }
     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 :                     it = myTransportables.erase(it);
     129              :                     // proceeding registers t as waiting on edge
     130           12 :                     t->proceed(MSNet::getInstance(), SIMSTEP);
     131           12 :                     MSTransportableControl& tc = (t->isPerson() ?
     132            3 :                                                   MSNet::getInstance()->getPersonControl() :
     133            9 :                                                   MSNet::getInstance()->getContainerControl());
     134           12 :                     tc.abortWaitingForVehicle(t);
     135           12 :                     t->getEdge()->removeTransportable(t);
     136           12 :                     otherVeh->addTransportable(t);
     137           12 :                     stage2->setVehicle(otherVeh);
     138           12 :                     continue;
     139           12 :                 }
     140              :             }
     141              :         }
     142              :         it++;
     143              :     }
     144           12 : }
     145              : 
     146              : 
     147              : bool
     148        77869 : MSDevice_Transportable::willTransferAtJoin(const MSTransportable* t, const MSBaseVehicle* joinVeh) {
     149        77869 :     if (joinVeh && t->getNumRemainingStages() > 1) {
     150            6 :         MSStageDriving* const stage2 = dynamic_cast<MSStageDriving*>(t->getNextStage(1));
     151            6 :         return stage2->isWaitingFor(joinVeh);
     152              :     }
     153              :     return false;
     154              : }
     155              : 
     156              : 
     157              : bool
     158      1347054 : MSDevice_Transportable::notifyMove(SUMOTrafficObject& /*tObject*/, double /*oldPos*/, double newPos, double newSpeed) {
     159      1347054 :     SUMOVehicle& veh = myHolder;
     160      1347054 :     const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     161      1347054 :     if (myStopped) {
     162       709650 :         if (!veh.isStopped()) {
     163         6616 :             const SUMOTime freeFlowTimeCorrection = MSGlobals::gUseMesoSim ? TIME2STEPS(newPos / newSpeed) : 0;
     164        18955 :             for (MSTransportable* const transportable : myTransportables) {
     165        12339 :                 transportable->setDeparted(currentTime - freeFlowTimeCorrection);
     166              :             }
     167         6616 :             myStopped = false;
     168              :         }
     169              :     } else {
     170       637404 :         if (veh.isStopped()) {
     171        73819 :             myStopped = true;
     172        73819 :             MSStop& stop = veh.getNextStopMutable();
     173        73819 :             const MSVehicle* joinVeh = dynamic_cast<MSVehicle*>(MSNet::getInstance()->getVehicleControl().getVehicle(stop.pars.join));
     174        73819 :             const SUMOTime boardingDuration = veh.getVehicleType().getLoadingDuration(!myAmContainer);
     175              :             int numUnloaded = 0;
     176        91026 :             for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
     177        85819 :                 MSTransportable* transportable = *i;
     178        85819 :                 MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(transportable->getCurrentStage());
     179        85819 :                 if (stage->canLeaveVehicle(transportable, myHolder, stop) && !willTransferAtJoin(transportable, joinVeh)) {
     180        77863 :                     SUMOTime& timeForNext = myAmContainer ? stop.timeToLoadNextContainer : stop.timeToBoardNextPerson;
     181        77863 :                     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     182        64709 :                     if (taxiDevice != nullptr && timeForNext == 0 && !MSGlobals::gUseMesoSim) {
     183              :                         // taxi passengers must leave at the end of the stop duration
     184         1046 :                         timeForNext = stop.pars.started + stop.pars.duration;
     185              :                     }
     186        77863 :                     if (timeForNext - DELTA_T > currentTime) {
     187              :                         // try deboarding again in the next step
     188        68612 :                         myStopped = false;
     189        68612 :                         break;
     190              :                     }
     191         9251 :                     if (stage->getDestinationStop() != nullptr) {
     192         4844 :                         stage->getDestinationStop()->addTransportable(transportable);
     193              :                     }
     194              : 
     195              :                     SUMOTime arrivalTime = currentTime;
     196         9251 :                     if (MSGlobals::gUseMesoSim) {
     197              :                         // no boarding / unboarding time in meso
     198         1687 :                         arrivalTime += 1;
     199              :                     } else {
     200         7564 :                         const SUMOTime boardingTime = (SUMOTime)((double)boardingDuration * transportable->getVehicleType().getBoardingFactor());
     201         7564 :                         if (timeForNext > currentTime - DELTA_T) {
     202         6246 :                             timeForNext += boardingTime;
     203              :                         } else {
     204         1318 :                             timeForNext = currentTime + boardingTime;
     205              :                         }
     206              :                     }
     207              :                     //ensure that vehicle stops long enough for deboarding
     208         9251 :                     stop.duration = MAX2(stop.duration, timeForNext - currentTime);
     209              : 
     210         9251 :                     veh.removeTransportableMass(transportable);
     211         9251 :                     i = myTransportables.erase(i); // erase first in case proceed throws an exception
     212         9251 :                     numUnloaded++;
     213         9251 :                     if (taxiDevice != nullptr) {
     214         2077 :                         taxiDevice->customerArrived(transportable);
     215              :                     }
     216         9251 :                     if (!transportable->proceed(MSNet::getInstance(), arrivalTime)) {
     217         4752 :                         if (myAmContainer) {
     218          369 :                             MSNet::getInstance()->getContainerControl().erase(transportable);
     219              :                         } else {
     220         4383 :                             MSNet::getInstance()->getPersonControl().erase(transportable);
     221              :                         }
     222              :                     }
     223         9251 :                     if (MSStopOut::active()) {
     224              :                         SUMOVehicle* vehicle = dynamic_cast<SUMOVehicle*>(&veh);
     225         1887 :                         if (myAmContainer) {
     226          118 :                             MSStopOut::getInstance()->unloadedContainers(vehicle, 1);
     227              :                         } else {
     228         1769 :                             MSStopOut::getInstance()->unloadedPersons(vehicle, 1);
     229              :                         }
     230              :                     }
     231         9251 :                     continue;
     232         9251 :                 }
     233              :                 ++i;
     234              :             }
     235        73819 :             if (numUnloaded != 0) {
     236         5630 :                 changeAttached();
     237              :             }
     238              :         }
     239              :     }
     240      1347054 :     return true;
     241              : }
     242              : 
     243              : 
     244              : bool
     245        76859 : MSDevice_Transportable::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     246        76859 :     if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
     247         2503 :         const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
     248         5446 :         for (MSTransportable* const transportable : myTransportables) {
     249         2943 :             transportable->setDeparted(currentTime);
     250              :         }
     251              :     }
     252        76859 :     if (MSGlobals::gUseMesoSim) {
     253              :         // to trigger vehicle leaving
     254        23973 :         notifyMove(veh, -1., -1., -1.);
     255              :     }
     256        76859 :     return true;
     257              : }
     258              : 
     259              : 
     260              : bool
     261        78061 : MSDevice_Transportable::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
     262              :                                     MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
     263        78061 :     if (reason >= MSMoveReminder::NOTIFICATION_ARRIVED) {
     264         8217 :         for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
     265         4197 :             MSTransportableControl& tc = myAmContainer ? MSNet::getInstance()->getContainerControl() : MSNet::getInstance()->getPersonControl();
     266         4197 :             MSTransportable* transportable = *i;
     267         4197 :             if (transportable->getDestination() != veh.getEdge()) {
     268          182 :                 WRITE_WARNINGF("Teleporting % '%' from vehicle destination edge '%' to intended destination edge '%' time=%",
     269              :                                myAmContainer ? "container" : "person", transportable->getID(), veh.getEdge()->getID(),
     270              :                                transportable->getDestination()->getID(), time2string(SIMSTEP));
     271              :                 tc.registerTeleportWrongDest();
     272              :             }
     273         4197 :             if (!transportable->proceed(MSNet::getInstance(), MSNet::getInstance()->getCurrentTimeStep(), true)) {
     274         3964 :                 tc.erase(transportable);
     275              :             }
     276         4197 :             i = myTransportables.erase(i);
     277              :         }
     278              :     }
     279        78061 :     return true;
     280              : }
     281              : 
     282              : 
     283              : void
     284        13673 : MSDevice_Transportable::addTransportable(MSTransportable* transportable) {
     285        13673 :     if (myTransportables.empty()) {
     286         5558 :         myOriginalType = &myHolder.getVehicleType();
     287              :     }
     288        13673 :     myTransportables.push_back(transportable);
     289        13673 :     if (MSStopOut::active()) {
     290         2889 :         if (myAmContainer) {
     291          187 :             MSStopOut::getInstance()->loadedContainers(&myHolder, 1);
     292              :         } else {
     293         2702 :             MSStopOut::getInstance()->loadedPersons(&myHolder, 1);
     294              :         }
     295              :     }
     296        13673 :     MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     297              :     if (taxiDevice != nullptr) {
     298         2088 :         taxiDevice->customerEntered(transportable);
     299              :     }
     300        13673 :     changeAttached();
     301        13673 : }
     302              : 
     303              : 
     304              : void
     305          203 : MSDevice_Transportable::removeTransportable(MSTransportable* transportable) {
     306          203 :     auto it = std::find(myTransportables.begin(), myTransportables.end(), transportable);
     307          203 :     if (it != myTransportables.end()) {
     308          203 :         myTransportables.erase(it);
     309          203 :         if (MSStopOut::active() && myHolder.isStopped()) {
     310           55 :             if (myAmContainer) {
     311           20 :                 MSStopOut::getInstance()->unloadedContainers(&myHolder, 1);
     312              :             } else {
     313           35 :                 MSStopOut::getInstance()->unloadedPersons(&myHolder, 1);
     314              :             }
     315              :         }
     316          203 :         MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
     317              :         if (taxiDevice != nullptr) {
     318           11 :             taxiDevice->customerArrived(transportable);
     319              :         }
     320          203 :         changeAttached();
     321              :     }
     322          203 : }
     323              : 
     324              : 
     325              : void
     326        19506 : MSDevice_Transportable::changeAttached() {
     327        19506 :     if (myLoadedType != nullptr) {
     328          168 :         int perAttached = myAmContainer ? myLoadedType->getContainerCapacity() : myLoadedType->getPersonCapacity();
     329          168 :         if (perAttached > 0) {
     330          168 :             MSBaseVehicle& veh = dynamic_cast<MSBaseVehicle&>(myHolder);
     331          168 :             SUMOVehicleClass oldVC = myHolder.getVClass();
     332          168 :             const double numAttached = ceil(myTransportables.size() / perAttached);
     333          168 :             if (numAttached > 0.) {
     334          148 :                 MSVehicleType* stype = &veh.getSingularType();
     335          148 :                 stype->setVClass(myLoadedType->getVehicleClass());
     336          148 :                 stype->setGUIShape(myLoadedType->getGuiShape());
     337          148 :                 stype->setLength(myOriginalType->getLength() + numAttached * myLoadedType->getLength());
     338          148 :                 stype->setMass(myOriginalType->getMass() + numAttached * myLoadedType->getMass());
     339              :                 SUMOVTypeParameter& sparam = const_cast<SUMOVTypeParameter&>(stype->getParameter());
     340          148 :                 sparam.carriageLength = myLoadedType->getParameter().carriageLength;
     341          148 :                 sparam.locomotiveLength = myLoadedType->getParameter().locomotiveLength;
     342          148 :                 sparam.carriageGap = myLoadedType->getParameter().carriageGap;
     343              :             } else {
     344           20 :                 myHolder.replaceVehicleType(myOriginalType);
     345              :             }
     346          168 :             if (oldVC != myHolder.getVClass()) {
     347           24 :                 veh.reroute(SIMSTEP, "device." + deviceName() + ".loadedType", veh.getRouterTT());
     348              :             }
     349              :         }
     350              :     }
     351        19506 : }
     352              : 
     353              : 
     354              : void
     355            9 : MSDevice_Transportable::saveState(OutputDevice& out) const {
     356            9 :     out.openTag(SUMO_TAG_DEVICE);
     357            9 :     out.writeAttr(SUMO_ATTR_ID, getID());
     358              :     std::vector<std::string> internals;
     359            9 :     internals.push_back(toString(myStopped));
     360            9 :     out.writeAttr(SUMO_ATTR_STATE, toString(internals));
     361            9 :     out.closeTag();
     362            9 : }
     363              : 
     364              : 
     365              : void
     366            0 : MSDevice_Transportable::loadState(const SUMOSAXAttributes& attrs) {
     367            0 :     std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
     368            0 :     bis >> myStopped;
     369            0 : }
     370              : 
     371              : 
     372              : std::string
     373            5 : MSDevice_Transportable::getParameter(const std::string& key) const {
     374            5 :     if (key == "IDList") {
     375              :         std::vector<std::string> ids;
     376           10 :         for (const MSTransportable* t : myTransportables) {
     377            5 :             ids.push_back(t->getID());
     378              :         }
     379           10 :         return toString(ids);
     380            5 :     }
     381            0 :     throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
     382              : }
     383              : 
     384              : 
     385              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1