Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/mesosim/MELoop.cpp
169665 views
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 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
MELoop::MELoop(const SUMOTime recheckInterval) : myFullRecheckInterval(recheckInterval), myLinkRecheckInterval(TIME2STEPS(1)) {
47
}
48
49
MELoop::~MELoop() {
50
for (std::vector<MESegment*>::const_iterator j = myEdges2FirstSegments.begin(); j != myEdges2FirstSegments.end(); ++j) {
51
for (MESegment* s = *j; s != nullptr;) {
52
MESegment* n = s->getNextSegment();
53
delete s;
54
s = n;
55
}
56
}
57
}
58
59
60
void
61
MELoop::simulate(SUMOTime tMax) {
62
while (!myLeaderCars.empty()) {
63
const SUMOTime time = myLeaderCars.begin()->first;
64
std::vector<MEVehicle*> vehs = myLeaderCars[time];
65
assert(time > tMax - DELTA_T || vehs.size() == 0);
66
if (time > tMax) {
67
return;
68
}
69
myLeaderCars.erase(time);
70
for (std::vector<MEVehicle*>::const_iterator i = vehs.begin(); i != vehs.end(); ++i) {
71
checkCar(*i);
72
assert(myLeaderCars.empty() || myLeaderCars.begin()->first >= time);
73
}
74
}
75
}
76
77
78
SUMOTime
79
MELoop::changeSegment(MEVehicle* veh, SUMOTime leaveTime, MESegment* const toSegment, MSMoveReminder::Notification reason, const bool ignoreLink) const {
80
int qIdx = 0;
81
MESegment* const onSegment = veh->getSegment();
82
if (MESegment::isInvalid(toSegment)) {
83
if (veh->isStoppedTriggered()) {
84
return leaveTime + MAX2(SUMOTime(1), myLinkRecheckInterval);
85
}
86
if (onSegment != nullptr) {
87
onSegment->send(veh, toSegment, qIdx, leaveTime, reason);
88
} else {
89
WRITE_WARNINGF(TL("Vehicle '%' teleports beyond arrival edge '%', time=%."),
90
veh->getID(), veh->getEdge()->getID(), time2string(leaveTime));
91
}
92
veh->setSegment(toSegment); // signal arrival
93
MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
94
return leaveTime;
95
} else if (!MSGlobals::gCheckRoutes && !ignoreLink && !MESegment::isInvalid(onSegment) && &onSegment->getEdge() != &toSegment->getEdge() &&
96
veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr) {
97
if (veh->isStopped()) {
98
veh->processStop();
99
}
100
return SUMOTime_MAX;
101
}
102
const SUMOTime entry = toSegment->hasSpaceFor(veh, leaveTime, qIdx);
103
if (entry == leaveTime && (ignoreLink || veh->mayProceed())) {
104
if (onSegment != nullptr) {
105
if (veh->getQueIndex() == MESegment::PARKING_QUEUE) { // parking or just aborted parking
106
if (veh->isParking()) {
107
veh->processStop();
108
}
109
veh->getEdge()->getLanes()[0]->removeParking(veh); // TODO for GUI only
110
} else {
111
onSegment->send(veh, toSegment, qIdx, leaveTime, onSegment->getNextSegment() == nullptr ? MSMoveReminder::NOTIFICATION_JUNCTION : MSMoveReminder::NOTIFICATION_SEGMENT);
112
}
113
toSegment->receive(veh, qIdx, leaveTime, false, ignoreLink, &onSegment->getEdge() != &toSegment->getEdge());
114
} else {
115
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
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
veh->updateDetectors(veh->getLastEntryTime(), true, MSMoveReminder::NOTIFICATION_TELEPORT);
123
toSegment->receive(veh, qIdx, leaveTime, false, true, true);
124
}
125
return entry;
126
}
127
if (entry == leaveTime && !ignoreLink) { // this is a long way of saying !veh->mayProceed() (which is a costly call)
128
return entry + MAX2(SUMOTime(1), myLinkRecheckInterval);
129
}
130
return entry;
131
}
132
133
134
void
135
MELoop::checkCar(MEVehicle* veh) {
136
const SUMOTime leaveTime = veh->getEventTime();
137
MESegment* const onSegment = veh->getSegment();
138
MESegment* const toSegment = veh->getQueIndex() == MESegment::PARKING_QUEUE ? onSegment : nextSegment(onSegment, veh);
139
const bool teleporting = (onSegment == nullptr); // is the vehicle currently teleporting?
140
// @note reason is only evaluated if toSegment == nullptr
141
const SUMOTime nextEntry = changeSegment(veh, leaveTime, toSegment, MSMoveReminder::NOTIFICATION_ARRIVED, teleporting);
142
if (nextEntry == leaveTime) {
143
return;
144
}
145
const bool r1 = MSGlobals::gTimeToGridlock > 0 && veh->getWaitingTime() > MSGlobals::gTimeToGridlock;
146
const bool r3 = MSGlobals::gTimeToTeleportDisconnected >= 0 && veh->getWaitingTime() > MSGlobals::gTimeToTeleportDisconnected;
147
if (!veh->isStopped() && (r1 || r3)) {
148
const bool disconnected = (MSGlobals::gTimeToTeleportDisconnected >= 0
149
&& veh->succEdge(1) != nullptr
150
&& veh->getEdge()->allowedLanes(*veh->succEdge(1), veh->getVClass()) == nullptr);
151
if ((r1 && !disconnected) || (r3 && disconnected)) {
152
teleportVehicle(veh, toSegment, disconnected);
153
return;
154
}
155
}
156
if (veh->getBlockTime() == SUMOTime_MAX && !veh->isStopped()) {
157
veh->setBlockTime(leaveTime);
158
}
159
if (nextEntry == SUMOTime_MAX) {
160
// all usable queues on the next segment are full
161
SUMOTime newEventTime = MAX3(toSegment->getEventTime() + 1, leaveTime + 1, leaveTime + myFullRecheckInterval);
162
if (MSGlobals::gTimeToGridlock > 0) {
163
// if teleporting is enabled, make sure we look at the vehicle when the gridlock-time is up
164
const SUMOTime recheck = MSGlobals::gTimeToTeleportDisconnected >= 0 ? MIN2(MSGlobals::gTimeToGridlock, MSGlobals::gTimeToTeleportDisconnected) : MSGlobals::gTimeToGridlock;
165
newEventTime = MAX2(MIN2(newEventTime, veh->getBlockTime() + recheck + 1), leaveTime + DELTA_T);
166
}
167
veh->setEventTime(newEventTime);
168
} else {
169
// receiving segment has recently received another vehicle or the junction is blocked
170
veh->setEventTime(nextEntry);
171
}
172
addLeaderCar(veh, teleporting ? nullptr : onSegment->getLink(veh));
173
}
174
175
176
void
177
MELoop::teleportVehicle(MEVehicle* veh, MESegment* const toSegment, bool disconnected) {
178
const SUMOTime leaveTime = veh->getEventTime();
179
MESegment* const onSegment = veh->getSegment();
180
if (MSGlobals::gRemoveGridlocked) {
181
WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
182
veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(),
183
time2string(leaveTime));
184
MSNet::getInstance()->getVehicleControl().registerTeleportJam();
185
int qIdx = 0;
186
onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED);
187
veh->setSegment(nullptr);
188
MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
189
return;
190
}
191
const bool teleporting = (onSegment == nullptr); // is the vehicle already teleporting?
192
// try to find a place on the current edge
193
MESegment* teleSegment = disconnected ? toSegment : toSegment->getNextSegment();
194
while (teleSegment != nullptr && changeSegment(veh, leaveTime, teleSegment, MSMoveReminder::NOTIFICATION_TELEPORT, true) != leaveTime) {
195
// @caution the time to get to the next segment here is ignored XXX
196
teleSegment = teleSegment->getNextSegment();
197
}
198
if (teleSegment != nullptr) {
199
if (!teleporting) {
200
// we managed to teleport in a single jump
201
const std::string reason = disconnected ? " (disconnected)" : "";
202
WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long%, from edge '%':% to edge '%':%, time=%."),
203
veh->getID(), reason, onSegment->getEdge().getID(), onSegment->getIndex(),
204
teleSegment->getEdge().getID(), teleSegment->getIndex(), time2string(leaveTime));
205
MSNet::getInstance()->getVehicleControl().registerTeleportJam();
206
}
207
} else {
208
// teleport across the current edge and try insertion later
209
if (!teleporting) {
210
int qIdx = 0;
211
// announce start of multi-step teleport, arrival will be announced in changeSegment()
212
WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
213
veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(), time2string(leaveTime));
214
MSNet::getInstance()->getVehicleControl().registerTeleportJam();
215
// remove from current segment
216
onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT);
217
// mark veh as teleporting
218
veh->setSegment(nullptr);
219
}
220
// @caution microsim uses current travel time teleport duration
221
const SUMOTime teleArrival = leaveTime + TIME2STEPS(veh->getEdge()->getLength() / MAX2(veh->getEdge()->getSpeedLimit(), NUMERICAL_EPS));
222
const bool atDest = veh->moveRoutePointer();
223
if (atDest) {
224
// teleporting to end of route
225
changeSegment(veh, teleArrival, nullptr, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED, true);
226
} else {
227
veh->setEventTime(teleArrival);
228
addLeaderCar(veh, nullptr);
229
// teleporting vehicles must react to rerouters
230
getSegmentForEdge(*veh->getEdge())->addReminders(veh);
231
veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
232
}
233
}
234
}
235
236
237
void
238
MELoop::addLeaderCar(MEVehicle* veh, MSLink* link) {
239
myLeaderCars[veh->getEventTime()].push_back(veh);
240
veh->setApproaching(link);
241
}
242
243
244
void
245
MELoop::clearState() {
246
myLeaderCars.clear();
247
}
248
249
250
bool
251
MELoop::removeLeaderCar(MEVehicle* v) {
252
const auto candIt = myLeaderCars.find(v->getEventTime());
253
if (candIt != myLeaderCars.end()) {
254
std::vector<MEVehicle*>& cands = candIt->second;
255
auto it = find(cands.begin(), cands.end(), v);
256
if (it != cands.end()) {
257
cands.erase(it);
258
return true;
259
}
260
}
261
return false;
262
}
263
264
265
void
266
MELoop::vaporizeCar(MEVehicle* v, MSMoveReminder::Notification reason) {
267
int qIdx = 0;
268
v->getSegment()->send(v, nullptr, qIdx, MSNet::getInstance()->getCurrentTimeStep(), reason);
269
removeLeaderCar(v);
270
}
271
272
273
MESegment*
274
MELoop::nextSegment(MESegment* s, MEVehicle* v) {
275
if (s != nullptr) { // vehicle is not teleporting
276
MESegment* next = s->getNextSegment();
277
if (next != nullptr) {
278
// ok, the street continues
279
return next;
280
}
281
}
282
// we have to check the next edge in the vehicle's route
283
const MSEdge* nextEdge = v->succEdge(1);
284
if (nextEdge == nullptr) {
285
// end of route
286
return nullptr;
287
}
288
return myEdges2FirstSegments[nextEdge->getNumericalID()];
289
}
290
291
292
int
293
MELoop::numSegmentsFor(const double length, const double sLength) {
294
int no = (int)floor(length / sLength + 0.5);
295
if (no == 0) { // assure there is at least one segment
296
return 1;
297
} else {
298
return no;
299
}
300
}
301
302
303
void
304
MELoop::buildSegmentsFor(const MSEdge& e, const OptionsCont& oc) {
305
const MESegment::MesoEdgeType& edgeType = MSNet::getInstance()->getMesoType(e.getEdgeType());
306
const double length = e.getLength();
307
const int numSegments = numSegmentsFor(length, oc.getFloat("meso-edgelength"));
308
const double slength = length / (double)numSegments;
309
MESegment* newSegment = nullptr;
310
MESegment* nextSegment = nullptr;
311
const bool laneQueue = oc.getBool("meso-lane-queue");
312
bool multiQueue = laneQueue || (oc.getBool("meso-multi-queue") && e.getLanes().size() > 1 && e.getNumSuccessors() > 1);
313
for (int s = numSegments - 1; s >= 0; s--) {
314
std::string id = e.getID() + ":" + toString(s);
315
newSegment = new MESegment(id, e, nextSegment, slength, e.getLanes()[0]->getSpeedLimit(), s, multiQueue, edgeType);
316
multiQueue = laneQueue;
317
nextSegment = newSegment;
318
}
319
while (e.getNumericalID() >= static_cast<int>(myEdges2FirstSegments.size())) {
320
myEdges2FirstSegments.push_back(0);
321
}
322
myEdges2FirstSegments[e.getNumericalID()] = newSegment;
323
}
324
325
326
void
327
MELoop::updateSegmentsForEdge(const MSEdge& e) {
328
if (e.getNumericalID() < (int)myEdges2FirstSegments.size()) {
329
const MESegment::MesoEdgeType& edgeType = MSNet::getInstance()->getMesoType(e.getEdgeType());
330
MESegment* s = myEdges2FirstSegments[e.getNumericalID()];
331
while (s != nullptr) {
332
s->initSegment(edgeType, e, s->getCapacity());
333
s = s->getNextSegment();
334
}
335
}
336
}
337
338
339
MESegment*
340
MELoop::getSegmentForEdge(const MSEdge& e, double pos) {
341
if (e.getNumericalID() >= (int)myEdges2FirstSegments.size()) {
342
return nullptr;
343
}
344
MESegment* s = myEdges2FirstSegments[e.getNumericalID()];
345
if (pos > 0) {
346
double cpos = 0;
347
while (s->getNextSegment() != nullptr && cpos + s->getLength() < pos) {
348
cpos += s->getLength();
349
s = s->getNextSegment();
350
}
351
}
352
return s;
353
}
354
355
356
bool
357
MELoop::isEnteringRoundabout(const MSEdge& e) {
358
for (const MSEdge* succ : e.getSuccessors()) {
359
if (succ->isRoundabout()) {
360
return true;
361
}
362
}
363
return false;
364
}
365
366
367
/****************************************************************************/
368
369