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 MELoop.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @date Tue, May 2005
17 : ///
18 : // The main mesocopic simulation loop
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <queue>
23 : #include <vector>
24 : #include <map>
25 : #include <cmath>
26 :
27 : #include <microsim/MSNet.h>
28 : #include <microsim/MSEdge.h>
29 : #include <microsim/MSGlobals.h>
30 : #include <microsim/MSLane.h>
31 : #include <microsim/MSVehicle.h>
32 : #include <microsim/MSVehicleControl.h>
33 : #include <utils/options/OptionsCont.h>
34 : #include <utils/common/ToString.h>
35 : #include <utils/common/FileHelpers.h>
36 : #include <utils/common/SUMOTime.h>
37 : #include <utils/common/RandHelper.h>
38 : #include "MELoop.h"
39 : #include "MESegment.h"
40 : #include "MEVehicle.h"
41 :
42 :
43 : // ===========================================================================
44 : // method definitions
45 : // ===========================================================================
46 6451 : MELoop::MELoop(const SUMOTime recheckInterval) : myFullRecheckInterval(recheckInterval), myLinkRecheckInterval(TIME2STEPS(1)) {
47 6451 : }
48 :
49 6357 : MELoop::~MELoop() {
50 329454 : for (std::vector<MESegment*>::const_iterator j = myEdges2FirstSegments.begin(); j != myEdges2FirstSegments.end(); ++j) {
51 1003332 : for (MESegment* s = *j; s != nullptr;) {
52 : MESegment* n = s->getNextSegment();
53 680235 : delete s;
54 : s = n;
55 : }
56 : }
57 6357 : }
58 :
59 :
60 : void
61 15347565 : MELoop::simulate(SUMOTime tMax) {
62 51337365 : while (!myLeaderCars.empty()) {
63 47721014 : const SUMOTime time = myLeaderCars.begin()->first;
64 47721014 : std::vector<MEVehicle*> vehs = myLeaderCars[time];
65 : assert(time > tMax - DELTA_T || vehs.size() == 0);
66 47721014 : if (time > tMax) {
67 : return;
68 : }
69 : myLeaderCars.erase(time);
70 73310577 : for (std::vector<MEVehicle*>::const_iterator i = vehs.begin(); i != vehs.end(); ++i) {
71 37320777 : checkCar(*i);
72 : assert(myLeaderCars.empty() || myLeaderCars.begin()->first >= time);
73 : }
74 47721014 : }
75 : }
76 :
77 :
78 : SUMOTime
79 37336171 : MELoop::changeSegment(MEVehicle* veh, SUMOTime leaveTime, MESegment* const toSegment, MSMoveReminder::Notification reason, const bool ignoreLink) const {
80 37336171 : int qIdx = 0;
81 : MESegment* const onSegment = veh->getSegment();
82 : if (MESegment::isInvalid(toSegment)) {
83 687019 : if (veh->isStoppedTriggered()) {
84 2160 : return leaveTime + MAX2(SUMOTime(1), myLinkRecheckInterval);
85 : }
86 684859 : if (onSegment != nullptr) {
87 681847 : onSegment->send(veh, toSegment, qIdx, leaveTime, reason);
88 : } else {
89 9036 : WRITE_WARNINGF(TL("Vehicle '%' teleports beyond arrival edge '%', time=%."),
90 : veh->getID(), veh->getEdge()->getID(), time2string(leaveTime));
91 : }
92 684859 : veh->setSegment(toSegment); // signal arrival
93 684859 : MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
94 684859 : return leaveTime;
95 36719099 : } else if (!MSGlobals::gCheckRoutes && !ignoreLink && !MESegment::isInvalid(onSegment) && &onSegment->getEdge() != &toSegment->getEdge() &&
96 30411 : veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr) {
97 27072 : if (veh->isStopped()) {
98 2 : veh->processStop();
99 : }
100 : return SUMOTime_MAX;
101 : }
102 36622080 : const SUMOTime entry = toSegment->hasSpaceFor(veh, leaveTime, qIdx);
103 36622080 : if (entry == leaveTime && (ignoreLink || veh->mayProceed())) {
104 25323814 : if (onSegment != nullptr) {
105 25323264 : if (veh->getQueIndex() == MESegment::PARKING_QUEUE) { // parking or just aborted parking
106 5645 : if (veh->isParking()) {
107 5643 : veh->processStop();
108 : }
109 5645 : veh->getEdge()->getLanes()[0]->removeParking(veh); // TODO for GUI only
110 : } else {
111 45895544 : onSegment->send(veh, toSegment, qIdx, leaveTime, onSegment->getNextSegment() == nullptr ? MSMoveReminder::NOTIFICATION_JUNCTION : MSMoveReminder::NOTIFICATION_SEGMENT);
112 : }
113 25323264 : toSegment->receive(veh, qIdx, leaveTime, false, ignoreLink, &onSegment->getEdge() != &toSegment->getEdge());
114 : } else {
115 1650 : WRITE_WARNINGF(TL("Vehicle '%' ends teleporting on edge '%':%, time=%."),
116 : veh->getID(), toSegment->getEdge().getID(), toSegment->getIndex(), time2string(leaveTime));
117 : // this is not quite correct but suffices for interrogation by
118 : // subsequent methods (veh->getSpeed() needs segment != 0)
119 550 : veh->setSegment(myEdges2FirstSegments[veh->getEdge()->getNumericalID()]);
120 : // clean up detectors (do not add traffic data)
121 : // note: updateDatector is not called if leaveTime == getLastEntryTime()
122 550 : veh->updateDetectors(veh->getLastEntryTime(), veh->getEventTime(), true, MSMoveReminder::NOTIFICATION_TELEPORT);
123 550 : toSegment->receive(veh, qIdx, leaveTime, false, true, true);
124 : }
125 25323814 : return entry;
126 : }
127 11298266 : if (entry == leaveTime && !ignoreLink) { // this is a long way of saying !veh->mayProceed() (which is a costly call)
128 9059042 : return entry + MAX2(SUMOTime(1), myLinkRecheckInterval);
129 : }
130 : return entry;
131 : }
132 :
133 :
134 : void
135 37320777 : MELoop::checkCar(MEVehicle* veh) {
136 : const SUMOTime leaveTime = veh->getEventTime();
137 : MESegment* const onSegment = veh->getSegment();
138 37320777 : MESegment* const toSegment = veh->getQueIndex() == MESegment::PARKING_QUEUE ? onSegment : nextSegment(onSegment, veh);
139 37320777 : const bool teleporting = (onSegment == nullptr); // is the vehicle currently teleporting?
140 : // @note reason is only evaluated if toSegment == nullptr
141 37320777 : const SUMOTime nextEntry = changeSegment(veh, leaveTime, toSegment, MSMoveReminder::NOTIFICATION_ARRIVED, teleporting);
142 37320777 : if (nextEntry == leaveTime) {
143 : return;
144 : }
145 11323752 : const bool r1 = MSGlobals::gTimeToGridlock > 0 && veh->getWaitingTime() > MSGlobals::gTimeToGridlock;
146 11323752 : const bool r3 = MSGlobals::gTimeToTeleportDisconnected >= 0 && veh->getWaitingTime() > MSGlobals::gTimeToTeleportDisconnected;
147 11323752 : if (!veh->isStopped() && (r1 || r3)) {
148 7004 : const bool disconnected = (MSGlobals::gTimeToTeleportDisconnected >= 0
149 1818 : && veh->succEdge(1) != nullptr
150 8822 : && veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr);
151 7004 : if ((r1 && !disconnected) || (r3 && disconnected)) {
152 5202 : teleportVehicle(veh, toSegment, disconnected);
153 5202 : return;
154 : }
155 : }
156 11318550 : if (veh->getBlockTime() == SUMOTime_MAX && (!veh->isStopped()
157 8421581 : || (!veh->isStoppedTriggered() && veh->isStoppedParking()))) {
158 : veh->setBlockTime(leaveTime);
159 : }
160 11318550 : if (nextEntry == SUMOTime_MAX) {
161 : // all usable queues on the next segment are full
162 2020025 : SUMOTime newEventTime = MAX3(toSegment->getEventTime() + 1, leaveTime + 1, leaveTime + myFullRecheckInterval);
163 2020025 : if (MSGlobals::gTimeToGridlock > 0) {
164 : // if teleporting is enabled, make sure we look at the vehicle when the gridlock-time is up
165 1533730 : const SUMOTime recheck = MSGlobals::gTimeToTeleportDisconnected >= 0 ? MIN2(MSGlobals::gTimeToGridlock, MSGlobals::gTimeToTeleportDisconnected) : MSGlobals::gTimeToGridlock;
166 1533730 : newEventTime = MAX2(MIN2(newEventTime, veh->getBlockTime() + recheck + 1), leaveTime + DELTA_T);
167 : }
168 : veh->setEventTime(newEventTime);
169 : } else {
170 : // receiving segment has recently received another vehicle or the junction is blocked
171 : veh->setEventTime(nextEntry);
172 : }
173 11318550 : addLeaderCar(veh, teleporting ? nullptr : onSegment->getLink(veh));
174 : }
175 :
176 :
177 : void
178 5202 : MELoop::teleportVehicle(MEVehicle* veh, MESegment* const toSegment, bool disconnected) {
179 : const SUMOTime leaveTime = veh->getEventTime();
180 : MESegment* const onSegment = veh->getSegment();
181 5202 : if (MSGlobals::gRemoveGridlocked) {
182 48 : WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
183 : veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(),
184 : time2string(leaveTime));
185 16 : MSNet::getInstance()->getVehicleControl().registerTeleportJam();
186 : int qIdx = 0;
187 16 : onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED);
188 16 : veh->setSegment(nullptr);
189 16 : MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
190 16 : return;
191 : }
192 : const bool teleporting = (onSegment == nullptr); // is the vehicle already teleporting?
193 : // try to find a place on the current edge
194 5186 : MESegment* teleSegment = disconnected ? toSegment : toSegment->getNextSegment();
195 8932 : while (teleSegment != nullptr && changeSegment(veh, leaveTime, teleSegment, MSMoveReminder::NOTIFICATION_TELEPORT, true) != leaveTime) {
196 : // @caution the time to get to the next segment here is ignored XXX
197 : teleSegment = teleSegment->getNextSegment();
198 : }
199 5186 : if (teleSegment != nullptr) {
200 408 : if (!teleporting) {
201 : // we managed to teleport in a single jump
202 798 : const std::string reason = disconnected ? " (disconnected)" : "";
203 1212 : WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long%, from edge '%':% to edge '%':%, time=%."),
204 : veh->getID(), reason, onSegment->getEdge().getID(), onSegment->getIndex(),
205 : teleSegment->getEdge().getID(), teleSegment->getIndex(), time2string(leaveTime));
206 404 : MSNet::getInstance()->getVehicleControl().registerTeleportJam();
207 : }
208 : } else {
209 : // teleport across the current edge and try insertion later
210 4778 : if (!teleporting) {
211 : int qIdx = 0;
212 : // announce start of multi-step teleport, arrival will be announced in changeSegment()
213 10782 : WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
214 : veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(), time2string(leaveTime));
215 3594 : MSNet::getInstance()->getVehicleControl().registerTeleportJam();
216 : // remove from current segment
217 3594 : onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT);
218 : // mark veh as teleporting
219 3594 : veh->setSegment(nullptr);
220 : } else {
221 1184 : veh->updateDetectors(veh->getLastEntryTime(), leaveTime, true, MSMoveReminder::NOTIFICATION_TELEPORT);
222 : }
223 : // @caution microsim uses current travel time teleport duration
224 14334 : const SUMOTime teleArrival = leaveTime + TIME2STEPS(veh->getEdge()->getLength() / MAX2(veh->getEdge()->getSpeedLimit(), NUMERICAL_EPS));
225 4778 : const bool atDest = veh->moveRoutePointer();
226 4778 : if (atDest) {
227 : // teleporting to end of route
228 3012 : changeSegment(veh, teleArrival, nullptr, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED, true);
229 : } else {
230 : veh->setEventTime(teleArrival);
231 1766 : addLeaderCar(veh, nullptr);
232 : // teleporting vehicles must react to rerouters
233 1766 : getSegmentForEdge(*veh->getEdge())->addReminders(veh);
234 1766 : veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
235 : }
236 : }
237 : }
238 :
239 :
240 : void
241 37329433 : MELoop::addLeaderCar(MEVehicle* veh, MSLink* link) {
242 37329433 : myLeaderCars[veh->getEventTime()].push_back(veh);
243 37329433 : veh->setApproaching(link);
244 37329433 : }
245 :
246 :
247 : void
248 16 : MELoop::clearState() {
249 : myLeaderCars.clear();
250 16 : }
251 :
252 :
253 : bool
254 8578 : MELoop::removeLeaderCar(MEVehicle* v) {
255 8578 : const auto candIt = myLeaderCars.find(v->getEventTime());
256 8578 : if (candIt != myLeaderCars.end()) {
257 2425 : std::vector<MEVehicle*>& cands = candIt->second;
258 2425 : auto it = find(cands.begin(), cands.end(), v);
259 2425 : if (it != cands.end()) {
260 : cands.erase(it);
261 : return true;
262 : }
263 : }
264 : return false;
265 : }
266 :
267 :
268 : void
269 0 : MELoop::vaporizeCar(MEVehicle* v, MSMoveReminder::Notification reason) {
270 : int qIdx = 0;
271 0 : v->getSegment()->send(v, nullptr, qIdx, MSNet::getInstance()->getCurrentTimeStep(), reason);
272 0 : removeLeaderCar(v);
273 0 : }
274 :
275 :
276 : MESegment*
277 37151519 : MELoop::nextSegment(MESegment* s, MEVehicle* v) {
278 37151519 : if (s != nullptr) { // vehicle is not teleporting
279 : MESegment* next = s->getNextSegment();
280 37149783 : if (next != nullptr) {
281 : // ok, the street continues
282 : return next;
283 : }
284 : }
285 : // we have to check the next edge in the vehicle's route
286 16036148 : const MSEdge* nextEdge = v->succEdge(1);
287 16036148 : if (nextEdge == nullptr) {
288 : // end of route
289 : return nullptr;
290 : }
291 15360369 : if (MSGlobals::gUsingInternalLanes && s != nullptr && s->getEdge().isNormal()) {
292 2880962 : const MSEdge* internal = s->getEdge().getInternalFollowingEdge(nextEdge, v->getVClass());
293 2880962 : if (internal) {
294 : nextEdge = internal;
295 : }
296 : }
297 15360369 : return myEdges2FirstSegments[nextEdge->getNumericalID()];
298 : }
299 :
300 :
301 : int
302 473408 : MELoop::numSegmentsFor(const double length, const double sLength) {
303 473408 : int no = (int)floor(length / sLength + 0.5);
304 473408 : if (no == 0) { // assure there is at least one segment
305 : return 1;
306 : } else {
307 187791 : return no;
308 : }
309 : }
310 :
311 :
312 : void
313 325518 : MELoop::buildSegmentsFor(const MSEdge& e, const OptionsCont& oc) {
314 325518 : const MESegment::MesoEdgeType& edgeType = MSNet::getInstance()->getMesoType(e.getEdgeType());
315 : const double length = e.getLength();
316 325518 : const int numSegments = numSegmentsFor(length, edgeType.edgeLength);
317 325518 : const double slength = length / (double)numSegments;
318 : MESegment* newSegment = nullptr;
319 : MESegment* nextSegment = nullptr;
320 325518 : const bool laneQueue = oc.getBool("meso-lane-queue");
321 630176 : bool multiQueue = laneQueue || (oc.getBool("meso-multi-queue") && e.getLanes().size() > 1 && e.getNumSuccessors() > 1);
322 1010352 : for (int s = numSegments - 1; s >= 0; s--) {
323 1369668 : std::string id = e.getID() + ":" + toString(s);
324 684834 : newSegment = new MESegment(id, e, nextSegment, slength, e.getLanes()[0]->getSpeedLimit(), s, multiQueue, edgeType);
325 : multiQueue = laneQueue;
326 : nextSegment = newSegment;
327 : }
328 651036 : while (e.getNumericalID() >= static_cast<int>(myEdges2FirstSegments.size())) {
329 325518 : myEdges2FirstSegments.push_back(0);
330 : }
331 325518 : myEdges2FirstSegments[e.getNumericalID()] = newSegment;
332 696474 : for (MSLane* lane : e.getLanes()) {
333 370956 : lane->updateMesoGUISegments();
334 : }
335 325518 : }
336 :
337 :
338 : MESegment*
339 161401550 : MELoop::getSegmentForEdge(const MSEdge& e, double pos) {
340 161401550 : if (e.getNumericalID() >= (int)myEdges2FirstSegments.size()) {
341 : return nullptr;
342 : }
343 161401526 : MESegment* s = myEdges2FirstSegments[e.getNumericalID()];
344 161401526 : if (pos > 0) {
345 : double cpos = 0;
346 813081 : while (s->getNextSegment() != nullptr && cpos + s->getLength() < pos) {
347 : cpos += s->getLength();
348 : s = s->getNextSegment();
349 : }
350 : }
351 : return s;
352 : }
353 :
354 :
355 : bool
356 234800 : MELoop::isEnteringRoundabout(const MSEdge& e) {
357 639500 : for (const MSEdge* succ : e.getSuccessors()) {
358 404712 : if (succ->isRoundabout()) {
359 : return true;
360 : }
361 : }
362 : return false;
363 : }
364 :
365 :
366 : /****************************************************************************/
|