/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2026 German Aerospace Center (DLR) and others.3// This program and the accompanying materials are made available under the4// terms of the Eclipse Public License 2.0 which is available at5// https://www.eclipse.org/legal/epl-2.0/6// This Source Code may also be made available under the following Secondary7// Licenses when the conditions for such availability set forth in the Eclipse8// Public License 2.0 are satisfied: GNU General Public License, version 29// or later which is available at10// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html11// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later12/****************************************************************************/13/// @file MESegment.h14/// @author Daniel Krajzewicz15/// @date Tue, May 200516///17// A single mesoscopic segment (cell)18/****************************************************************************/19#pragma once20#include <config.h>2122#include <vector>23#include <cassert>24#include <utils/common/SUMOVehicleClass.h>25#include <utils/common/Named.h>26#include <utils/common/SUMOTime.h>27#include <microsim/MSMoveReminder.h>282930// ===========================================================================31// class declarations32// ===========================================================================33class SUMOVehicle;34class MSEdge;35class MSLink;36class MSDetectorFileOutput;37class MSVehicleControl;38class MEVehicle;39class OutputDevice;404142// ===========================================================================43// class definitions44// ===========================================================================45/**46* @class MESegment47* @brief A single mesoscopic segment (cell)48*/49class MESegment : public Named {50public:51static const double DO_NOT_PATCH_JAM_THRESHOLD;52static const int PARKING_QUEUE = -1;53/// @brief special param value54static const std::string OVERRIDE_TLS_PENALTIES;5556/// @brief edge type specific meso parameters57struct MesoEdgeType {58SUMOTime tauff;59SUMOTime taufj;60SUMOTime taujf;61SUMOTime taujj;62double jamThreshold;63bool junctionControl;64double tlsPenalty;65double tlsFlowPenalty;66SUMOTime minorPenalty;67bool overtaking;68double edgeLength;69};707172private:73class Queue {74public:75Queue(const SVCPermissions permissions) : myPermissions(permissions) {}76inline int size() const {77return (int)myVehicles.size();78}79inline const std::vector<MEVehicle*>& getVehicles() const {80return myVehicles;81}82MEVehicle* remove(MEVehicle* v);83inline std::vector<MEVehicle*>& getModifiableVehicles() {84return myVehicles;85}86inline double getOccupancy() const {87return myOccupancy;88}89inline void setOccupancy(const double occ) {90myOccupancy = occ;91}92inline bool allows(SUMOVehicleClass vclass) const {93return (myPermissions & vclass) == vclass;94}9596/// @brief return the next time at which a vehicle may enter this queue97inline SUMOTime getEntryBlockTime() const {98return myEntryBlockTime;99}100101/// @brief set the next time at which a vehicle may enter this queue102inline void setEntryBlockTime(SUMOTime entryBlockTime) {103myEntryBlockTime = entryBlockTime;104}105106inline SUMOTime getBlockTime() const {107return myBlockTime;108}109inline void setBlockTime(SUMOTime t) {110myBlockTime = t;111}112113inline void setPermissions(SVCPermissions p) {114myPermissions = p;115}116117void addDetector(MSMoveReminder* data);118119void addReminders(MEVehicle* veh) const;120121private:122/// The vClass permissions for this queue123SVCPermissions myPermissions;124125std::vector<MEVehicle*> myVehicles;126127/// @brief The occupied space (in m) in the queue128double myOccupancy = 0.;129130/// @brief The block time for vehicles who wish to enter this queue131SUMOTime myEntryBlockTime = SUMOTime_MIN;132133/// @brief The block time134SUMOTime myBlockTime = -1;135136/// @brief The data collection for all kinds of detectors137std::vector<MSMoveReminder*> myDetectorData;138139};140141public:142/** @brief constructor143* @param[in] id The id of this segment (currently: "<EDGEID>:<SEGMENTNO>")144* @param[in] parent The edge this segment is located within145* @param[in] next The following segment (belonging to the same edge)146* @param[in] length The segment's length147* @param[in] speed The speed allowed on this segment148* @param[in] idx The running index of this segment within the segment's edge149* @param[in] multiQueue whether to install multiple queues on this segment150* @param[in] edgeType edge type specific meso parameters such as the different taus151*/152MESegment(const std::string& id,153const MSEdge& parent, MESegment* next,154const double length, const double speed,155const int idx,156const bool multiQueue,157const MesoEdgeType& edgeType);158159/// @brief set model parameters (may be updated from additional file after network loading is complete)160void initSegment(const MesoEdgeType& edgeType, const MSEdge& parent, const double capacity);161162/// @name Measure collection163/// @{164165/** @brief Adds a data collector for a detector to this segment166*167* @param[in] data The data collector to add168* @param[in] queueIndex The queue (aka lane) to use, -1 means all169*/170void addDetector(MSMoveReminder* data, int queueIndex = -1);171172/** @brief Removes a data collector for a detector from this segment173*174* @param[in] data The data collector to remove175* @note: currently not used176*/177// void removeDetector(MSMoveReminder* data);178179/** @brief Updates data of a detector for one or all vehicle queues180*181* @param[in] data The detector data to update182* @param[in] queueIndex The queue (aka lane) to use, -1 means all183*/184void prepareDetectorForWriting(MSMoveReminder& data, int queueIndex = -1);185/// @}186187/** @brief Returns whether the given vehicle would still fit into the segment188*189* @param[in] veh The vehicle to check space for190* @param[in] entryTime The time at which the vehicle wants to enter191* @param[out] qIdx The index of the queue the vehicle should choose192* @param[in] init whether the check is done at insertion time193* @return the earliest time a vehicle may be added to this segment194*/195SUMOTime hasSpaceFor(const MEVehicle* const veh, const SUMOTime entryTime, int& qIdx, const bool init = false) const;196197/** @brief Inserts (emits) vehicle into the segment198*199* @param[in] veh The vehicle to emit200* @param[in] time The emission time201* @return Whether the emission was successful202*/203bool initialise(MEVehicle* veh, SUMOTime time);204205/** @brief Returns the total number of cars on the segment206*207* @return the total number of cars on the segment208*/209inline int getCarNumber() const {210return myNumVehicles;211}212213/// @brief return the number of queues214inline int numQueues() const {215return (int)myQueues.size();216}217/** @brief Returns the cars in the queue with the given index for visualization218* @return the Queue (XXX not thread-safe!)219*/220inline const std::vector<MEVehicle*>& getQueue(int index) const {221assert(index < (int)myQueues.size());222return myQueues[index].getVehicles();223}224225/** @brief Returns the running index of the segment in the edge (0 is the most upstream).226*227* @return the running index of the segment in the edge228*/229inline int getIndex() const {230return myIndex;231}232233/** @brief Returns the following segment on the same edge (0 if it is the last).234*235* @return the following segment on the same edge (0 if it is the last)236*/237inline MESegment* getNextSegment() const {238return myNextSegment;239}240241/** @brief Returns the length of the segment in meters.242*243* @return the length of the segment244*/245inline double getLength() const {246return myLength;247}248249/** @brief Returns the sum of the lengths of all usable lanes of the segment in meters.250*251* @return the capacity of the segment252*/253inline double getCapacity() const {254return myCapacity;255}256257/** @brief Returns the occupany of the segment (the sum of the vehicle lengths + minGaps)258*259* @return the occupany of the segment in meters260*/261inline double getBruttoOccupancy() const {262double occ = 0.;263for (const Queue& q : myQueues) {264occ += q.getOccupancy();265}266return occ;267}268269/** @brief Returns the relative occupany of the segment (percentage of road used))270* @return the occupany of the segment in percent271*/272inline double getRelativeOccupancy() const {273return getBruttoOccupancy() / myCapacity;274}275276/** @brief Returns the relative occupany of the segment (percentage of road used))277* at which the segment is considered jammed278* @return the jam threshold of the segment in percent279*/280inline double getRelativeJamThreshold() const {281return myJamThreshold / myCapacity;282}283284/** @brief Returns the average speed of vehicles on the segment in meters per second.285* If there is no vehicle on the segment it returns the maximum allowed speed286* @param[in] useCache whether to use a cached value if available287* @note this value is cached in myMeanSpeed. Since caching only takes place288* once every simstep there is a potential for side-influences (i.e. GUI calls to289* this method, ...) For that reason the simulation logic doesn't use the cache.290* This shouldn't matter much for speed since it is only used during291* initializsation of vehicles onto the segment.292* @return the average speed on the segment293*/294double getMeanSpeed(bool useCache) const;295296/// @brief reset myLastMeanSpeedUpdate297void resetCachedSpeeds();298299/// @brief wrapper to satisfy the FunctionBinding signature300inline double getMeanSpeed() const {301return getMeanSpeed(true);302}303304305void writeVehicles(OutputDevice& of) const;306307/** @brief Removes the given car from the edge's que308*309* @param[in] v The vehicle to remove310* @param[in] leaveTime The time at which the vehicle is leaving the que311* @param[in] reason The reason for removing to send to reminders312* @return The next first vehicle to add to the net's que313*/314MEVehicle* removeCar(MEVehicle* v, SUMOTime leaveTime, const MSMoveReminder::Notification reason);315316/** @brief Returns the link the given car will use when passing the next junction317*318* This returns non-zero values only for the last segment and only319* if junction control is enabled.320*321* @param[in] veh The vehicle in question322* @param[in] tlsPenalty Whether the link should be returned for computing tlsPenalty323* @return The link to use or 0 without junction control324*/325MSLink* getLink(const MEVehicle* veh, bool tlsPenalty = false) const;326327/** @brief Returns whether the vehicle may use the next link328*329* In case of disabled junction control it returns always true.330*331* @param[in] veh The vehicle in question332* @return Whether it may pass to the next segment333*/334bool isOpen(const MEVehicle* veh) const;335336/** @brief Removes the vehicle from the segment, adapting its parameters337*338* @param[in] veh The vehicle in question339* @param[in] next The subsequent segment for delay calculation340* @param[in] time the leave time341* @todo Isn't always time == veh->getEventTime?342*/343void send(MEVehicle* veh, MESegment* const next, const int nextQIdx, SUMOTime time, const MSMoveReminder::Notification reason);344345/** @brief Adds the vehicle to the segment, adapting its parameters346*347* @param[in] veh The vehicle in question348* @param[in] time the leave time349* @param[in] isDepart whether the vehicle just departed350* @todo Isn't always time == veh->getEventTime?351*/352void receive(MEVehicle* veh, const int qIdx, SUMOTime time, const bool isDepart = false, const bool isTeleport = false, const bool newEdge = false);353354355/** @brief tries to remove any car from this segment356*357* @param[in] currentTime the current time358* @return Whether vaporization was successful359* @note: cars removed via this method do NOT count as arrivals */360bool vaporizeAnyCar(SUMOTime currentTime, const MSDetectorFileOutput* filter);361362/** @brief Returns the edge this segment belongs to363* @return the edge this segment belongs to364*/365inline const MSEdge& getEdge() const {366return myEdge;367}368369370/** @brief reset mySpeed and patch the speed of371* all vehicles in it. Also set/recompute myJamThreshold372* @param[in] jamThresh follows the semantic of option meso-jam-threshold373*/374void setSpeed(double newSpeed, SUMOTime currentTime, double jamThresh = DO_NOT_PATCH_JAM_THRESHOLD, int qIdx = -1);375376/** @brief Returns the (planned) time at which the next vehicle leaves this segment377* @return The time the vehicle thinks it leaves378*/379SUMOTime getEventTime() const;380381/// @brief Like getEventTime but returns seconds (for visualization)382inline double getEventTimeSeconds() const {383return STEPS2TIME(getEventTime());384}385386/// @brief get the last headway time in seconds387inline double getLastHeadwaySeconds() const {388return STEPS2TIME(myLastHeadway);389}390391/// @brief get the earliest entry time in seconds392inline double getEntryBlockTimeSeconds() const {393SUMOTime t = SUMOTime_MAX;394for (const Queue& q : myQueues) {395t = MIN2(t, q.getEntryBlockTime());396}397return STEPS2TIME(t);398}399400/// @brief Get the waiting time for vehicles in all queues401double getWaitingSeconds() const;402403/// @name State saving/loading404/// @{405406/** @brief Saves the state of this segment into the given stream407*408* Some internal values which must be restored are saved as well as ids of409* the vehicles stored in internal queues and the last departures of connected410* edges.411*412* @param[in, filled] out The (possibly binary) device to write the state into413* @todo What about throwing an IOError?414*/415void saveState(OutputDevice& out) const;416417/** @brief Remove all vehicles before quick-loading state */418void clearState();419420/** @brief Loads the state of this segment with the given parameters421*422* This method is called for every internal que the segment has.423* Every vehicle is retrieved from the given MSVehicleControl and added to this424* segment. Then, the internal queues that store vehicles dependant to their next425* edge are filled the same way. Then, the departure of last vehicles onto the next426* edge are restored.427*428* @param[in] vehs The vehicles for the current que429* @param[in] blockTime The time the last vehicle left the que430* @param[in] queIdx The index of the current que431* @todo What about throwing an IOError?432* @todo What about throwing an error if something else fails (a vehicle can not be referenced)?433*/434void loadState(const std::vector<SUMOVehicle*>& vehs, const SUMOTime blockTime, const SUMOTime entryBlockTime, const int queIdx);435/// @}436437438/** @brief returns all vehicles (for debugging)439*/440std::vector<const MEVehicle*> getVehicles() const;441442/** @brief returns flow based on headway443* @note: returns magic number 10000 when headway cannot be computed444*/445double getFlow() const;446447/// @brief whether the given segment is 0 or encodes vaporization448static inline bool isInvalid(const MESegment* segment) {449return segment == nullptr || segment == &myVaporizationTarget;450}451452/// @brief return a time after earliestEntry at which a vehicle may be inserted at full speed453SUMOTime getNextInsertionTime(SUMOTime earliestEntry) const;454455/// @brief return the remaining physical space on this segment456inline int remainingVehicleCapacity(const double vehLength) const {457int cap = 0;458for (const Queue& q : myQueues) {459if (q.getOccupancy() == 0. && myQueueCapacity < vehLength) {460// even small segments can hold at least one vehicle461cap += 1;462} else {463cap += (int)((myQueueCapacity - q.getOccupancy()) / vehLength);464}465}466return cap;467}468469/// @brief return the minimum headway-time with which vehicles may enter or leave this segment470inline SUMOTime getMinimumHeadwayTime() const {471return myTau_ff;472}473474/// @brief add this lanes MoveReminders to the given vehicle475void addReminders(MEVehicle* veh) const;476477/** @brief Returns the penalty time for passing a link (if using gMesoTLSPenalty > 0 or gMesoMinorPenalty > 0)478* @param[in] veh The vehicle in question479* @return The time penalty480*/481SUMOTime getLinkPenalty(const MEVehicle* veh) const;482483/// @brief called when permissions change due to Rerouter or TraCI484void updatePermissions();485486/// @brief whether the traffic light should use normal junction control despite penalty options487void overrideTLSPenalty() {488myTLSPenalty = false;489}490491private:492bool overtake();493494void setSpeedForQueue(double newSpeed, SUMOTime currentTime,495SUMOTime blockTime, const std::vector<MEVehicle*>& vehs);496497/** @brief compute the new arrival time when switching speed498*/499SUMOTime newArrival(const MEVehicle* const v, double newSpeed, SUMOTime currentTime);500501/// @brief whether a leader in any queue is blocked502bool hasBlockedLeader() const;503504/** @brief compute a value for myJamThreshold505* if jamThresh is negative, compute a value which allows free flow at mySpeed506* interpret jamThresh as the relative occupation at which jam starts507*/508void recomputeJamThreshold(double jamThresh);509510/// @brief compute jam threshold for the given speed and jam-threshold option511double jamThresholdForSpeed(double speed, double jamThresh) const;512513/// @brief whether the given link may be passed because the option meso-junction-control.limited is set514bool limitedControlOverride(const MSLink* link) const;515516/// @brief convert net time gap (leader back to follower front) to gross time gap (leader front to follower front)517inline SUMOTime tauWithVehLength(SUMOTime tau, double lengthWithGap, double vehicleTau) const {518return (SUMOTime)((double)tau * vehicleTau + lengthWithGap * myTau_length);519}520521SUMOTime getTauJJ(double nextQueueSize, double nextQueueCapacity, double nextJamThreshold) const;522523private:524/// @brief The microsim edge this segment belongs to525const MSEdge& myEdge;526527/// @brief The next segment of this edge, 0 if this is the last segment of this edge528MESegment* myNextSegment;529530/// @brief The segment's length531const double myLength;532533/// @brief Running number of the segment in the edge534const int myIndex;535536/// @name Model constants that may be reset once via additional file537/// @{538539/// @brief The time headway parameters, see the Eissfeldt thesis540SUMOTime myTau_ff, myTau_fj, myTau_jf, myTau_jj;541542/// @brief Whether tls penalty is enabled543bool myTLSPenalty;544545/// @brief penalty for minor links546bool myCheckMinorPenalty; // for legacy compatibility (#7802, 7804)547SUMOTime myMinorPenalty;548549/// @brief Whether junction control is enabled550bool myJunctionControl;551552/// @brief Whether overtaking is permitted on this segment553bool myOvertaking;554/// @}555556/// @brief Headway parameter for computing gross time headyway from net time headway, length and edge speed557double myTau_length;558559/// @brief The number of lanes represented by the queue * the length of the lane560double myCapacity = 0.;561562/// @brief The number of lanes represented by the queue * the length of the lane563double myQueueCapacity = 0.;564565/// @brief The space (in m) which needs to be occupied before the segment is considered jammed566double myJamThreshold;567568/// @brief The car queues. Vehicles are inserted in the front and removed in the back569std::vector<Queue> myQueues;570571/// @brief The cached value for the number of vehicles572int myNumVehicles;573574/// @brief The follower edge to allowed que index mapping for multi queue segments575std::map<const MSEdge*, int> myFollowerMap;576577/// @brief the last headway578SUMOTime myLastHeadway;579580/* @brief segment for signifying vaporization. This segment has invalid581* data and should only be used as a unique pointer */582static MSEdge myDummyParent;583static MESegment myVaporizationTarget;584585/// @brief the mean speed on this segment. Updated at event time or on demand586mutable double myMeanSpeed;587588/// @brief the time at which myMeanSpeed was last updated589mutable SUMOTime myLastMeanSpeedUpdate;590591private:592/// @brief Invalidated copy constructor.593MESegment(const MESegment&);594595/// @brief Invalidated assignment operator.596MESegment& operator=(const MESegment&);597598/// @brief constructor for dummy segment599MESegment(const std::string& id);600};601602603