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.sourceforge.net/
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  SUMOReal departPos = 0;
95  const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
96  StringTokenizer st(desc);
97  std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, 0, ok, "");
98  MSBusStop* bs = 0;
99  if (bsID != "") {
100  bs = MSNet::getInstance()->getBusStop(bsID);
101  if (bs == 0) {
102  throw ProcessError("Unknown bus stop '" + bsID + "' for person '" + myVehicleParameter->id + "'.");
103  }
104  departPos = bs->getBeginLanePosition();
105  }
106  if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
107  const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, pid.c_str(), ok);
108  from = MSEdge::dictionary(fromID);
109  if (from == 0) {
110  throw ProcessError("The from edge '" + fromID + "' within a ride of person '" + pid + "' is not known.");
111  }
112  if (!myActivePlan->empty() && &myActivePlan->back()->getDestination() != from) {
113  throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + fromID + "!=" + myActivePlan->back()->getDestination().getID() + ").");
114  }
115  if (myActivePlan->empty()) {
117  *from, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start"));
118  }
119  } else if (myActivePlan->empty()) {
120  throw ProcessError("The start edge within for person '" + pid + "' is not known.");
121  }
122  const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, pid.c_str(), ok);
123  MSEdge* to = MSEdge::dictionary(toID);
124  if (to == 0) {
125  throw ProcessError("The to edge '" + toID + "' within a ride of person '" + pid + "' is not known.");
126  }
127  myActivePlan->push_back(new MSPerson::MSPersonStage_Driving(*to, bs, st.getVector()));
128  break;
129  }
130  case SUMO_TAG_WALK: {
131  myActiveRoute.clear();
132  bool ok = true;
133  if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
135  } else {
136  if (attrs.hasAttribute(SUMO_ATTR_FROM) && attrs.hasAttribute(SUMO_ATTR_TO)) {
137  const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok);
138  MSEdge* from = MSEdge::dictionary(fromID);
139  if (from == 0) {
140  throw ProcessError("The from edge '" + fromID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
141  }
142  const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok);
143  MSEdge* to = MSEdge::dictionary(toID);
144  if (to == 0) {
145  throw ProcessError("The to edge '" + toID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
146  }
147  MSNet::getInstance()->getRouterTT().compute(from, to, 0, 0, myActiveRoute); // @todo: only footways, current time?
148  }
149  }
150  if (myActiveRoute.empty()) {
151  throw ProcessError("No edges to walk for person '" + myVehicleParameter->id + "'.");
152  }
153  if (!myActivePlan->empty() && &myActivePlan->back()->getDestination() != myActiveRoute.front()) {
154  throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + "!=" + myActivePlan->back()->getDestination().getID() + ").");
155  }
156  SUMOReal departPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_DEPARTPOS, myVehicleParameter->id.c_str(), ok, 0);
157  SUMOReal arrivalPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok, -1);
158  const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, 0, ok, -1);
160  if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
161  speed = attrs.getOpt<SUMOReal>(SUMO_ATTR_SPEED, 0, ok, speed);
162  if (speed < 0) {
163  throw ProcessError("Negative walking speed for '" + myVehicleParameter->id + "'.");
164  }
165  }
166  std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, 0, ok, "");
167  MSBusStop* bs = 0;
168  if (bsID != "") {
169  bs = MSNet::getInstance()->getBusStop(bsID);
170  if (bs == 0) {
171  throw ProcessError("Unknown bus stop '" + bsID + "' for person '" + myVehicleParameter->id + "'.");
172  }
173  }
174  if (myActivePlan->empty()) {
176  *myActiveRoute.front(), -1, myVehicleParameter->depart, departPos, "start"));
177  }
178  myActivePlan->push_back(new MSPerson::MSPersonStage_Walking(myActiveRoute, bs, duration, speed, departPos, arrivalPos));
179  myActiveRoute.clear();
180  break;
181  }
182  case SUMO_TAG_FLOW:
183  if (attrs.hasAttribute(SUMO_ATTR_FROM) && attrs.hasAttribute(SUMO_ATTR_TO)) {
185  bool ok = true;
186  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
187  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
188  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok),
189  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
190  closeRoute(true);
191  }
192  break;
193  case SUMO_TAG_TRIP: {
194  bool ok = true;
196  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok),
197  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
198  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok),
199  myActiveRoute, "for vehicle '" + myVehicleParameter->id + "'");
200  } else {
201  const MSEdge* fromTaz = MSEdge::dictionary(myVehicleParameter->fromTaz + "-source");
202  if (fromTaz == 0) {
203  WRITE_ERROR("Source district '" + myVehicleParameter->fromTaz + "' not known for '" + myVehicleParameter->id + "'!");
204  } else if (fromTaz->getNoFollowing() == 0) {
205  WRITE_ERROR("Source district '" + myVehicleParameter->fromTaz + "' has no outgoing edges for '" + myVehicleParameter->id + "'!");
206  } else {
207  myActiveRoute.push_back(fromTaz->getFollower(0));
208  }
209  }
210  closeRoute(true);
211  closeVehicle();
212  }
213  break;
214  default:
215  break;
216  }
217  // parse embedded vtype information
218  if (myCurrentVType != 0 && element != SUMO_TAG_VTYPE) {
220  return;
221  }
222 }
223 
224 
225 void
227  bool ok = true;
228  myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
229  if (ok) {
231  if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
232  const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
233  StringTokenizer st(vTypes);
234  while (st.hasNext()) {
235  std::string vtypeID = st.next();
237  if (type == 0) {
238  throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
239  }
241  }
242  }
243  }
244 }
245 
246 
247 void
249  if (myCurrentVTypeDistribution != 0) {
252  WRITE_ERROR("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
253  } else if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
255  WRITE_ERROR("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
256  }
258  }
259 }
260 
261 
262 void
264  // check whether the id is really necessary
265  std::string rid;
266  if (myCurrentRouteDistribution != 0) {
268  rid = "distribution '" + myCurrentRouteDistributionID + "'";
269  } else if (myVehicleParameter != 0) {
270  // ok, a vehicle is wrapping the route,
271  // we may use this vehicle's id as default
272  myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
273  if (attrs.hasAttribute(SUMO_ATTR_ID)) {
274  WRITE_WARNING("Ids of internal routes are ignored (vehicle '" + myVehicleParameter->id + "').");
275  }
276  } else {
277  bool ok = true;
278  myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok, false);
279  if (!ok) {
280  return;
281  }
282  rid = "'" + myActiveRouteID + "'";
283  }
284  if (myVehicleParameter != 0) { // have to do this here for nested route distributions
285  rid = "for vehicle '" + myVehicleParameter->id + "'";
286  }
287  bool ok = true;
288  if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
289  MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
290  }
291  myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
293  WRITE_ERROR("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
294  }
297 }
298 
299 
300 void
303  switch (element) {
304  case SUMO_TAG_VTYPE: {
306  delete myCurrentVType;
307  myCurrentVType = 0;
308  if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
309  const std::string id = vehType->getID();
310  delete vehType;
311 #ifdef HAVE_INTERNAL
312  if (!MSGlobals::gStateLoaded) {
313 #endif
314  throw ProcessError("Another vehicle type (or distribution) with the id '" + id + "' exists.");
315 #ifdef HAVE_INTERNAL
316  }
317 #endif
318  } else {
319  if (myCurrentVTypeDistribution != 0) {
321  }
322  }
323  }
324  break;
325  default:
326  break;
327  }
328 }
329 
330 
331 void
332 MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
333  if (myActiveRoute.size() == 0) {
334  delete myActiveRouteColor;
335  myActiveRouteColor = 0;
338  myActiveRouteID = "";
339  myActiveRouteRefID = "";
340  return;
341  }
342  if (myVehicleParameter != 0) {
343  throw ProcessError("Vehicle's '" + myVehicleParameter->id + "' route has no edges.");
344  } else {
345  throw ProcessError("Route '" + myActiveRouteID + "' has no edges.");
346  }
347  }
351  myActiveRoute.clear();
352  if (!MSRoute::dictionary(myActiveRouteID, route)) {
353  delete route;
354 #ifdef HAVE_INTERNAL
355  if (!MSGlobals::gStateLoaded) {
356 #endif
357  if (myVehicleParameter != 0) {
358  if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == 0) {
359  throw ProcessError("Another route for vehicle '" + myVehicleParameter->id + "' exists.");
360  } else {
361  throw ProcessError("A vehicle with id '" + myVehicleParameter->id + "' already exists.");
362  }
363  } else {
364  throw ProcessError("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
365  }
366 #ifdef HAVE_INTERNAL
367  }
368 #endif
369  } else {
370  if (myCurrentRouteDistribution != 0) {
372  }
373  }
374  myActiveRouteID = "";
375  myActiveRouteColor = 0;
376  myActiveRouteStops.clear();
377 }
378 
379 
380 void
382  // check whether the id is really necessary
383  bool ok = true;
384  if (myVehicleParameter != 0) {
385  // ok, a vehicle is wrapping the route,
386  // we may use this vehicle's id as default
387  myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
388  } else {
389  myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, 0, ok);
390  if (!ok) {
391  return;
392  }
393  }
395  std::vector<SUMOReal> probs;
396  if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
397  bool ok = true;
398  StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
399  while (st.hasNext()) {
400  probs.push_back(TplConvert::_2SUMORealSec(st.next().c_str(), 1.0));
401  }
402  }
403  if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
404  bool ok = true;
405  StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
406  size_t probIndex = 0;
407  while (st.hasNext()) {
408  std::string routeID = st.next();
409  const MSRoute* route = MSRoute::dictionary(routeID);
410  if (route == 0) {
411  throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
412  }
413  const SUMOReal prob = (probs.size() > probIndex ? probs[probIndex] : 1.0);
414  myCurrentRouteDistribution->add(prob, route, false);
415  probIndex++;
416  }
417  if (probs.size() > 0 && probIndex != probs.size()) {
418  WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
419  " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
420  }
421  }
422 }
423 
424 
425 void
427  if (myCurrentRouteDistribution != 0) {
430  WRITE_ERROR("Route distribution '" + myCurrentRouteDistributionID + "' is empty.");
433  WRITE_ERROR("Another route (or distribution) with the id '" + myCurrentRouteDistributionID + "' exists.");
434  }
436  }
437 }
438 
439 
440 void
442  // get nested route
443  const MSRoute* route = MSRoute::dictionary("!" + myVehicleParameter->id);
446  // let's check whether this vehicle had to depart before the simulation starts
448  if (route != 0) {
449  route->addReference();
450  route->release();
451  }
452  return;
453  }
454  }
455  // get the vehicle's type
456  MSVehicleType* vtype = 0;
457  if (myVehicleParameter->vtypeid != "") {
458  vtype = vehControl.getVType(myVehicleParameter->vtypeid);
459  if (vtype == 0) {
460  throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
461  }
462  } else {
463  // there should be one (at least the default one)
464  vtype = vehControl.getVType();
465  }
466  if (route == 0) {
467  // if there is no nested route, try via the (hopefully) given route-id
469  }
470  if (route == 0) {
471  // nothing found? -> error
472  if (myVehicleParameter->routeid != "") {
473  throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
474  } else {
475  throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has no route.");
476  }
477  }
478  myActiveRouteID = "";
479 
480  // try to build the vehicle
481  SUMOVehicle* vehicle = 0;
482  if (vehControl.getVehicle(myVehicleParameter->id) == 0) {
483  vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype);
484  // maybe we do not want this vehicle to be inserted due to scaling
485  if (vehControl.isInQuota()) {
486  // add the vehicle to the vehicle control
487  vehControl.addVehicle(myVehicleParameter->id, vehicle);
489  vehControl.addWaiting(*route->begin(), vehicle);
490  vehControl.registerOneWaitingForPerson();
491  }
493  myVehicleParameter = 0;
494  } else {
495  vehControl.deleteVehicle(vehicle, true);
496  myVehicleParameter = 0;
497  vehicle = 0;
498  }
499  } else {
500  // strange: another vehicle with the same id already exists
501 #ifdef HAVE_INTERNAL
502  if (!MSGlobals::gStateLoaded) {
503 #endif
504  // and was not loaded while loading a simulation state
505  // -> error
506  throw ProcessError("Another vehicle with the id '" + myVehicleParameter->id + "' exists.");
507 #ifdef HAVE_INTERNAL
508  } else {
509  // ok, it seems to be loaded previously while loading a simulation state
510  vehicle = 0;
511  }
512 #endif
513  }
514  // check whether the vehicle shall be added directly to the network or
515  // shall stay in the internal buffer
516  if (vehicle != 0) {
517  if (vehicle->getParameter().departProcedure == DEPART_GIVEN) {
519  }
520  }
521 }
522 
523 
524 void
526  if (myActivePlan->size() == 0) {
527  throw ProcessError("Person '" + myVehicleParameter->id + "' has no plan.");
528  }
530  if (type == 0) {
531  throw ProcessError("The type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.");
532  }
534  // @todo: consider myScale?
535  if ((myAddVehiclesDirectly || checkLastDepart()) && MSNet::getInstance()->getPersonControl().add(myVehicleParameter->id, person)) {
538  } else {
539  delete person;
540  }
541  myVehicleParameter = 0;
542  myActivePlan = 0;
543 }
544 
545 
546 void
548  // let's check whether vehicles had to depart before the simulation starts
550  SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
554  return;
555  }
556  }
557  if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid) == 0) {
558  throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
559  }
560  if (MSRoute::dictionary("!" + myVehicleParameter->id) == 0) {
561  // if not, try via the (hopefully) given route-id
563  if (myVehicleParameter->routeid != "") {
564  throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
565  } else {
566  throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has no route.");
567  }
568  }
569  } else {
571  }
572  myActiveRouteID = "";
573 
574  // check whether the vehicle shall be added directly to the network or
575  // shall stay in the internal buffer
579  }
580  myVehicleParameter = 0;
581 }
582 
583 
584 void
586  bool ok = true;
587  std::string errorSuffix;
588  if (myActiveRouteID != "") {
589  errorSuffix = " in route '" + myActiveRouteID + "'.";
590  } else if (myActivePlan) {
591  errorSuffix = " in person '" + myVehicleParameter->id + "'.";
592  } else {
593  errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
594  }
597  // try to parse the assigned bus stop
598  stop.busstop = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, 0, ok, "");
599  if (stop.busstop != "") {
600  // ok, we have obviously a bus stop
602  if (bs != 0) {
603  const MSLane& l = bs->getLane();
604  stop.lane = l.getID();
605  stop.endPos = bs->getEndLanePosition();
606  stop.startPos = bs->getBeginLanePosition();
607  } else {
608  WRITE_ERROR("The bus stop '" + stop.busstop + "' is not known" + errorSuffix);
609  return;
610  }
611  } else {
612  // no, the lane and the position should be given
613  // get the lane
614  stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, 0, ok, "");
615  if (ok && stop.lane != "") {
616  if (MSLane::dictionary(stop.lane) == 0) {
617  WRITE_ERROR("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
618  return;
619  }
620  } else {
621  WRITE_ERROR("A stop must be placed on a bus stop or a lane" + errorSuffix);
622  return;
623  }
624  if (myActivePlan &&
625  !myActivePlan->empty() &&
626  &myActivePlan->back()->getDestination() != &MSLane::dictionary(stop.lane)->getEdge()) {
627  throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + MSLane::dictionary(stop.lane)->getEdge().getID() + "!=" + myActivePlan->back()->getDestination().getID() + ").");
628  }
629  if (myActivePlan && myActivePlan->empty()) {
631  MSLane::dictionary(stop.lane)->getEdge(), -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start"));
632  }
633  stop.endPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_ENDPOS, 0, ok, MSLane::dictionary(stop.lane)->getLength());
634  if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
635  WRITE_WARNING("Deprecated attribute 'pos' in description of stop" + errorSuffix);
636  stop.endPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_POSITION, 0, ok, stop.endPos);
637  }
638  stop.startPos = attrs.getOpt<SUMOReal>(SUMO_ATTR_STARTPOS, 0, ok, MAX2(0., stop.endPos - 2 * POSITION_EPS));
639  const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, 0, ok, false);
640  if (!ok || !checkStopPos(stop.startPos, stop.endPos, MSLane::dictionary(stop.lane)->getLength(), POSITION_EPS, friendlyPos)) {
641  WRITE_ERROR("Invalid start or end position for stop on lane '" + stop.lane + "'" + errorSuffix);
642  return;
643  }
644  }
645 
646  // get the standing duration
648  stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, 0, ok, true);
649  stop.duration = -1;
650  stop.until = -1;
651  } else {
652  stop.duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, 0, ok, -1);
653  stop.until = attrs.getOptSUMOTimeReporting(SUMO_ATTR_UNTIL, 0, ok, -1);
654  if (!ok || (stop.duration < 0 && stop.until < 0)) {
655  WRITE_ERROR("Invalid duration or end time is given for a stop on lane '" + stop.lane + "'" + errorSuffix);
656  return;
657  }
658  stop.triggered = attrs.getOpt<bool>(SUMO_ATTR_TRIGGERED, 0, ok, false);
659  }
660  stop.parking = attrs.getOpt<bool>(SUMO_ATTR_PARKING, 0, ok, stop.triggered);
661  if (!ok) {
662  WRITE_ERROR("Invalid bool for 'triggered' or 'parking' for stop on lane '" + stop.lane + "'" + errorSuffix);
663  return;
664  }
665  const std::string idx = attrs.getOpt<std::string>(SUMO_ATTR_INDEX, 0, ok, "end");
666  if (idx == "end") {
667  stop.index = STOP_INDEX_END;
668  } else if (idx == "fit") {
669  stop.index = STOP_INDEX_FIT;
670  } else {
671  stop.index = attrs.get<int>(SUMO_ATTR_INDEX, 0, ok);
672  if (!ok || stop.index < 0) {
673  WRITE_ERROR("Invalid 'index' for stop on lane '" + stop.lane + "'" + errorSuffix);
674  return;
675  }
676  }
677  if (myActiveRouteID != "") {
678  myActiveRouteStops.push_back(stop);
679  } else if (myActivePlan) {
680  std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, 0, ok, "waiting");
682  MSLane::dictionary(stop.lane)->getEdge(), stop.duration, stop.until, stop.startPos, actType));
683  } else {
684  myVehicleParameter->stops.push_back(stop);
685  }
686 }
687 
688 
689 /****************************************************************************/