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