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