Line data Source code
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 742794 : MEVehicle::MEVehicle(SUMOVehicleParameter* pars, ConstMSRoutePtr route,
54 742794 : MSVehicleType* type, const double speedFactor) :
55 : MSBaseVehicle(pars, route, type, speedFactor),
56 742794 : mySegment(nullptr),
57 742794 : myQueIndex(0),
58 742794 : myEventTime(SUMOTime_MIN),
59 742794 : myLastEntryTime(SUMOTime_MIN),
60 742794 : myBlockTime(SUMOTime_MAX),
61 742794 : myInfluencer(nullptr) {
62 742794 : }
63 :
64 :
65 : double
66 0 : MEVehicle::getBackPositionOnLane(const MSLane* /* lane */) const {
67 0 : return getPositionOnLane() - getVehicleType().getLength();
68 : }
69 :
70 :
71 : double
72 6887496 : 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 6887496 : return mySegment == nullptr ? 0 : (double(mySegment->getIndex()) /* + fracOnSegment */) * mySegment->getLength();
76 : }
77 :
78 :
79 : double
80 52773 : MEVehicle::getAngle() const {
81 52773 : const MSLane* const lane = getEdge()->getLanes()[0];
82 52773 : return lane->getShape().rotationAtOffset(lane->interpolateLanePosToGeometryPos(getPositionOnLane()));
83 : }
84 :
85 :
86 : double
87 160694 : MEVehicle::getSlope() const {
88 160694 : const MSLane* const lane = getEdge()->getLanes()[0];
89 160694 : return lane->getShape().slopeDegreeAtOffset(lane->interpolateLanePosToGeometryPos(getPositionOnLane()));
90 : }
91 :
92 :
93 : Position
94 100586 : MEVehicle::getPosition(const double offset) const {
95 100586 : const MSLane* const lane = getEdge()->getLanes()[0];
96 100586 : return lane->geometryPositionAtOffset(getPositionOnLane() + offset);
97 : }
98 :
99 : PositionVector
100 0 : MEVehicle::getBoundingBox(double offset) const {
101 0 : double a = getAngle() + M_PI; // angle pointing backwards
102 0 : double l = getLength();
103 0 : Position pos = getPosition();
104 0 : Position backPos = pos + Position(l * cos(a), l * sin(a));
105 0 : PositionVector centerLine;
106 0 : centerLine.push_back(pos);
107 0 : centerLine.push_back(backPos);
108 0 : if (offset != 0) {
109 0 : centerLine.extrapolate2D(offset);
110 : }
111 : PositionVector result = centerLine;
112 0 : result.move2side(MAX2(0.0, 0.5 * myType->getWidth() + offset));
113 0 : centerLine.move2side(MIN2(0.0, -0.5 * myType->getWidth() - offset));
114 0 : result.append(centerLine.reverse(), POSITION_EPS);
115 0 : return result;
116 0 : }
117 :
118 : double
119 45162669 : MEVehicle::getSpeed() const {
120 45162669 : if (getWaitingTime() > 0 || isStopped()) {
121 17550347 : return 0;
122 : } else {
123 27612322 : return getAverageSpeed();
124 : }
125 : }
126 :
127 :
128 : double
129 27612322 : MEVehicle::getAverageSpeed() const {
130 : // cache for thread safety
131 27612322 : MESegment* s = mySegment;
132 27612322 : if (s == nullptr || myQueIndex == MESegment::PARKING_QUEUE) {
133 : return 0;
134 : } else {
135 27606906 : return MIN2(s->getLength() / STEPS2TIME(myEventTime - myLastEntryTime),
136 27606906 : getEdge()->getLanes()[myQueIndex]->getVehicleMaxSpeed(this));
137 : }
138 : }
139 :
140 :
141 : double
142 4130838 : MEVehicle::estimateLeaveSpeed(const MSLink* link) const {
143 : /// @see MSVehicle.cpp::estimateLeaveSpeed
144 4130838 : const double v = getSpeed();
145 4130838 : return MIN2(link->getViaLaneOrLane()->getVehicleMaxSpeed(this),
146 4130838 : (double)sqrt(2 * link->getLength() * getVehicleType().getCarFollowModel().getMaxAccel() + v * v));
147 : }
148 :
149 :
150 : double
151 106305268 : MEVehicle::getConservativeSpeed(SUMOTime& earliestArrival) const {
152 106305268 : earliestArrival = MAX2(myEventTime, earliestArrival - DELTA_T); // event times have subsecond resolution
153 106305268 : return mySegment->getLength() / STEPS2TIME(earliestArrival - myLastEntryTime);
154 : }
155 :
156 :
157 : bool
158 2869425 : MEVehicle::moveRoutePointer() {
159 : // vehicle has just entered a new edge. Position is 0
160 2869425 : if (myCurrEdge == myRoute->end() - 1 || (myParameter->arrivalEdge >= 0 && getRoutePosition() >= myParameter->arrivalEdge)) { // may happen during teleport
161 113 : return true;
162 : }
163 : ++myCurrEdge;
164 2869312 : if ((*myCurrEdge)->isVaporizing()) {
165 : return true;
166 : }
167 : // update via
168 2869208 : if (myParameter->via.size() > 0 && (*myCurrEdge)->getID() == myParameter->via.front()) {
169 2908 : myParameter->via.erase(myParameter->via.begin());
170 : }
171 2869208 : return hasArrived();
172 : }
173 :
174 :
175 : bool
176 26260786 : MEVehicle::hasArrived() const {
177 : // mySegment may be 0 due to teleporting or arrival
178 26260786 : return (myCurrEdge == myRoute->end() - 1 || (myParameter->arrivalEdge >= 0 && getRoutePosition() >= myParameter->arrivalEdge)) && (
179 4803591 : (mySegment == nullptr)
180 4802047 : || myEventTime == SUMOTime_MIN
181 4802047 : || getPositionOnLane() > myArrivalPos - POSITION_EPS);
182 : }
183 :
184 :
185 : bool
186 477663460 : MEVehicle::isOnRoad() const {
187 477663460 : return getSegment() != nullptr;
188 : }
189 :
190 :
191 : bool
192 22360 : MEVehicle::isIdling() const {
193 22360 : return false;
194 : }
195 :
196 :
197 : void
198 29699013 : MEVehicle::setApproaching(MSLink* link) {
199 29699013 : if (link != nullptr) {
200 5880034 : const double speed = getSpeed();
201 5932960 : link->setApproaching(this, getEventTime() + (link->getState() == LINKSTATE_ALLWAY_STOP ?
202 : (SUMOTime)RandHelper::rand((int)2) : 0), // tie braker
203 : speed, link->getViaLaneOrLane()->getVehicleMaxSpeed(this), true,
204 5880034 : speed, getWaitingTime(),
205 : // @note: dist is not used by meso (getZipperSpeed is never called)
206 : getSegment()->getLength(), 0);
207 : }
208 29699013 : }
209 :
210 :
211 : bool
212 325228 : MEVehicle::replaceRoute(ConstMSRoutePtr newRoute, const std::string& info, bool onInit, int offset, bool addRouteStops, bool removeStops, std::string* msgReturn) {
213 325228 : MSLink* const oldLink = mySegment != nullptr ? mySegment->getLink(this) : nullptr;
214 650455 : if (MSBaseVehicle::replaceRoute(newRoute, info, onInit, offset, addRouteStops, removeStops, msgReturn)) {
215 325227 : if (mySegment != nullptr) {
216 138096 : MSLink* const newLink = mySegment->getLink(this);
217 : // update approaching vehicle information
218 138096 : if (oldLink != newLink) {
219 355 : if (oldLink != nullptr) {
220 305 : oldLink->removeApproaching(this);
221 : }
222 355 : setApproaching(newLink);
223 : }
224 : }
225 325227 : return true;
226 : }
227 : return false;
228 : }
229 :
230 :
231 : SUMOTime
232 24066365 : MEVehicle::checkStop(SUMOTime time) {
233 : const SUMOTime initialTime = time;
234 : bool hadStop = false;
235 24081103 : for (MSStop& stop : myStops) {
236 117080 : if (stop.joinTriggered) {
237 1155 : WRITE_WARNINGF(TL("Join stops are not available in meso yet (vehicle '%', segment '%')."),
238 : getID(), mySegment->getID());
239 385 : continue;
240 : }
241 116695 : if (stop.edge != myCurrEdge || stop.segment != mySegment) {
242 : break;
243 : }
244 : const SUMOTime cur = time;
245 14353 : if (stop.duration > 0) { // it might be a triggered stop with duration -1
246 6659 : time += stop.duration;
247 : }
248 14353 : 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 14353 : if (MSGlobals::gUseStopEnded && stop.pars.ended >= 0) {
254 : time = MAX2(cur, stop.pars.ended);
255 : }
256 14353 : if (!stop.reached) {
257 14353 : stop.reached = true;
258 14353 : stop.pars.started = myLastEntryTime;
259 14353 : stop.endBoarding = stop.pars.extension >= 0 ? time + stop.pars.extension : SUMOTime_MAX;
260 14353 : if (MSStopOut::active()) {
261 956 : if (!hadStop) {
262 916 : MSStopOut::getInstance()->stopStarted(this, getPersonNumber(), getContainerNumber(), myLastEntryTime);
263 : } else {
264 120 : WRITE_WARNINGF(TL("Vehicle '%' has multiple stops on segment '%', time=% (stop-output will be merged)."),
265 : getID(), mySegment->getID(), time2string(time));
266 : }
267 : }
268 14353 : MSDevice_Taxi* taxi = static_cast<MSDevice_Taxi*>(getDevice(typeid(MSDevice_Taxi)));
269 : if (taxi != nullptr) {
270 1148 : taxi->notifyMove(*this, 0, 0, 0);
271 : }
272 : }
273 14353 : if (stop.triggered || stop.containerTriggered || stop.joinTriggered) {
274 771 : time = MAX2(time, cur + DELTA_T);
275 : }
276 : hadStop = true;
277 : }
278 24066365 : MSDevice_Tripinfo* tripinfo = static_cast<MSDevice_Tripinfo*>(getDevice(typeid(MSDevice_Tripinfo)));
279 : if (tripinfo != nullptr) {
280 13280588 : tripinfo->updateStopTime(time - initialTime);
281 : }
282 24066365 : return time;
283 : }
284 :
285 :
286 : bool
287 13615 : MEVehicle::resumeFromStopping() {
288 13615 : if (isStopped()) {
289 13609 : const SUMOTime now = SIMSTEP;
290 : MSStop& stop = myStops.front();
291 13609 : stop.pars.ended = now;
292 26755 : for (const auto& rem : myMoveReminders) {
293 13146 : rem.first->notifyStopEnded();
294 : }
295 13609 : if (MSStopOut::active()) {
296 907 : MSStopOut::getInstance()->stopEnded(this, stop.pars, mySegment->getEdge().getID());
297 : }
298 13609 : myPastStops.push_back(stop.pars);
299 13609 : myPastStops.back().routeIndex = (int)(stop.edge - myRoute->begin());
300 13609 : if (myAmRegisteredAsWaiting && (stop.triggered || stop.containerTriggered || stop.joinTriggered)) {
301 30 : MSNet::getInstance()->getVehicleControl().unregisterOneWaiting();
302 30 : myAmRegisteredAsWaiting = false;
303 : }
304 13609 : myStops.pop_front();
305 13609 : if (myEventTime > now) {
306 : // if this is an aborted stop we need to change the event time of the vehicle
307 6 : if (MSGlobals::gMesoNet->removeLeaderCar(this)) {
308 6 : myEventTime = now + 1;
309 6 : MSGlobals::gMesoNet->addLeaderCar(this, nullptr);
310 : }
311 : }
312 13609 : return true;
313 : }
314 : return false;
315 : }
316 :
317 :
318 : double
319 0 : MEVehicle::getCurrentStoppingTimeSeconds() const {
320 0 : SUMOTime time = myLastEntryTime;
321 0 : for (const MSStop& stop : myStops) {
322 0 : if (stop.reached) {
323 0 : time += stop.duration;
324 0 : 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 0 : return STEPS2TIME(time - myLastEntryTime);
334 : }
335 :
336 :
337 : void
338 12921 : MEVehicle::processStop() {
339 : assert(isStopped());
340 : double lastPos = -1;
341 : bool hadStop = false;
342 26529 : while (!myStops.empty()) {
343 : MSStop& stop = myStops.front();
344 21192 : if (stop.edge != myCurrEdge || stop.segment != mySegment || stop.pars.endPos <= lastPos) {
345 : break;
346 : }
347 : lastPos = stop.pars.endPos;
348 13608 : MSNet* const net = MSNet::getInstance();
349 13608 : SUMOTime dummy = -1; // boarding- and loading-time are not considered
350 13608 : if (hadStop && MSStopOut::active()) {
351 40 : stop.reached = true;
352 40 : MSStopOut::getInstance()->stopStarted(this, getPersonNumber(), getContainerNumber(), myLastEntryTime);
353 : }
354 13608 : if (net->hasPersons()) {
355 5166 : net->getPersonControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy);
356 : }
357 13608 : if (net->hasContainers()) {
358 244 : net->getContainerControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy);
359 : }
360 13608 : resumeFromStopping();
361 : hadStop = true;
362 : }
363 12921 : mySegment->getEdge().removeWaiting(this);
364 12921 : }
365 :
366 :
367 : bool
368 27440038 : MEVehicle::mayProceed() {
369 27440038 : if (mySegment == nullptr) {
370 : return true;
371 : }
372 27440038 : MSNet* const net = MSNet::getInstance();
373 27440038 : SUMOTime dummy = -1; // boarding- and loading-time are not considered
374 30650617 : for (MSStop& stop : myStops) {
375 6701629 : if (!stop.reached) {
376 : break;
377 : }
378 3397885 : if (net->getCurrentTimeStep() > stop.endBoarding) {
379 63 : if (stop.triggered || stop.containerTriggered) {
380 27 : MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(getDevice(typeid(MSDevice_Taxi)));
381 : if (taxiDevice != nullptr) {
382 24 : taxiDevice->cancelCurrentCustomers();
383 : }
384 27 : stop.triggered = false;
385 27 : stop.containerTriggered = false;
386 : }
387 63 : if (myAmRegisteredAsWaiting) {
388 : net->getVehicleControl().unregisterOneWaiting();
389 27 : myAmRegisteredAsWaiting = false;
390 : }
391 : }
392 3397885 : if (stop.triggered) {
393 171632 : 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 0 : WRITE_WARNINGF(TL("Vehicle '%' ignores triggered stop on lane '%' due to capacity constraints."), getID(), stop.lane->getID());
396 0 : stop.triggered = false;
397 0 : if (myAmRegisteredAsWaiting) {
398 : net->getVehicleControl().unregisterOneWaiting();
399 0 : myAmRegisteredAsWaiting = false;
400 : }
401 171632 : } else if (!net->hasPersons() || !net->getPersonControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy)) {
402 171182 : if (!myAmRegisteredAsWaiting) {
403 389 : MSNet::getInstance()->getVehicleControl().registerOneWaiting();
404 389 : myAmRegisteredAsWaiting = true;
405 : }
406 171182 : return false;
407 : }
408 : }
409 3226703 : if (stop.containerTriggered) {
410 16198 : 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 6 : WRITE_WARNINGF(TL("Vehicle '%' ignores container triggered stop on lane '%' due to capacity constraints."), getID(), stop.lane->getID());
413 2 : stop.containerTriggered = false;
414 2 : if (myAmRegisteredAsWaiting) {
415 : net->getVehicleControl().unregisterOneWaiting();
416 0 : myAmRegisteredAsWaiting = false;
417 : }
418 16196 : } else if (!net->hasContainers() || !net->getContainerControl().loadAnyWaiting(&mySegment->getEdge(), this, dummy, dummy)) {
419 16124 : if (!myAmRegisteredAsWaiting) {
420 44 : MSNet::getInstance()->getVehicleControl().registerOneWaiting();
421 44 : myAmRegisteredAsWaiting = true;
422 : }
423 16124 : return false;
424 : }
425 : }
426 3210579 : if (stop.joinTriggered) {
427 : // TODO do something useful here
428 : return false;
429 : }
430 : }
431 27252732 : return mySegment->isOpen(this);
432 : }
433 :
434 :
435 : double
436 0 : MEVehicle::getCurrentLinkPenaltySeconds() const {
437 0 : if (mySegment == nullptr) {
438 : return 0;
439 : } else {
440 0 : return STEPS2TIME(mySegment->getLinkPenalty(this));
441 : }
442 : }
443 :
444 :
445 : void
446 1299882 : MEVehicle::updateDetectorForWriting(MSMoveReminder* rem, SUMOTime currentTime, SUMOTime exitTime) {
447 2183737 : for (MoveReminderCont::iterator i = myMoveReminders.begin(); i != myMoveReminders.end(); ++i) {
448 2172423 : if (i->first == rem) {
449 1288568 : rem->updateDetector(*this, mySegment->getIndex() * mySegment->getLength(),
450 1288568 : (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 24058365 : MEVehicle::updateDetectors(const SUMOTime currentTime, const SUMOTime exitTime, 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 24058365 : const bool cleanUp = isLeave && (reason != MSMoveReminder::NOTIFICATION_SEGMENT);
467 54627286 : for (MoveReminderCont::iterator rem = myMoveReminders.begin(); rem != myMoveReminders.end();) {
468 30568921 : if (currentTime != getLastEntryTime() && reason < MSMoveReminder::NOTIFICATION_VAPORIZED_CALIBRATOR) {
469 30513063 : rem->first->updateDetector(*this, mySegment->getIndex() * mySegment->getLength(),
470 30513063 : (mySegment->getIndex() + 1) * mySegment->getLength(),
471 : getLastEntryTime(), currentTime, exitTime, cleanUp);
472 : #ifdef _DEBUG
473 : if (myTraceMoveReminders) {
474 : traceMoveReminder("notifyMove", rem->first, rem->second, true);
475 : }
476 : #endif
477 : }
478 30568921 : 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 21334326 : if (isLeave) {
486 21332036 : 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 24058365 : if (reason == MSMoveReminder::NOTIFICATION_JUNCTION || reason == MSMoveReminder::NOTIFICATION_TELEPORT) {
504 2867174 : myOdometer += getEdge()->getLength();
505 : }
506 24058365 : }
507 :
508 :
509 : MEVehicle::BaseInfluencer&
510 3 : MEVehicle::getBaseInfluencer() {
511 3 : if (myInfluencer == nullptr) {
512 2 : myInfluencer = new BaseInfluencer();
513 : }
514 3 : return *myInfluencer;
515 : }
516 :
517 :
518 : const MEVehicle::BaseInfluencer*
519 46 : MEVehicle::getBaseInfluencer() const {
520 46 : return myInfluencer;
521 : }
522 :
523 :
524 : void
525 2 : MEVehicle::onRemovalFromNet(const MSMoveReminder::Notification reason) {
526 2 : MSGlobals::gMesoNet->removeLeaderCar(this);
527 2 : MSGlobals::gMesoNet->changeSegment(this, MSNet::getInstance()->getCurrentTimeStep(), nullptr, reason);
528 2 : }
529 :
530 :
531 : int
532 5732 : MEVehicle::getSegmentIndex() const {
533 5732 : return getSegment() != nullptr ? getSegment()->getIndex() : -1;
534 : }
535 :
536 :
537 : double
538 0 : MEVehicle::getRightSideOnEdge(const MSLane* /*lane*/) const {
539 0 : if (mySegment == nullptr || mySegment->getIndex() >= getEdge()->getNumLanes()) {
540 0 : return 0;
541 : }
542 0 : const MSLane* lane = getEdge()->getLanes()[mySegment->getIndex()];
543 0 : return lane->getRightSideOnEdge() + lane->getWidth() * 0.5 - 0.5 * getVehicleType().getWidth();
544 :
545 : }
546 :
547 :
548 : void
549 1782 : MEVehicle::saveState(OutputDevice& out) {
550 1782 : if (mySegment != nullptr && MESegment::isInvalid(mySegment)) {
551 : // segment is vaporization target, do not write this vehicle
552 0 : return;
553 : }
554 1782 : MSBaseVehicle::saveState(out);
555 : assert(mySegment == nullptr || *myCurrEdge == &mySegment->getEdge());
556 : std::vector<SUMOTime> internals;
557 1782 : internals.push_back(myParameter->parametersSet);
558 1782 : internals.push_back(myDeparture);
559 1782 : internals.push_back((SUMOTime)distance(myRoute->begin(), myCurrEdge));
560 1782 : internals.push_back((SUMOTime)myDepartPos * 1000); // store as mm
561 1782 : internals.push_back(mySegment == nullptr ? (SUMOTime) - 1 : (SUMOTime)mySegment->getIndex());
562 1782 : internals.push_back((SUMOTime)getQueIndex());
563 1782 : internals.push_back(myEventTime);
564 1782 : internals.push_back(myLastEntryTime);
565 1782 : internals.push_back(myBlockTime);
566 3564 : out.writeAttr(SUMO_ATTR_STATE, toString(internals));
567 : // save past stops
568 4827 : for (SUMOVehicleParameter::Stop stop : myPastStops) {
569 3045 : stop.write(out, false);
570 : // do not write started and ended twice
571 3045 : if ((stop.parametersSet & STOP_STARTED_SET) == 0) {
572 6090 : out.writeAttr(SUMO_ATTR_STARTED, time2string(stop.started));
573 : }
574 3045 : if ((stop.parametersSet & STOP_ENDED_SET) == 0) {
575 6090 : out.writeAttr(SUMO_ATTR_ENDED, time2string(stop.ended));
576 : }
577 3045 : out.closeTag();
578 3045 : }
579 : // save upcoming stops
580 2144 : for (const MSStop& stop : myStops) {
581 362 : stop.write(out);
582 : }
583 : // save parameters
584 1782 : myParameter->writeParams(out);
585 4579 : for (MSDevice* dev : myDevices) {
586 2797 : dev->saveState(out);
587 : }
588 4176 : for (const auto& item : myMoveReminders) {
589 2394 : item.first->saveReminderState(out, *this);
590 : }
591 1782 : out.closeTag();
592 1782 : }
593 :
594 :
595 : void
596 1382 : MEVehicle::loadState(const SUMOSAXAttributes& attrs, const SUMOTime offset) {
597 1382 : if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
598 0 : throw ProcessError(TL("Error: Invalid vehicles in state (may be a micro state)!"));
599 : }
600 : int routeOffset;
601 : int segIndex;
602 : int queIndex;
603 1382 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
604 1382 : bis >> myParameter->parametersSet;
605 1382 : bis >> myDeparture;
606 1382 : bis >> routeOffset;
607 1382 : bis >> myDepartPos;
608 1382 : bis >> segIndex;
609 1382 : bis >> queIndex;
610 1382 : bis >> myEventTime;
611 1382 : bis >> myLastEntryTime;
612 1382 : bis >> myBlockTime;
613 1382 : myDepartPos /= 1000.; // was stored as mm
614 :
615 1382 : if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS_RANDOMIZED)) {
616 : bool ok;
617 9 : myArrivalPos = attrs.get<double>(SUMO_ATTR_ARRIVALPOS_RANDOMIZED, getID().c_str(), ok);
618 : }
619 :
620 : // load stops
621 : myStops.clear();
622 1382 : addStops(!MSGlobals::gCheckRoutes, &myCurrEdge, false);
623 :
624 1382 : if (hasDeparted()) {
625 707 : myDeparture -= offset;
626 707 : myEventTime -= offset;
627 707 : myLastEntryTime -= offset;
628 707 : myCurrEdge = myRoute->begin() + routeOffset;
629 707 : if (segIndex >= 0) {
630 707 : MESegment* seg = MSGlobals::gMesoNet->getSegmentForEdge(**myCurrEdge);
631 972 : while (seg->getIndex() != (int)segIndex) {
632 : seg = seg->getNextSegment();
633 266 : if (seg == nullptr) {
634 4 : throw ProcessError(TLF("Unknown segment '%:%' for vehicle '%' in loaded state.", (*myCurrEdge)->getID(), segIndex, getID()));
635 : }
636 : }
637 706 : setSegment(seg, queIndex);
638 706 : if (queIndex == MESegment::PARKING_QUEUE) {
639 260 : MSGlobals::gMesoNet->addLeaderCar(this, nullptr);
640 : }
641 : } else {
642 : // on teleport
643 0 : setSegment(nullptr, 0);
644 : assert(myEventTime != SUMOTime_MIN);
645 0 : MSGlobals::gMesoNet->addLeaderCar(this, nullptr);
646 : }
647 : // see MSBaseVehicle constructor
648 706 : if (myParameter->wasSet(VEHPARS_FORCE_REROUTE)) {
649 396 : calculateArrivalParams(true);
650 : }
651 : }
652 1381 : if (myBlockTime != SUMOTime_MAX) {
653 31 : myBlockTime -= offset;
654 : }
655 1381 : std::istringstream dis(attrs.getString(SUMO_ATTR_DISTANCE));
656 1381 : dis >> myOdometer >> myNumberReroutes;
657 1382 : }
658 :
659 :
660 : /****************************************************************************/
|