SUMO - Simulation of Urban MObility
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MSRouteHandler.cpp
Go to the documentation of this file.
1 /****************************************************************************/
10 // Parser and container for routes during their loading
11 /****************************************************************************/
12 // SUMO, Simulation of Urban MObility; see http://sumo-sim.org/
13 // Copyright (C) 2001-2013 DLR (http://www.dlr.de/) and contributors
14 /****************************************************************************/
15 //
16 // This file is part of SUMO.
17 // SUMO is free software: you can redistribute it and/or modify
18 // it under the terms of the GNU General Public License as published by
19 // the Free Software Foundation, either version 3 of the License, or
20 // (at your option) any later version.
21 //
22 /****************************************************************************/
23 
24 
25 // ===========================================================================
26 // included modules
27 // ===========================================================================
28 #ifdef _MSC_VER
29 #include <windows_config.h>
30 #else
31 #include <config.h>
32 #endif
33 
34 #include <string>
35 #include <map>
36 #include <vector>
37 #include <microsim/MSRoute.h>
38 #include <microsim/MSEdge.h>
39 #include <microsim/MSVehicleType.h>
40 #include <microsim/MSVehicle.h>
43 #include <microsim/MSLane.h>
44 #include "MSRouteHandler.h"
45 #include "MSPersonControl.h"
53 #include "MSNet.h"
54 
56 #include <microsim/MSGlobals.h>
58 
59 #ifdef CHECK_MEMORY_LEAKS
60 #include <foreign/nvwa/debug_new.h>
61 #endif // CHECK_MEMORY_LEAKS
62 
63 
64 // ===========================================================================
65 // method definitions
66 // ===========================================================================
67 MSRouteHandler::MSRouteHandler(const std::string& file,
68  bool addVehiclesDirectly) :
69  SUMORouteHandler(file),
70  myActivePlan(0),
71  myAddVehiclesDirectly(addVehiclesDirectly),
72  myCurrentVTypeDistribution(0),
73  myCurrentRouteDistribution(0) {
74  myActiveRoute.reserve(100);
75 }
76 
77 
79 }
80 
81 
82 void
84  const SUMOSAXAttributes& attrs) {
85  SUMORouteHandler::myStartElement(element, attrs);
86  switch (element) {
87  case SUMO_TAG_PERSON:
89  break;
90  case SUMO_TAG_RIDE: {
91  const std::string pid = myVehicleParameter->id;
92  bool ok = true;
93  MSEdge* from = 0;
94  const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
95  StringTokenizer st(desc);
96  std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, 0, ok, "");
97  MSBusStop* bs = 0;
98  if (bsID != "") {
99  bs = MSNet::getInstance()->getBusStop(bsID);
100  if (bs == 0) {
101  throw ProcessError("Unknown bus stop '" + bsID + "' for person '" + myVehicleParameter->id + "'.");
102  }
103  }
104  if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
105  const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, pid.c_str(), ok);
106  from = MSEdge::dictionary(fromID);
107  if (from == 0) {
108  throw ProcessError("The from edge '" + fromID + "' within a ride of person '" + pid + "' is not known.");
109  }
110  if (!myActivePlan->empty() && &myActivePlan->back()->getDestination() != from) {
111  throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + fromID + "!=" + myActivePlan->back()->getDestination().getID() + ").");
112  }
113  if (myActivePlan->empty()) {
115  *from, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start"));
116  }
117  } else if (myActivePlan->empty()) {
118  throw ProcessError("The start edge within for person '" + pid + "' is not known.");
119  }
120  const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, pid.c_str(), ok);
121  MSEdge* to = MSEdge::dictionary(toID);
122  if (to == 0) {
123  throw ProcessError("The to edge '" + toID + "' within a ride of person '" + pid + "' is not known.");
124  }
125  myActivePlan->push_back(new MSPerson::MSPersonStage_Driving(*to, bs, st.getVector()));
126  break;
127  }
128  case SUMO_TAG_WALK: {
129  myActiveRoute.clear();
130  bool ok = true;
131  if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
133  } else {
134  if (attrs.hasAttribute(SUMO_ATTR_FROM) && attrs.hasAttribute(SUMO_ATTR_TO)) {
135  const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok);
136  MSEdge* from = MSEdge::dictionary(fromID);
137  if (from == 0) {
138  throw ProcessError("The from edge '" + fromID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
139  }
140  const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok);
141  MSEdge* to = MSEdge::dictionary(toID);
142  if (to == 0) {
143  throw ProcessError("The to edge '" + toID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
144  }
145  MSNet::getInstance()->getRouterTT().compute(from, to, 0, 0, myActiveRoute); // @todo: only footways, current time?
146  }
147  }
148  if (myActiveRoute.empty()) {
149  throw ProcessError("No edges to walk for person '" + myVehicleParameter->id + "'.");
150  }
151  if (!myActivePlan->empty() && &myActivePlan->back()->getDestination() != myActiveRoute.front()) {
152  throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + "!=" + myActivePlan->back()->getDestination().getID() + ").");
153  }
154  SUMOReal departPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_DEPARTPOS, myVehicleParameter->id.c_str(), ok, 0);
155  SUMOReal arrivalPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok, -1);
156  const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, 0, ok, -1);
158  if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
159  speed = attrs.getOpt<SUMOReal>(SUMO_ATTR_SPEED, 0, ok, speed);
160  if (speed < 0) {
161  throw ProcessError("Negative walking speed for '" + myVehicleParameter->id + "'.");
162  }
163  }
164  std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, 0, ok, "");
165  MSBusStop* bs = 0;
166  if (bsID != "") {
167  bs = MSNet::getInstance()->getBusStop(bsID);
168  if (bs == 0) {
169  throw ProcessError("Unknown bus stop '" + bsID + "' for person '" + myVehicleParameter->id + "'.");
170  }
171  }
172  if (myActivePlan->empty()) {
174  *myActiveRoute.front(), -1, myVehicleParameter->depart, departPos, "start"));
175  }
176  myActivePlan->push_back(new MSPerson::MSPersonStage_Walking(myActiveRoute, bs, duration, speed, departPos, arrivalPos));
177  myActiveRoute.clear();
178  break;
179  }
180  case SUMO_TAG_FLOW:
181  if (attrs.hasAttribute(SUMO_ATTR_FROM) && attrs.hasAttribute(SUMO_ATTR_TO)) {
183  bool ok = true;
184  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
185  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
186  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok),
187  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
188  closeRoute(true);
189  }
190  break;
191  case SUMO_TAG_TRIP: {
192  bool ok = true;
194  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
195  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
196  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok),
197  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
198  } else {
199  const MSEdge* fromTaz = MSEdge::dictionary(myVehicleParameter->fromTaz + "-source");
200  if (fromTaz == 0) {
201  WRITE_ERROR("Source district '" + myVehicleParameter->fromTaz + "' not known for '" + myVehicleParameter->id + "'!");
202  } else if (fromTaz->getNoFollowing() == 0) {
203  WRITE_ERROR("Source district '" + myVehicleParameter->fromTaz + "' has no outgoing edges for '" + myVehicleParameter->id + "'!");
204  } else {
205  myActiveRoute.push_back(fromTaz->getFollower(0));
206  }
207  }
208  closeRoute(true);
209  closeVehicle();
210  }
211  break;
212  default:
213  break;
214  }
215  // parse embedded vtype information
216  if (myCurrentVType != 0 && element != SUMO_TAG_VTYPE && element != SUMO_TAG_PARAM) {
218  return;
219  }
220 }
221 
222 
223 void
225  bool ok = true;
226  myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
227  if (ok) {
229  if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
230  const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
231  StringTokenizer st(vTypes);
232  while (st.hasNext()) {
233  std::string vtypeID = st.next();
235  if (type == 0) {
236  throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
237  }
239  }
240  }
241  }
242 }
243 
244 
245 void
247  if (myCurrentVTypeDistribution != 0) {
250  WRITE_ERROR("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
251  } else if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
253  WRITE_ERROR("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
254  }
256  }
257 }
258 
259 
260 void
262  // check whether the id is really necessary
263  std::string rid;
264  if (myCurrentRouteDistribution != 0) {
266  rid = "distribution '" + myCurrentRouteDistributionID + "'";
267  } else if (myVehicleParameter != 0) {
268  // ok, a vehicle is wrapping the route,
269  // we may use this vehicle's id as default
270  myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
271  if (attrs.hasAttribute(SUMO_ATTR_ID)) {
272  WRITE_WARNING("Ids of internal routes are ignored (vehicle '" + myVehicleParameter->id + "').");
273  }
274  } else {
275  bool ok = true;
276  myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok, false);
277  if (!ok) {
278  return;
279  }
280  rid = "'" + myActiveRouteID + "'";
281  }
282  if (myVehicleParameter != 0) { // have to do this here for nested route distributions
283  rid = "for vehicle '" + myVehicleParameter->id + "'";
284  }
285  bool ok = true;
286  if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
287  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
288  }
289  myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
291  WRITE_ERROR("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
292  }
295 }
296 
297 
298 void
301  switch (element) {
302  case SUMO_TAG_VTYPE: {
304  delete myCurrentVType;
305  myCurrentVType = 0;
306  if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
307  const std::string id = vehType->getID();
308  delete vehType;
310  throw ProcessError("Another vehicle type (or distribution) with the id '" + id + "' exists.");
311  }
312  } else {
313  if (myCurrentVTypeDistribution != 0) {
315  }
316  }
317  }
318  break;
319  default:
320  break;
321  }
322 }
323 
324 
325 void
326 MSRouteHandler::closeRoute(const bool /* mayBeDisconnected */) {
327  if (myActiveRoute.size() == 0) {
328  delete myActiveRouteColor;
329  myActiveRouteColor = 0;
332  myActiveRouteID = "";
333  myActiveRouteRefID = "";
334  return;
335  }
336  if (myVehicleParameter != 0) {
337  throw ProcessError("Vehicle's '" + myVehicleParameter->id + "' route has no edges.");
338  } else {
339  throw ProcessError("Route '" + myActiveRouteID + "' has no edges.");
340  }
341  }
345  myActiveRoute.clear();
346  if (!MSRoute::dictionary(myActiveRouteID, route)) {
347  delete route;
349  if (myVehicleParameter != 0) {
350  if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == 0) {
351  throw ProcessError("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
352  } else {
353  throw ProcessError("A vehicle with id '" + myVehicleParameter->id + "' already exists.");
354  }
355  } else {
356  throw ProcessError("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
357  }
358  }
359  } else {
360  if (myCurrentRouteDistribution != 0) {
362  }
363  }
364  myActiveRouteID = "";
365  myActiveRouteColor = 0;
366  myActiveRouteStops.clear();
367 }
368 
369 
370 void
372  // check whether the id is really necessary
373  bool ok = true;
374  if (myVehicleParameter != 0) {
375  // ok, a vehicle is wrapping the route,
376  // we may use this vehicle's id as default
377  myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
378  } else {
379  myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
380  if (!ok) {
381  return;
382  }
383  }
385  std::vector<SUMOReal> probs;
386  if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
387  bool ok = true;
388  StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
389  while (st.hasNext()) {
390  probs.push_back(TplConvert::_2SUMORealSec(st.next().c_str(), 1.0));
391  }
392  }
393  if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
394  bool ok = true;
395  StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
396  size_t probIndex = 0;
397  while (st.hasNext()) {
398  std::string routeID = st.next();
399  const MSRoute* route = MSRoute::dictionary(routeID);
400  if (route == 0) {
401  throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
402  }
403  const SUMOReal prob = (probs.size() > probIndex ? probs[probIndex] : 1.0);
404  myCurrentRouteDistribution->add(prob, route, false);
405  probIndex++;
406  }
407  if (probs.size() > 0 && probIndex != probs.size()) {
408  WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
409  " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
410  }
411  }
412 }
413 
414 
415 void
417  if (myCurrentRouteDistribution != 0) {
420  WRITE_ERROR("Route distribution '" + myCurrentRouteDistributionID + "' is empty.");
423  WRITE_ERROR("Another route (or distribution) with the id '" + myCurrentRouteDistributionID + "' exists.");
424  }
426  }
427 }
428 
429 
430 void
432  // get nested route
433  const MSRoute* route = MSRoute::dictionary("!" + myVehicleParameter->id);
436  // let's check whether this vehicle had to depart before the simulation starts
438  if (route != 0) {
439  route->addReference();
440  route->release();
441  }
442  return;
443  }
444  }
445  // get the vehicle's type
446  MSVehicleType* vtype = 0;
447  if (myVehicleParameter->vtypeid != "") {
448  vtype = vehControl.getVType(myVehicleParameter->vtypeid);
449  if (vtype == 0) {
450  throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
451  }
452  } else {
453  // there should be one (at least the default one)
454  vtype = vehControl.getVType();
455  }
456  if (route == 0) {
457  // if there is no nested route, try via the (hopefully) given route-id
459  }
460  if (route == 0) {
461  // nothing found? -> error
462  if (myVehicleParameter->routeid != "") {
463  throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
464  } else {
465  throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has no route.");
466  }
467  }
468  myActiveRouteID = "";
469 
470  // try to build the vehicle
471  SUMOVehicle* vehicle = 0;
472  if (vehControl.getVehicle(myVehicleParameter->id) == 0) {
473  vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype);
474  // maybe we do not want this vehicle to be inserted due to scaling
475  if (vehControl.isInQuota()) {
476  // add the vehicle to the vehicle control
477  vehControl.addVehicle(myVehicleParameter->id, vehicle);
479  vehControl.addWaiting(*route->begin(), vehicle);
480  vehControl.registerOneWaitingForPerson();
481  }
483  myVehicleParameter = 0;
484  } else {
485  vehControl.deleteVehicle(vehicle, true);
486  myVehicleParameter = 0;
487  vehicle = 0;
488  }
489  } else {
490  // strange: another vehicle with the same id already exists
492  // and was not loaded while loading a simulation state
493  // -> error
494  throw ProcessError("Another vehicle with the id '" + myVehicleParameter->id + "' exists.");
495  } else {
496  // ok, it seems to be loaded previously while loading a simulation state
497  vehicle = 0;
498  }
499  }
500  // check whether the vehicle shall be added directly to the network or
501  // shall stay in the internal buffer
502  if (vehicle != 0) {
503  if (vehicle->getParameter().departProcedure == DEPART_GIVEN) {
505  }
506  }
507 }
508 
509 
510 void
512  if (myActivePlan->size() == 0) {
513  throw ProcessError("Person '" + myVehicleParameter->id + "' has no plan.");
514  }
516  if (type == 0) {
517  throw ProcessError("The type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
518  }
520  // @todo: consider myScale?
521  if ((myAddVehiclesDirectly || checkLastDepart()) && MSNet::getInstance()->getPersonControl().add(myVehicleParameter->id, person)) {
524  } else {
525  delete person;
526  }
527  myVehicleParameter = 0;
528  myActivePlan = 0;
529 }
530 
531 
532 void
534  // let's check whether vehicles had to depart before the simulation starts
536  SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
540  return;
541  }
542  }
543  if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid) == 0) {
544  throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
545  }
546  if (MSRoute::dictionary("!" + myVehicleParameter->id) == 0) {
547  // if not, try via the (hopefully) given route-id
549  if (myVehicleParameter->routeid != "") {
550  throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
551  } else {
552  throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has no route.");
553  }
554  }
555  } else {
557  }
558  myActiveRouteID = "";
559 
560  // check whether the vehicle shall be added directly to the network or
561  // shall stay in the internal buffer
565  }
566  myVehicleParameter = 0;
567 }
568 
569 
570 void
572  bool ok = true;
573  std::string errorSuffix;
574  if (myActiveRouteID != "") {
575  errorSuffix = " in route '" + myActiveRouteID + "'.";
576  } else if (myActivePlan) {
577  errorSuffix = " in person '" + myVehicleParameter->id + "'.";
578  } else {
579  errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
580  }
583  // try to parse the assigned bus stop
584  stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, 0, ok, "");
585  if (stop.busstop != "") {
586  // ok, we have obviously a bus stop
588  if (bs != 0) {
589  const MSLane& l = bs->getLane();
590  stop.lane = l.getID();
591  stop.endPos = bs->getEndLanePosition();
592  stop.startPos = bs->getBeginLanePosition();
593  } else {
594  WRITE_ERROR("The bus stop '" + stop.busstop + "' is not known" + errorSuffix);
595  return;
596  }
597  } else {
598  // no, the lane and the position should be given
599  // get the lane
600  stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, 0, ok, "");
601  if (ok && stop.lane != "") {
602  if (MSLane::dictionary(stop.lane) == 0) {
603  WRITE_ERROR("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
604  return;
605  }
606  } else {
607  WRITE_ERROR("A stop must be placed on a bus stop or a lane" + errorSuffix);
608  return;
609  }
610  if (myActivePlan &&
611  !myActivePlan->empty() &&
612  &myActivePlan->back()->getDestination() != &MSLane::dictionary(stop.lane)->getEdge()) {
613  throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + MSLane::dictionary(stop.lane)->getEdge().getID() + "!=" + myActivePlan->back()->getDestination().getID() + ").");
614  }
615  if (myActivePlan && myActivePlan->empty()) {
617  MSLane::dictionary(stop.lane)->getEdge(), -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start"));
618  }
619  stop.endPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_ENDPOS, 0, ok, MSLane::dictionary(stop.lane)->getLength());
620  if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
621  WRITE_WARNING("Deprecated attribute 'pos' in description of stop" + errorSuffix);
622  stop.endPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_POSITION, 0, ok, stop.endPos);
623  }
624  stop.startPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_STARTPOS, 0, ok, MAX2(0., stop.endPos - 2 * POSITION_EPS));
625  const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, 0, ok, false);
626  if (!ok || !checkStopPos(stop.startPos, stop.endPos, MSLane::dictionary(stop.lane)->getLength(), POSITION_EPS, friendlyPos)) {
627  WRITE_ERROR("Invalid start or end position for stop on lane '" + stop.lane + "'" + errorSuffix);
628  return;
629  }
630  }
631 
632  // get the standing duration
634  stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, 0, ok, true);
635  stop.duration = -1;
636  stop.until = -1;
637  } else {
638  stop.duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, 0, ok, -1);
639  stop.until = attrs.getOptSUMOTimeReporting(SUMO_ATTR_UNTIL, 0, ok, -1);
640  if (!ok || (stop.duration < 0 && stop.until < 0)) {
641  WRITE_ERROR("Invalid duration or end time is given for a stop on lane '" + stop.lane + "'" + errorSuffix);
642  return;
643  }
644  stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, 0, ok, false);
645  }
646  stop.parking = attrs.getOpt<bool>(SUMO_ATTR_PARKING, 0, ok, stop.triggered);
647  if (!ok) {
648  WRITE_ERROR("Invalid bool for 'triggered' or 'parking' for stop on lane '" + stop.lane + "'" + errorSuffix);
649  return;
650  }
651 
652  // expected persons
653  std::string expectedStr = attrs.getOpt<std::string>(SUMO_ATTR_EXPECTED, 0, ok, "");
654  std::set<std::string> personIDs;
655  SUMOSAXAttributes::parseStringSet(expectedStr, personIDs);
656  stop.awaitedPersons = personIDs;
657 
658  const std::string idx = attrs.getOpt<std::string>(SUMO_ATTR_INDEX, 0, ok, "end");
659  if (idx == "end") {
660  stop.index = STOP_INDEX_END;
661  } else if (idx == "fit") {
662  stop.index = STOP_INDEX_FIT;
663  } else {
664  stop.index = attrs.get<int>(SUMO_ATTR_INDEX, 0, ok);
665  if (!ok || stop.index < 0) {
666  WRITE_ERROR("Invalid 'index' for stop on lane '" + stop.lane + "'" + errorSuffix);
667  return;
668  }
669  }
670  if (myActiveRouteID != "") {
671  myActiveRouteStops.push_back(stop);
672  } else if (myActivePlan) {
673  std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, 0, ok, "waiting");
675  MSLane::dictionary(stop.lane)->getEdge(), stop.duration, stop.until, stop.startPos, actType));
676  } else {
677  myVehicleParameter->stops.push_back(stop);
678  }
679 }
680 
681 
682 /****************************************************************************/