Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/mesosim/MEVehicle.cpp
169666 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 MEVehicle.cpp
15
/// @author Daniel Krajzewicz
16
/// @author Michael Behrisch
17
/// @date Tue, May 2005
18
///
19
// A vehicle from the mesoscopic point of view
20
/****************************************************************************/
21
#include <config.h>
22
23
#include <iostream>
24
#include <cassert>
25
#include <utils/common/StdDefs.h>
26
#include <utils/common/FileHelpers.h>
27
#include <utils/common/MsgHandler.h>
28
#include <utils/geom/GeomHelper.h>
29
#include <utils/iodevices/OutputDevice.h>
30
#include <utils/xml/SUMOSAXAttributes.h>
31
#include <microsim/devices/MSDevice_Tripinfo.h>
32
#include <microsim/devices/MSDevice_Vehroutes.h>
33
#include <microsim/devices/MSDevice_Taxi.h>
34
#include <microsim/output/MSStopOut.h>
35
#include <microsim/MSGlobals.h>
36
#include <microsim/MSEdge.h>
37
#include <microsim/MSLane.h>
38
#include <microsim/MSNet.h>
39
#include <microsim/MSVehicleType.h>
40
#include <microsim/MSLink.h>
41
#include <microsim/MSStop.h>
42
#include <microsim/MSVehicleControl.h>
43
#include <microsim/transportables/MSTransportableControl.h>
44
#include <microsim/devices/MSDevice.h>
45
#include "MELoop.h"
46
#include "MEVehicle.h"
47
#include "MESegment.h"
48
49
50
// ===========================================================================
51
// method definitions
52
// ===========================================================================
53
MEVehicle::MEVehicle(SUMOVehicleParameter* pars, ConstMSRoutePtr route,
54
MSVehicleType* type, const double speedFactor) :
55
MSBaseVehicle(pars, route, type, speedFactor),
56
mySegment(nullptr),
57
myQueIndex(0),
58
myEventTime(SUMOTime_MIN),
59
myLastEntryTime(SUMOTime_MIN),
60
myBlockTime(SUMOTime_MAX),
61
myInfluencer(nullptr) {
62
}
63
64
65
double
66
MEVehicle::getBackPositionOnLane(const MSLane* /* lane */) const {
67
return getPositionOnLane() - getVehicleType().getLength();
68
}
69
70
71
double
72
MEVehicle::getPositionOnLane() const {
73
// the following interpolation causes problems with arrivals and calibrators
74
// const double fracOnSegment = MIN2(double(1), STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep() - myLastEntryTime) / STEPS2TIME(myEventTime - myLastEntryTime));
75
return mySegment == nullptr ? 0 : (double(mySegment->getIndex()) /* + fracOnSegment */) * mySegment->getLength();
76
}
77
78
79
double
80
MEVehicle::getAngle() const {
81
const MSLane* const lane = getEdge()->getLanes()[0];
82
return lane->getShape().rotationAtOffset(lane->interpolateLanePosToGeometryPos(getPositionOnLane()));
83
}
84
85
86
double
87
MEVehicle::getSlope() const {
88
const MSLane* const lane = getEdge()->getLanes()[0];
89
return lane->getShape().slopeDegreeAtOffset(lane->interpolateLanePosToGeometryPos(getPositionOnLane()));
90
}
91
92
93
Position
94
MEVehicle::getPosition(const double offset) const {
95
const MSLane* const lane = getEdge()->getLanes()[0];
96
return lane->geometryPositionAtOffset(getPositionOnLane() + offset);
97
}
98
99
PositionVector
100
MEVehicle::getBoundingBox(double offset) const {
101
double a = getAngle() + M_PI; // angle pointing backwards
102
double l = getLength();
103
Position pos = getPosition();
104
Position backPos = pos + Position(l * cos(a), l * sin(a));
105
PositionVector centerLine;
106
centerLine.push_back(pos);
107
centerLine.push_back(backPos);
108
if (offset != 0) {
109
centerLine.extrapolate2D(offset);
110
}
111
PositionVector result = centerLine;
112
result.move2side(MAX2(0.0, 0.5 * myType->getWidth() + offset));
113
centerLine.move2side(MIN2(0.0, -0.5 * myType->getWidth() - offset));
114
result.append(centerLine.reverse(), POSITION_EPS);
115
return result;
116
}
117
118
double
119
MEVehicle::getSpeed() const {
120
if (getWaitingTime() > 0 || isStopped()) {
121
return 0;
122
} else {
123
return getAverageSpeed();
124
}
125
}
126
127
128
double
129
MEVehicle::getAverageSpeed() const {
130
// cache for thread safety
131
MESegment* s = mySegment;
132
if (s == nullptr || myQueIndex == MESegment::PARKING_QUEUE) {
133
return 0;
134
} else {
135
return MIN2(s->getLength() / STEPS2TIME(myEventTime - myLastEntryTime),
136
getEdge()->getLanes()[myQueIndex]->getVehicleMaxSpeed(this));
137
}
138
}
139
140
141
double
142
MEVehicle::estimateLeaveSpeed(const MSLink* link) const {
143
/// @see MSVehicle.cpp::estimateLeaveSpeed
144
const double v = getSpeed();
145
return MIN2(link->getViaLaneOrLane()->getVehicleMaxSpeed(this),
146
(double)sqrt(2 * link->getLength() * getVehicleType().getCarFollowModel().getMaxAccel() + v * v));
147
}
148
149
150
double
151
MEVehicle::getConservativeSpeed(SUMOTime& earliestArrival) const {
152
earliestArrival = MAX2(myEventTime, earliestArrival - DELTA_T); // event times have subsecond resolution
153
return mySegment->getLength() / STEPS2TIME(earliestArrival - myLastEntryTime);
154
}
155
156
157
bool
158
MEVehicle::moveRoutePointer() {
159
// vehicle has just entered a new edge. Position is 0
160
if (myCurrEdge == myRoute->end() - 1 || (myParameter->arrivalEdge >= 0 && getRoutePosition() >= myParameter->arrivalEdge)) { // may happen during teleport
161
return true;
162
}
163
++myCurrEdge;
164
if ((*myCurrEdge)->isVaporizing()) {
165
return true;
166
}
167
// update via
168
if (myParameter->via.size() > 0 && (*myCurrEdge)->getID() == myParameter->via.front()) {
169
myParameter->via.erase(myParameter->via.begin());
170
}
171
return hasArrived();
172
}
173
174
175
bool
176
MEVehicle::hasArrived() const {
177
// mySegment may be 0 due to teleporting or arrival
178
return (myCurrEdge == myRoute->end() - 1 || (myParameter->arrivalEdge >= 0 && getRoutePosition() >= myParameter->arrivalEdge)) && (
179
(mySegment == nullptr)
180
|| myEventTime == SUMOTime_MIN
181
|| getPositionOnLane() > myArrivalPos - POSITION_EPS);
182
}
183
184
185
bool
186
MEVehicle::isOnRoad() const {
187
return getSegment() != nullptr;
188
}
189
190
191
bool
192
MEVehicle::isIdling() const {
193
return false;
194
}
195
196
197
void
198
MEVehicle::setApproaching(MSLink* link) {
199
if (link != nullptr) {
200
const double speed = getSpeed();
201
link->setApproaching(this, getEventTime() + (link->getState() == LINKSTATE_ALLWAY_STOP ?
202
(SUMOTime)RandHelper::rand((int)2) : 0), // tie braker
203
speed, speed, true,
204
speed, getWaitingTime(),
205
// @note: dist is not used by meso (getZipperSpeed is never called)
206
getSegment()->getLength(), 0);
207
}
208
}
209
210
211
bool
212
MEVehicle::replaceRoute(ConstMSRoutePtr newRoute, const std::string& info, bool onInit, int offset, bool addRouteStops, bool removeStops, std::string* msgReturn) {
213
MSLink* const oldLink = mySegment != nullptr ? mySegment->getLink(this) : nullptr;
214
if (MSBaseVehicle::replaceRoute(newRoute, info, onInit, offset, addRouteStops, removeStops, msgReturn)) {
215
if (mySegment != nullptr) {
216
MSLink* const newLink = mySegment->getLink(this);
217
// update approaching vehicle information
218
if (oldLink != newLink) {
219
if (oldLink != nullptr) {
220
oldLink->removeApproaching(this);
221
}
222
setApproaching(newLink);
223
}
224
}
225
return true;
226
}
227
return false;
228
}
229
230
231
SUMOTime
232
MEVehicle::checkStop(SUMOTime time) {
233
const SUMOTime initialTime = time;
234
bool hadStop = false;
235
for (MSStop& stop : myStops) {
236
if (stop.joinTriggered) {
237
WRITE_WARNINGF(TL("Join stops are not available in meso yet (vehicle '%', segment '%')."),
238
getID(), mySegment->getID());
239
continue;
240
}
241
if (stop.edge != myCurrEdge || stop.segment != mySegment) {
242
break;
243
}
244
const SUMOTime cur = time;
245
if (stop.duration > 0) { // it might be a triggered stop with duration -1
246
time += stop.duration;
247
}
248
if (stop.pars.until > time) {
249
// @note: this assumes the stop is reached at time. With the way this is called in MESegment (time == entryTime),
250
// travel time is overestimated of the stop is not at the start of the segment
251
time = stop.pars.until;
252
}
253
if (MSGlobals::gUseStopEnded && stop.pars.ended >= 0) {
254
time = MAX2(cur, stop.pars.ended);
255
}
256
if (!stop.reached) {
257
stop.reached = true;
258
stop.pars.started = myLastEntryTime;
259
stop.endBoarding = stop.pars.extension >= 0 ? time + stop.pars.extension : SUMOTime_MAX;
260
if (MSStopOut::active()) {
261
if (!hadStop) {
262
MSStopOut::getInstance()->stopStarted(this, getPersonNumber(), getContainerNumber(), myLastEntryTime);
263
} else {
264
WRITE_WARNINGF(TL("Vehicle '%' has multiple stops on segment '%', time=% (stop-output will be merged)."),
265
getID(), mySegment->getID(), time2string(time));
266
}
267
}
268
MSDevice_Taxi* taxi = static_cast<MSDevice_Taxi*>(getDevice(typeid(MSDevice_Taxi)));
269
if (taxi != nullptr) {
270
taxi->notifyMove(*this, 0, 0, 0);
271
}
272
}
273
if (stop.triggered || stop.containerTriggered || stop.joinTriggered) {
274
time = MAX2(time, cur + DELTA_T);
275
}
276
hadStop = true;
277
}
278
MSDevice_Tripinfo* tripinfo = static_cast<MSDevice_Tripinfo*>(getDevice(typeid(MSDevice_Tripinfo)));
279
if (tripinfo != nullptr) {
280
tripinfo->updateStopTime(time - initialTime);
281
}
282
return time;
283
}
284
285
286
bool
287
MEVehicle::resumeFromStopping() {
288
if (isStopped()) {
289
const SUMOTime now = SIMSTEP;
290
MSStop& stop = myStops.front();
291
stop.pars.ended = now;
292
for (const auto& rem : myMoveReminders) {
293
rem.first->notifyStopEnded();
294
}
295
if (MSStopOut::active()) {
296
MSStopOut::getInstance()->stopEnded(this, stop.pars, mySegment->getEdge().getID());
297
}
298
myPastStops.push_back(stop.pars);
299
myPastStops.back().routeIndex = (int)(stop.edge - myRoute->begin());
300
if (myAmRegisteredAsWaiting && (stop.triggered || stop.containerTriggered || stop.joinTriggered)) {
301
MSNet::getInstance()->getVehicleControl().unregisterOneWaiting();
302
myAmRegisteredAsWaiting = false;
303
}
304
myStops.pop_front();
305
if (myEventTime > now) {
306
// if this is an aborted stop we need to change the event time of the vehicle
307
if (MSGlobals::gMesoNet->removeLeaderCar(this)) {
308
myEventTime = now + 1;
309
MSGlobals::gMesoNet->addLeaderCar(this, nullptr);
310
}
311
}
312
return true;
313
}
314
return false;
315
}
316
317
318
double
319
MEVehicle::getCurrentStoppingTimeSeconds() const {
320
SUMOTime time = myLastEntryTime;
321
for (const MSStop& stop : myStops) {
322
if (stop.reached) {
323
time += stop.duration;
324
if (stop.pars.until > time) {
325
// @note: this assumes the stop is reached at time. With the way this is called in MESegment (time == entryTime),
326
// travel time is overestimated of the stop is not at the start of the segment
327
time = stop.pars.until;
328
}
329
} else {
330
break;
331
}
332
}
333
return STEPS2TIME(time - myLastEntryTime);
334
}
335
336
337
void
338
MEVehicle::processStop() {
339
assert(isStopped());
340
double lastPos = -1;
341
bool hadStop = false;
342
while (!myStops.empty()) {
343
MSStop& stop = myStops.front();
344
if (stop.edge != myCurrEdge || stop.segment != mySegment || stop.pars.endPos <= lastPos) {
345
break;
346
}
347
lastPos = stop.pars.endPos;
348
MSNet* const net = MSNet::getInstance();
349
SUMOTime dummy = -1; // boarding- and loading-time are not considered
350
if (hadStop && MSStopOut::active()) {
351
stop.reached = true;
352
MSStopOut::getInstance()->stopStarted(this, getPersonNumber(), getContainerNumber(), myLastEntryTime);
353
}
354
if (net->hasPersons()) {
355
net->getPersonControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy);
356
}
357
if (net->hasContainers()) {
358
net->getContainerControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy);
359
}
360
resumeFromStopping();
361
hadStop = true;
362
}
363
mySegment->getEdge().removeWaiting(this);
364
}
365
366
367
bool
368
MEVehicle::mayProceed() {
369
if (mySegment == nullptr) {
370
return true;
371
}
372
MSNet* const net = MSNet::getInstance();
373
SUMOTime dummy = -1; // boarding- and loading-time are not considered
374
for (MSStop& stop : myStops) {
375
if (!stop.reached) {
376
break;
377
}
378
if (net->getCurrentTimeStep() > stop.endBoarding) {
379
if (stop.triggered || stop.containerTriggered) {
380
MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(getDevice(typeid(MSDevice_Taxi)));
381
if (taxiDevice != nullptr) {
382
taxiDevice->cancelCurrentCustomers();
383
}
384
stop.triggered = false;
385
stop.containerTriggered = false;
386
}
387
if (myAmRegisteredAsWaiting) {
388
net->getVehicleControl().unregisterOneWaiting();
389
myAmRegisteredAsWaiting = false;
390
}
391
}
392
if (stop.triggered) {
393
if (getVehicleType().getPersonCapacity() == getPersonNumber()) {
394
// we could not check this on entering the segment because there may be persons who still want to leave
395
WRITE_WARNINGF(TL("Vehicle '%' ignores triggered stop on lane '%' due to capacity constraints."), getID(), stop.lane->getID());
396
stop.triggered = false;
397
if (myAmRegisteredAsWaiting) {
398
net->getVehicleControl().unregisterOneWaiting();
399
myAmRegisteredAsWaiting = false;
400
}
401
} else if (!net->hasPersons() || !net->getPersonControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy)) {
402
if (!myAmRegisteredAsWaiting) {
403
MSNet::getInstance()->getVehicleControl().registerOneWaiting();
404
myAmRegisteredAsWaiting = true;
405
}
406
return false;
407
}
408
}
409
if (stop.containerTriggered) {
410
if (getVehicleType().getContainerCapacity() == getContainerNumber()) {
411
// we could not check this on entering the segment because there may be containers who still want to leave
412
WRITE_WARNINGF(TL("Vehicle '%' ignores container triggered stop on lane '%' due to capacity constraints."), getID(), stop.lane->getID());
413
stop.containerTriggered = false;
414
if (myAmRegisteredAsWaiting) {
415
net->getVehicleControl().unregisterOneWaiting();
416
myAmRegisteredAsWaiting = false;
417
}
418
} else if (!net->hasContainers() || !net->getContainerControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy)) {
419
if (!myAmRegisteredAsWaiting) {
420
MSNet::getInstance()->getVehicleControl().registerOneWaiting();
421
myAmRegisteredAsWaiting = true;
422
}
423
return false;
424
}
425
}
426
if (stop.joinTriggered) {
427
// TODO do something useful here
428
return false;
429
}
430
}
431
return mySegment->isOpen(this);
432
}
433
434
435
double
436
MEVehicle::getCurrentLinkPenaltySeconds() const {
437
if (mySegment == nullptr) {
438
return 0;
439
} else {
440
return STEPS2TIME(mySegment->getLinkPenalty(this));
441
}
442
}
443
444
445
void
446
MEVehicle::updateDetectorForWriting(MSMoveReminder* rem, SUMOTime currentTime, SUMOTime exitTime) {
447
for (MoveReminderCont::iterator i = myMoveReminders.begin(); i != myMoveReminders.end(); ++i) {
448
if (i->first == rem) {
449
rem->updateDetector(*this, mySegment->getIndex() * mySegment->getLength(),
450
(mySegment->getIndex() + 1) * mySegment->getLength(),
451
getLastEntryTime(), currentTime, exitTime, false);
452
#ifdef _DEBUG
453
if (myTraceMoveReminders) {
454
traceMoveReminder("notifyMove", i->first, i->second, true);
455
}
456
#endif
457
return;
458
}
459
}
460
}
461
462
463
void
464
MEVehicle::updateDetectors(SUMOTime currentTime, const bool isLeave, const MSMoveReminder::Notification reason) {
465
// segments of the same edge have the same reminder so no cleaning up must take place
466
const bool cleanUp = isLeave && (reason != MSMoveReminder::NOTIFICATION_SEGMENT);
467
for (MoveReminderCont::iterator rem = myMoveReminders.begin(); rem != myMoveReminders.end();) {
468
if (currentTime != getLastEntryTime() && reason < MSMoveReminder::NOTIFICATION_VAPORIZED_CALIBRATOR) {
469
rem->first->updateDetector(*this, mySegment->getIndex() * mySegment->getLength(),
470
(mySegment->getIndex() + 1) * mySegment->getLength(),
471
getLastEntryTime(), currentTime, getEventTime(), cleanUp);
472
#ifdef _DEBUG
473
if (myTraceMoveReminders) {
474
traceMoveReminder("notifyMove", rem->first, rem->second, true);
475
}
476
#endif
477
}
478
if (!isLeave || rem->first->notifyLeave(*this, mySegment->getLength(), reason)) {
479
#ifdef _DEBUG
480
if (isLeave && myTraceMoveReminders) {
481
traceMoveReminder("notifyLeave", rem->first, rem->second, true);
482
}
483
#endif
484
485
if (isLeave) {
486
rem->second += getEdge()->getLength();
487
#ifdef _DEBUG
488
if (myTraceMoveReminders) {
489
traceMoveReminder("adaptedPos", rem->first, rem->second, true);
490
}
491
#endif
492
}
493
++rem;
494
} else {
495
#ifdef _DEBUG
496
if (myTraceMoveReminders) {
497
traceMoveReminder("remove", rem->first, rem->second, false);
498
}
499
#endif
500
rem = myMoveReminders.erase(rem);
501
}
502
}
503
if (reason == MSMoveReminder::NOTIFICATION_JUNCTION || reason == MSMoveReminder::NOTIFICATION_TELEPORT) {
504
myOdometer += getEdge()->getLength();
505
}
506
}
507
508
509
MEVehicle::BaseInfluencer&
510
MEVehicle::getBaseInfluencer() {
511
if (myInfluencer == nullptr) {
512
myInfluencer = new BaseInfluencer();
513
}
514
return *myInfluencer;
515
}
516
517
518
const MEVehicle::BaseInfluencer*
519
MEVehicle::getBaseInfluencer() const {
520
return myInfluencer;
521
}
522
523
524
void
525
MEVehicle::onRemovalFromNet(const MSMoveReminder::Notification reason) {
526
MSGlobals::gMesoNet->removeLeaderCar(this);
527
MSGlobals::gMesoNet->changeSegment(this, MSNet::getInstance()->getCurrentTimeStep(), nullptr, reason);
528
}
529
530
531
int
532
MEVehicle::getSegmentIndex() const {
533
return getSegment() != nullptr ? getSegment()->getIndex() : -1;
534
}
535
536
537
double
538
MEVehicle::getRightSideOnEdge(const MSLane* /*lane*/) const {
539
if (mySegment == nullptr || mySegment->getIndex() >= getEdge()->getNumLanes()) {
540
return 0;
541
}
542
const MSLane* lane = getEdge()->getLanes()[mySegment->getIndex()];
543
return lane->getRightSideOnEdge() + lane->getWidth() * 0.5 - 0.5 * getVehicleType().getWidth();
544
545
}
546
547
548
void
549
MEVehicle::saveState(OutputDevice& out) {
550
if (mySegment != nullptr && MESegment::isInvalid(mySegment)) {
551
// segment is vaporization target, do not write this vehicle
552
return;
553
}
554
MSBaseVehicle::saveState(out);
555
assert(mySegment == nullptr || *myCurrEdge == &mySegment->getEdge());
556
std::vector<SUMOTime> internals;
557
internals.push_back(myParameter->parametersSet);
558
internals.push_back(myDeparture);
559
internals.push_back((SUMOTime)distance(myRoute->begin(), myCurrEdge));
560
internals.push_back((SUMOTime)myDepartPos * 1000); // store as mm
561
internals.push_back(mySegment == nullptr ? (SUMOTime) - 1 : (SUMOTime)mySegment->getIndex());
562
internals.push_back((SUMOTime)getQueIndex());
563
internals.push_back(myEventTime);
564
internals.push_back(myLastEntryTime);
565
internals.push_back(myBlockTime);
566
out.writeAttr(SUMO_ATTR_STATE, toString(internals));
567
// save past stops
568
for (SUMOVehicleParameter::Stop stop : myPastStops) {
569
stop.write(out, false);
570
// do not write started and ended twice
571
if ((stop.parametersSet & STOP_STARTED_SET) == 0) {
572
out.writeAttr(SUMO_ATTR_STARTED, time2string(stop.started));
573
}
574
if ((stop.parametersSet & STOP_ENDED_SET) == 0) {
575
out.writeAttr(SUMO_ATTR_ENDED, time2string(stop.ended));
576
}
577
out.closeTag();
578
}
579
// save upcoming stops
580
for (const MSStop& stop : myStops) {
581
stop.write(out);
582
}
583
// save parameters
584
myParameter->writeParams(out);
585
for (MSDevice* dev : myDevices) {
586
dev->saveState(out);
587
}
588
for (const auto& item : myMoveReminders) {
589
item.first->saveReminderState(out, *this);
590
}
591
out.closeTag();
592
}
593
594
595
void
596
MEVehicle::loadState(const SUMOSAXAttributes& attrs, const SUMOTime offset) {
597
if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
598
throw ProcessError(TL("Error: Invalid vehicles in state (may be a micro state)!"));
599
}
600
int routeOffset;
601
int segIndex;
602
int queIndex;
603
std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
604
bis >> myParameter->parametersSet;
605
bis >> myDeparture;
606
bis >> routeOffset;
607
bis >> myDepartPos;
608
bis >> segIndex;
609
bis >> queIndex;
610
bis >> myEventTime;
611
bis >> myLastEntryTime;
612
bis >> myBlockTime;
613
myDepartPos /= 1000.; // was stored as mm
614
615
if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS_RANDOMIZED)) {
616
bool ok;
617
myArrivalPos = attrs.get<double>(SUMO_ATTR_ARRIVALPOS_RANDOMIZED, getID().c_str(), ok);
618
}
619
620
// load stops
621
myStops.clear();
622
addStops(!MSGlobals::gCheckRoutes, &myCurrEdge, false);
623
624
if (hasDeparted()) {
625
myDeparture -= offset;
626
myEventTime -= offset;
627
myLastEntryTime -= offset;
628
myCurrEdge = myRoute->begin() + routeOffset;
629
if (segIndex >= 0) {
630
MESegment* seg = MSGlobals::gMesoNet->getSegmentForEdge(**myCurrEdge);
631
while (seg->getIndex() != (int)segIndex) {
632
seg = seg->getNextSegment();
633
if (seg == nullptr) {
634
throw ProcessError(TLF("Unknown segment '%:%' for vehicle '%' in loaded state.", (*myCurrEdge)->getID(), segIndex, getID()));
635
}
636
}
637
setSegment(seg, queIndex);
638
if (queIndex == MESegment::PARKING_QUEUE) {
639
MSGlobals::gMesoNet->addLeaderCar(this, nullptr);
640
}
641
} else {
642
// on teleport
643
setSegment(nullptr, 0);
644
assert(myEventTime != SUMOTime_MIN);
645
MSGlobals::gMesoNet->addLeaderCar(this, nullptr);
646
}
647
// see MSBaseVehicle constructor
648
if (myParameter->wasSet(VEHPARS_FORCE_REROUTE)) {
649
calculateArrivalParams(true);
650
}
651
}
652
if (myBlockTime != SUMOTime_MAX) {
653
myBlockTime -= offset;
654
}
655
std::istringstream dis(attrs.getString(SUMO_ATTR_DISTANCE));
656
dis >> myOdometer >> myNumberReroutes;
657
}
658
659
660
/****************************************************************************/
661
662