/****************************************************************************/1// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo2// Copyright (C) 2001-2025 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;53static const std::string OVERRIDE_TLS_PENALTIES;5455/// @brief edge type specific meso parameters56struct MesoEdgeType {57SUMOTime tauff;58SUMOTime taufj;59SUMOTime taujf;60SUMOTime taujj;61double jamThreshold;62bool junctionControl;63double tlsPenalty;64double tlsFlowPenalty;65SUMOTime minorPenalty;66bool overtaking;67};686970private:71class Queue {72public:73Queue(const SVCPermissions permissions) : myPermissions(permissions) {}74inline int size() const {75return (int)myVehicles.size();76}77inline const std::vector<MEVehicle*>& getVehicles() const {78return myVehicles;79}80MEVehicle* remove(MEVehicle* v);81inline std::vector<MEVehicle*>& getModifiableVehicles() {82return myVehicles;83}84inline double getOccupancy() const {85return myOccupancy;86}87inline void setOccupancy(const double occ) {88myOccupancy = occ;89}90inline bool allows(SUMOVehicleClass vclass) const {91return (myPermissions & vclass) == vclass;92}9394/// @brief return the next time at which a vehicle may enter this queue95inline SUMOTime getEntryBlockTime() const {96return myEntryBlockTime;97}9899/// @brief set the next time at which a vehicle may enter this queue100inline void setEntryBlockTime(SUMOTime entryBlockTime) {101myEntryBlockTime = entryBlockTime;102}103104inline SUMOTime getBlockTime() const {105return myBlockTime;106}107inline void setBlockTime(SUMOTime t) {108myBlockTime = t;109}110111inline void setPermissions(SVCPermissions p) {112myPermissions = p;113}114115void addDetector(MSMoveReminder* data);116117void addReminders(MEVehicle* veh) const;118119private:120/// The vClass permissions for this queue121SVCPermissions myPermissions;122123std::vector<MEVehicle*> myVehicles;124125/// @brief The occupied space (in m) in the queue126double myOccupancy = 0.;127128/// @brief The block time for vehicles who wish to enter this queue129SUMOTime myEntryBlockTime = SUMOTime_MIN;130131/// @brief The block time132SUMOTime myBlockTime = -1;133134/// @brief The data collection for all kinds of detectors135std::vector<MSMoveReminder*> myDetectorData;136137};138139public:140/** @brief constructor141* @param[in] id The id of this segment (currently: "<EDGEID>:<SEGMENTNO>")142* @param[in] parent The edge this segment is located within143* @param[in] next The following segment (belonging to the same edge)144* @param[in] length The segment's length145* @param[in] speed The speed allowed on this segment146* @param[in] idx The running index of this segment within the segment's edge147* @param[in] multiQueue whether to install multiple queues on this segment148* @param[in] edgeType edge type specific meso parameters such as the different taus149*/150MESegment(const std::string& id,151const MSEdge& parent, MESegment* next,152const double length, const double speed,153const int idx,154const bool multiQueue,155const MesoEdgeType& edgeType);156157/// @brief set model parameters (may be updated from additional file after network loading is complete)158void initSegment(const MesoEdgeType& edgeType, const MSEdge& parent, const double capacity);159160/// @name Measure collection161/// @{162163/** @brief Adds a data collector for a detector to this segment164*165* @param[in] data The data collector to add166* @param[in] queueIndex The queue (aka lane) to use, -1 means all167*/168void addDetector(MSMoveReminder* data, int queueIndex = -1);169170/** @brief Removes a data collector for a detector from this segment171*172* @param[in] data The data collector to remove173* @note: currently not used174*/175// void removeDetector(MSMoveReminder* data);176177/** @brief Updates data of a detector for one or all vehicle queues178*179* @param[in] data The detector data to update180* @param[in] queueIndex The queue (aka lane) to use, -1 means all181*/182void prepareDetectorForWriting(MSMoveReminder& data, int queueIndex = -1);183/// @}184185/** @brief Returns whether the given vehicle would still fit into the segment186*187* @param[in] veh The vehicle to check space for188* @param[in] entryTime The time at which the vehicle wants to enter189* @param[out] qIdx The index of the queue the vehicle should choose190* @param[in] init whether the check is done at insertion time191* @return the earliest time a vehicle may be added to this segment192*/193SUMOTime hasSpaceFor(const MEVehicle* const veh, const SUMOTime entryTime, int& qIdx, const bool init = false) const;194195/** @brief Inserts (emits) vehicle into the segment196*197* @param[in] veh The vehicle to emit198* @param[in] time The emission time199* @return Whether the emission was successful200*/201bool initialise(MEVehicle* veh, SUMOTime time);202203/** @brief Returns the total number of cars on the segment204*205* @return the total number of cars on the segment206*/207inline int getCarNumber() const {208return myNumVehicles;209}210211/// @brief return the number of queues212inline int numQueues() const {213return (int)myQueues.size();214}215/** @brief Returns the cars in the queue with the given index for visualization216* @return the Queue (XXX not thread-safe!)217*/218inline const std::vector<MEVehicle*>& getQueue(int index) const {219assert(index < (int)myQueues.size());220return myQueues[index].getVehicles();221}222223/** @brief Returns the running index of the segment in the edge (0 is the most upstream).224*225* @return the running index of the segment in the edge226*/227inline int getIndex() const {228return myIndex;229}230231/** @brief Returns the following segment on the same edge (0 if it is the last).232*233* @return the following segment on the same edge (0 if it is the last)234*/235inline MESegment* getNextSegment() const {236return myNextSegment;237}238239/** @brief Returns the length of the segment in meters.240*241* @return the length of the segment242*/243inline double getLength() const {244return myLength;245}246247/** @brief Returns the sum of the lengths of all usable lanes of the segment in meters.248*249* @return the capacity of the segment250*/251inline double getCapacity() const {252return myCapacity;253}254255/** @brief Returns the occupany of the segment (the sum of the vehicle lengths + minGaps)256*257* @return the occupany of the segment in meters258*/259inline double getBruttoOccupancy() const {260double occ = 0.;261for (const Queue& q : myQueues) {262occ += q.getOccupancy();263}264return occ;265}266267/** @brief Returns the relative occupany of the segment (percentage of road used))268* @return the occupany of the segment in percent269*/270inline double getRelativeOccupancy() const {271return getBruttoOccupancy() / myCapacity;272}273274/** @brief Returns the relative occupany of the segment (percentage of road used))275* at which the segment is considered jammed276* @return the jam threshold of the segment in percent277*/278inline double getRelativeJamThreshold() const {279return myJamThreshold / myCapacity;280}281282/** @brief Returns the average speed of vehicles on the segment in meters per second.283* If there is no vehicle on the segment it returns the maximum allowed speed284* @param[in] useCache whether to use a cached value if available285* @note this value is cached in myMeanSpeed. Since caching only takes place286* once every simstep there is a potential for side-influences (i.e. GUI calls to287* this method, ...) For that reason the simulation logic doesn't use the cache.288* This shouldn't matter much for speed since it is only used during289* initializsation of vehicles onto the segment.290* @return the average speed on the segment291*/292double getMeanSpeed(bool useCache) const;293294/// @brief reset myLastMeanSpeedUpdate295void resetCachedSpeeds();296297/// @brief wrapper to satisfy the FunctionBinding signature298inline double getMeanSpeed() const {299return getMeanSpeed(true);300}301302303void writeVehicles(OutputDevice& of) const;304305/** @brief Removes the given car from the edge's que306*307* @param[in] v The vehicle to remove308* @param[in] leaveTime The time at which the vehicle is leaving the que309* @param[in] reason The reason for removing to send to reminders310* @return The next first vehicle to add to the net's que311*/312MEVehicle* removeCar(MEVehicle* v, SUMOTime leaveTime, const MSMoveReminder::Notification reason);313314/** @brief Returns the link the given car will use when passing the next junction315*316* This returns non-zero values only for the last segment and only317* if junction control is enabled.318*319* @param[in] veh The vehicle in question320* @param[in] tlsPenalty Whether the link should be returned for computing tlsPenalty321* @return The link to use or 0 without junction control322*/323MSLink* getLink(const MEVehicle* veh, bool tlsPenalty = false) const;324325/** @brief Returns whether the vehicle may use the next link326*327* In case of disabled junction control it returns always true.328*329* @param[in] veh The vehicle in question330* @return Whether it may pass to the next segment331*/332bool isOpen(const MEVehicle* veh) const;333334/** @brief Removes the vehicle from the segment, adapting its parameters335*336* @param[in] veh The vehicle in question337* @param[in] next The subsequent segment for delay calculation338* @param[in] time the leave time339* @todo Isn't always time == veh->getEventTime?340*/341void send(MEVehicle* veh, MESegment* const next, const int nextQIdx, SUMOTime time, const MSMoveReminder::Notification reason);342343/** @brief Adds the vehicle to the segment, adapting its parameters344*345* @param[in] veh The vehicle in question346* @param[in] time the leave time347* @param[in] isDepart whether the vehicle just departed348* @todo Isn't always time == veh->getEventTime?349*/350void receive(MEVehicle* veh, const int qIdx, SUMOTime time, const bool isDepart = false, const bool isTeleport = false, const bool newEdge = false);351352353/** @brief tries to remove any car from this segment354*355* @param[in] currentTime the current time356* @return Whether vaporization was successful357* @note: cars removed via this method do NOT count as arrivals */358bool vaporizeAnyCar(SUMOTime currentTime, const MSDetectorFileOutput* filter);359360/** @brief Returns the edge this segment belongs to361* @return the edge this segment belongs to362*/363inline const MSEdge& getEdge() const {364return myEdge;365}366367368/** @brief reset mySpeed and patch the speed of369* all vehicles in it. Also set/recompute myJamThreshold370* @param[in] jamThresh follows the semantic of option meso-jam-threshold371*/372void setSpeed(double newSpeed, SUMOTime currentTime, double jamThresh = DO_NOT_PATCH_JAM_THRESHOLD, int qIdx = -1);373374/** @brief Returns the (planned) time at which the next vehicle leaves this segment375* @return The time the vehicle thinks it leaves376*/377SUMOTime getEventTime() const;378379/// @brief Like getEventTime but returns seconds (for visualization)380inline double getEventTimeSeconds() const {381return STEPS2TIME(getEventTime());382}383384/// @brief get the last headway time in seconds385inline double getLastHeadwaySeconds() const {386return STEPS2TIME(myLastHeadway);387}388389/// @brief get the earliest entry time in seconds390inline double getEntryBlockTimeSeconds() const {391SUMOTime t = SUMOTime_MAX;392for (const Queue& q : myQueues) {393t = MIN2(t, q.getEntryBlockTime());394}395return STEPS2TIME(t);396}397398/// @brief Get the waiting time for vehicles in all queues399double getWaitingSeconds() const;400401/// @name State saving/loading402/// @{403404/** @brief Saves the state of this segment into the given stream405*406* Some internal values which must be restored are saved as well as ids of407* the vehicles stored in internal queues and the last departures of connected408* edges.409*410* @param[in, filled] out The (possibly binary) device to write the state into411* @todo What about throwing an IOError?412*/413void saveState(OutputDevice& out) const;414415/** @brief Remove all vehicles before quick-loading state */416void clearState();417418/** @brief Loads the state of this segment with the given parameters419*420* This method is called for every internal que the segment has.421* Every vehicle is retrieved from the given MSVehicleControl and added to this422* segment. Then, the internal queues that store vehicles dependant to their next423* edge are filled the same way. Then, the departure of last vehicles onto the next424* edge are restored.425*426* @param[in] vehs The vehicles for the current que427* @param[in] blockTime The time the last vehicle left the que428* @param[in] queIdx The index of the current que429* @todo What about throwing an IOError?430* @todo What about throwing an error if something else fails (a vehicle can not be referenced)?431*/432void loadState(const std::vector<SUMOVehicle*>& vehs, const SUMOTime blockTime, const SUMOTime entryBlockTime, const int queIdx);433/// @}434435436/** @brief returns all vehicles (for debugging)437*/438std::vector<const MEVehicle*> getVehicles() const;439440/** @brief returns flow based on headway441* @note: returns magic number 10000 when headway cannot be computed442*/443double getFlow() const;444445/// @brief whether the given segment is 0 or encodes vaporization446static inline bool isInvalid(const MESegment* segment) {447return segment == nullptr || segment == &myVaporizationTarget;448}449450/// @brief return a time after earliestEntry at which a vehicle may be inserted at full speed451SUMOTime getNextInsertionTime(SUMOTime earliestEntry) const;452453/// @brief return the remaining physical space on this segment454inline int remainingVehicleCapacity(const double vehLength) const {455int cap = 0;456for (const Queue& q : myQueues) {457if (q.getOccupancy() == 0. && myQueueCapacity < vehLength) {458// even small segments can hold at least one vehicle459cap += 1;460} else {461cap += (int)((myQueueCapacity - q.getOccupancy()) / vehLength);462}463}464return cap;465}466467/// @brief return the minimum headway-time with which vehicles may enter or leave this segment468inline SUMOTime getMinimumHeadwayTime() const {469return myTau_ff;470}471472/// @brief add this lanes MoveReminders to the given vehicle473void addReminders(MEVehicle* veh) const;474475/** @brief Returns the penalty time for passing a link (if using gMesoTLSPenalty > 0 or gMesoMinorPenalty > 0)476* @param[in] veh The vehicle in question477* @return The time penalty478*/479SUMOTime getLinkPenalty(const MEVehicle* veh) const;480481/// @brief called when permissions change due to Rerouter or TraCI482void updatePermissions();483484private:485bool overtake();486487void setSpeedForQueue(double newSpeed, SUMOTime currentTime,488SUMOTime blockTime, const std::vector<MEVehicle*>& vehs);489490/** @brief compute the new arrival time when switching speed491*/492SUMOTime newArrival(const MEVehicle* const v, double newSpeed, SUMOTime currentTime);493494/// @brief whether a leader in any queue is blocked495bool hasBlockedLeader() const;496497/** @brief compute a value for myJamThreshold498* if jamThresh is negative, compute a value which allows free flow at mySpeed499* interpret jamThresh as the relative occupation at which jam starts500*/501void recomputeJamThreshold(double jamThresh);502503/// @brief compute jam threshold for the given speed and jam-threshold option504double jamThresholdForSpeed(double speed, double jamThresh) const;505506/// @brief whether the given link may be passed because the option meso-junction-control.limited is set507bool limitedControlOverride(const MSLink* link) const;508509/// @brief convert net time gap (leader back to follower front) to gross time gap (leader front to follower front)510inline SUMOTime tauWithVehLength(SUMOTime tau, double lengthWithGap, double vehicleTau) const {511return (SUMOTime)((double)tau * vehicleTau + lengthWithGap * myTau_length);512}513514SUMOTime getTauJJ(double nextQueueSize, double nextQueueCapacity, double nextJamThreshold) const;515516/// @brief whether the traffic light should use normal junction control despite penalty options517bool tlsPenaltyOverride() const;518519private:520/// @brief The microsim edge this segment belongs to521const MSEdge& myEdge;522523/// @brief The next segment of this edge, 0 if this is the last segment of this edge524MESegment* myNextSegment;525526/// @brief The segment's length527const double myLength;528529/// @brief Running number of the segment in the edge530const int myIndex;531532/// @name Model constants that may be reset once via additional file533/// @{534535/// @brief The time headway parameters, see the Eissfeldt thesis536SUMOTime myTau_ff, myTau_fj, myTau_jf, myTau_jj;537538/// @brief Whether tls penalty is enabled539bool myTLSPenalty;540541/// @brief penalty for minor links542bool myCheckMinorPenalty; // for legacy compatibility (#7802, 7804)543SUMOTime myMinorPenalty;544545/// @brief Whether junction control is enabled546bool myJunctionControl;547548/// @brief Whether overtaking is permitted on this segment549bool myOvertaking;550/// @}551552/// @brief Headway parameter for computing gross time headyway from net time headway, length and edge speed553double myTau_length;554555/// @brief The number of lanes represented by the queue * the length of the lane556double myCapacity = 0.;557558/// @brief The number of lanes represented by the queue * the length of the lane559double myQueueCapacity = 0.;560561/// @brief The space (in m) which needs to be occupied before the segment is considered jammed562double myJamThreshold;563564/// @brief The car queues. Vehicles are inserted in the front and removed in the back565std::vector<Queue> myQueues;566567/// @brief The cached value for the number of vehicles568int myNumVehicles;569570/// @brief The follower edge to allowed que index mapping for multi queue segments571std::map<const MSEdge*, int> myFollowerMap;572573/// @brief the last headway574SUMOTime myLastHeadway;575576/* @brief segment for signifying vaporization. This segment has invalid577* data and should only be used as a unique pointer */578static MSEdge myDummyParent;579static MESegment myVaporizationTarget;580581/// @brief the mean speed on this segment. Updated at event time or on demand582mutable double myMeanSpeed;583584/// @brief the time at which myMeanSpeed was last updated585mutable SUMOTime myLastMeanSpeedUpdate;586587private:588/// @brief Invalidated copy constructor.589MESegment(const MESegment&);590591/// @brief Invalidated assignment operator.592MESegment& operator=(const MESegment&);593594/// @brief constructor for dummy segment595MESegment(const std::string& id);596};597598599