1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file MSRouteHandler.cpp
11 /// @author Daniel Krajzewicz
12 /// @author Jakob Erdmann
13 /// @author Sascha Krieg
14 /// @author Michael Behrisch
15 /// @date Mon, 9 Jul 2001
16 /// @version $Id$
17 ///
18 // Parser and container for routes during their loading
19 /****************************************************************************/
20
21
22 // ===========================================================================
23 // included modules
24 // ===========================================================================
25 #include <config.h>
26
27 #include <string>
28 #include <map>
29 #include <vector>
30 #include <microsim/MSRoute.h>
31 #include <microsim/MSEdge.h>
32 #include <microsim/MSJunction.h>
33 #include <microsim/MSVehicleType.h>
34 #include <microsim/MSVehicle.h>
35 #include <microsim/MSInsertionControl.h>
36 #include <microsim/MSVehicleControl.h>
37 #include <microsim/MSLane.h>
38 #include "MSRouteHandler.h"
39 #include "MSTransportableControl.h"
40 #include <utils/xml/SUMOSAXHandler.h>
41 #include <utils/xml/SUMOXMLDefinitions.h>
42 #include <utils/common/MsgHandler.h>
43 #include <utils/common/StringUtils.h>
44 #include <utils/common/StringTokenizer.h>
45 #include <utils/common/UtilExceptions.h>
46 #include <utils/options/OptionsCont.h>
47 #include <utils/router/IntermodalRouter.h>
48 #include <utils/router/PedestrianRouter.h>
49 #include "MSNet.h"
50
51 #include "MSParkingArea.h"
52 #include "MSStoppingPlace.h"
53 #include <microsim/MSGlobals.h>
54 #include <microsim/trigger/MSChargingStation.h>
55 #include <utils/vehicle/SUMOVehicleParserHelper.h>
56
57
58 // ===========================================================================
59 // static members
60 // ===========================================================================
61 std::mt19937 MSRouteHandler::myParsingRNG;
62
63
64 // ===========================================================================
65 // method definitions
66 // ===========================================================================
MSRouteHandler(const std::string & file,bool addVehiclesDirectly)67 MSRouteHandler::MSRouteHandler(const std::string& file,
68 bool addVehiclesDirectly) :
69 SUMORouteHandler(file, addVehiclesDirectly ? "" : "routes"),
70 myActivePlan(nullptr),
71 myActiveContainerPlan(nullptr),
72 myAddVehiclesDirectly(addVehiclesDirectly),
73 myCurrentVTypeDistribution(nullptr),
74 myCurrentRouteDistribution(nullptr),
75 myAmLoadingState(false) {
76 myActiveRoute.reserve(100);
77 }
78
79
~MSRouteHandler()80 MSRouteHandler::~MSRouteHandler() {
81 }
82
83 void
deleteActivePlans()84 MSRouteHandler::deleteActivePlans() {
85 MSTransportable::MSTransportablePlan::iterator i;
86 if (myActivePlan != nullptr) {
87 for (i = myActivePlan->begin(); i != myActivePlan->end(); i++) {
88 delete *i;
89 }
90 delete myActivePlan;
91 myActivePlan = nullptr;
92 }
93 if (myActiveContainerPlan != nullptr) {
94 for (i = myActiveContainerPlan->begin(); i != myActiveContainerPlan->end(); i++) {
95 delete *i;
96 }
97 delete myActiveContainerPlan;
98 myActivePlan = nullptr;
99 }
100 }
101
102
103 void
parseFromViaTo(std::string element,const SUMOSAXAttributes & attrs)104 MSRouteHandler::parseFromViaTo(std::string element,
105 const SUMOSAXAttributes& attrs) {
106 myActiveRoute.clear();
107 bool useTaz = OptionsCont::getOptions().getBool("with-taz");
108 if (useTaz && !myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET) && !myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
109 WRITE_WARNING("Taz usage was requested but no taz present in " + element + " '" + myVehicleParameter->id + "'!");
110 useTaz = false;
111 }
112 bool ok = true;
113 if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_FROM)) && myVehicleParameter->wasSet(VEHPARS_FROM_TAZ_SET)) {
114 const MSEdge* fromTaz = MSEdge::dictionary(myVehicleParameter->fromTaz + "-source");
115 if (fromTaz == nullptr) {
116 throw ProcessError("Source taz '" + myVehicleParameter->fromTaz + "' not known for " + element + " '" + myVehicleParameter->id + "'!");
117 } else if (fromTaz->getNumSuccessors() == 0) {
118 throw ProcessError("Source taz '" + myVehicleParameter->fromTaz + "' has no outgoing edges for " + element + " '" + myVehicleParameter->id + "'!");
119 } else {
120 myActiveRoute.push_back(fromTaz);
121 }
122 } else {
123 MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok, "", true),
124 myActiveRoute, "for " + element + " '" + myVehicleParameter->id + "'");
125 }
126 if (!attrs.hasAttribute(SUMO_ATTR_VIA) && !attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
127 myInsertStopEdgesAt = (int)myActiveRoute.size();
128 }
129 MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true),
130 myActiveRoute, "for " + element + " '" + myVehicleParameter->id + "'");
131 myVehicleParameter->via = StringTokenizer(attrs.getOpt<std::string>(SUMO_ATTR_VIA, myVehicleParameter->id.c_str(), ok, "", true)).getVector();
132 if ((useTaz || !attrs.hasAttribute(SUMO_ATTR_TO)) && myVehicleParameter->wasSet(VEHPARS_TO_TAZ_SET)) {
133 const MSEdge* toTaz = MSEdge::dictionary(myVehicleParameter->toTaz + "-sink");
134 if (toTaz == nullptr) {
135 throw ProcessError("Sink taz '" + myVehicleParameter->toTaz + "' not known for " + element + " '" + myVehicleParameter->id + "'!");
136 } else if (toTaz->getNumPredecessors() == 0) {
137 throw ProcessError("Sink taz '" + myVehicleParameter->toTaz + "' has no incoming edges for " + element + " '" + myVehicleParameter->id + "'!");
138 } else {
139 myActiveRoute.push_back(toTaz);
140 }
141 } else {
142 MSEdge::parseEdgesList(attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "", true),
143 myActiveRoute, "for " + element + " '" + myVehicleParameter->id + "'");
144 }
145 myActiveRouteID = "!" + myVehicleParameter->id;
146 if (myVehicleParameter->routeid == "") {
147 myVehicleParameter->routeid = myActiveRouteID;
148 }
149 }
150
151
152 void
myStartElement(int element,const SUMOSAXAttributes & attrs)153 MSRouteHandler::myStartElement(int element,
154 const SUMOSAXAttributes& attrs) {
155 SUMORouteHandler::myStartElement(element, attrs);
156 try {
157 switch (element) {
158 case SUMO_TAG_PERSON:
159 case SUMO_TAG_PERSONFLOW:
160 if (!MSNet::getInstance()->getVehicleControl().hasVType(myVehicleParameter->vtypeid)) {
161 const std::string error = "The type '" + myVehicleParameter->vtypeid + "' for person '" + myVehicleParameter->id + "' is not known.";
162 delete myVehicleParameter;
163 myVehicleParameter = nullptr;
164 throw ProcessError(error);
165 }
166 myActivePlan = new MSTransportable::MSTransportablePlan();
167 break;
168 case SUMO_TAG_CONTAINER:
169 myActiveContainerPlan = new MSTransportable::MSTransportablePlan();
170 break;
171 case SUMO_TAG_RIDE: {
172 const std::string pid = myVehicleParameter->id;
173 bool ok = true;
174 MSEdge* from = nullptr;
175 const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, pid.c_str(), ok);
176 StringTokenizer st(desc);
177 std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
178 MSStoppingPlace* bs = nullptr;
179 MSEdge* to = nullptr;
180 if (bsID != "") {
181 bs = MSNet::getInstance()->getStoppingPlace(bsID, SUMO_TAG_BUS_STOP);
182 if (bs == nullptr) {
183 throw ProcessError("Unknown bus stop '" + bsID + "' for person '" + myVehicleParameter->id + "'.");
184 }
185 to = &bs->getLane().getEdge();
186 }
187 double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
188 bs == nullptr ? -NUMERICAL_EPS : bs->getEndLanePosition());
189 if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
190 const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, pid.c_str(), ok);
191 from = MSEdge::dictionary(fromID);
192 if (from == nullptr) {
193 throw ProcessError("The from edge '" + fromID + "' within a ride of person '" + pid + "' is not known.");
194 }
195 if (!myActivePlan->empty() && myActivePlan->back()->getDestination() != from) {
196 throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + fromID + "!=" + myActivePlan->back()->getDestination()->getID() + ").");
197 }
198 if (myActivePlan->empty()) {
199 myActivePlan->push_back(new MSTransportable::Stage_Waiting(
200 from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
201 }
202 } else if (myActivePlan->empty()) {
203 throw ProcessError("The start edge for person '" + pid + "' is not known.");
204 }
205 if (to == nullptr) {
206 const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, pid.c_str(), ok);
207 to = MSEdge::dictionary(toID);
208 if (to == nullptr) {
209 throw ProcessError("The to edge '" + toID + "' within a ride of person '" + pid + "' is not known.");
210 }
211 }
212 const std::string intendedVeh = attrs.getOpt<std::string>(SUMO_ATTR_INTENDED, nullptr, ok, "");
213 const SUMOTime intendedDepart = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DEPART, nullptr, ok, -1);
214 myActivePlan->push_back(new MSPerson::MSPersonStage_Driving(to, bs, arrivalPos, st.getVector(), intendedVeh, intendedDepart));
215 break;
216 }
217 case SUMO_TAG_TRANSPORT:
218 try {
219 const std::string containerId = myVehicleParameter->id;
220 bool ok = true;
221 MSEdge* from = nullptr;
222 const std::string desc = attrs.get<std::string>(SUMO_ATTR_LINES, containerId.c_str(), ok);
223 StringTokenizer st(desc);
224 std::string csID = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
225 MSStoppingPlace* cs = nullptr;
226 if (csID != "") {
227 cs = MSNet::getInstance()->getStoppingPlace(csID, SUMO_TAG_CONTAINER_STOP);
228 if (cs == nullptr) {
229 throw ProcessError("Unknown container stop '" + csID + "' for container '" + myVehicleParameter->id + "'.");
230 }
231 }
232 double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok,
233 cs == nullptr ? -NUMERICAL_EPS : cs->getEndLanePosition());
234 if (attrs.hasAttribute(SUMO_ATTR_FROM)) {
235 const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, containerId.c_str(), ok);
236 from = MSEdge::dictionary(fromID);
237 if (from == nullptr) {
238 throw ProcessError("The from edge '" + fromID + "' within a transport of container '" + containerId + "' is not known.");
239 }
240 if (!myActiveContainerPlan->empty() && myActiveContainerPlan->back()->getDestination() != from) {
241 throw ProcessError("Disconnected plan for container '" + myVehicleParameter->id + "' (" + fromID + "!=" + myActiveContainerPlan->back()->getDestination()->getID() + ").");
242 }
243 if (myActiveContainerPlan->empty()) {
244 myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
245 from, nullptr, -1, myVehicleParameter->depart, myVehicleParameter->departPos, "start", true));
246 }
247 } else if (myActiveContainerPlan->empty()) {
248 throw ProcessError("The start edge within a transport of container '" + containerId + "' is not known.");
249 }
250 const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, containerId.c_str(), ok);
251 MSEdge* to = MSEdge::dictionary(toID);
252 if (to == nullptr) {
253 throw ProcessError("The to edge '" + toID + "' within a transport of container '" + containerId + "' is not known.");
254 }
255 myActiveContainerPlan->push_back(new MSContainer::MSContainerStage_Driving(to, cs, arrivalPos, st.getVector()));
256
257 } catch (ProcessError&) {
258 deleteActivePlans();
259 throw;
260 }
261 break;
262 case SUMO_TAG_TRANSHIP: {
263 myActiveRoute.clear();
264 bool ok = true;
265 double departPos = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS, myVehicleParameter->id.c_str(), ok, 0);
266 double arrivalPos = attrs.getOpt<double>(SUMO_ATTR_ARRIVALPOS, myVehicleParameter->id.c_str(), ok, -NUMERICAL_EPS);
267 double speed = DEFAULT_CONTAINER_TRANSHIP_SPEED;
268 const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
269 // need to check for explicitly set speed since we might have // DEFAULT_VEHTYPE
270 if (vtype != nullptr && vtype->wasSet(VTYPEPARS_MAXSPEED_SET)) {
271 speed = vtype->getMaxSpeed();
272 }
273 speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, speed);
274 if (speed <= 0) {
275 throw ProcessError("Non-positive tranship speed for container '" + myVehicleParameter->id + "'.");
276 }
277 std::string csID = attrs.getOpt<std::string>(SUMO_ATTR_CONTAINER_STOP, nullptr, ok, "");
278 MSStoppingPlace* cs = nullptr;
279 if (csID != "") {
280 cs = MSNet::getInstance()->getStoppingPlace(csID, SUMO_TAG_CONTAINER_STOP);
281 if (cs == nullptr) {
282 throw ProcessError("Unknown container stop '" + csID + "' for container '" + myVehicleParameter->id + "'.");
283 }
284 arrivalPos = cs->getEndLanePosition();
285 }
286 if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
287 MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
288 } else {
289 if (attrs.hasAttribute(SUMO_ATTR_FROM) && attrs.hasAttribute(SUMO_ATTR_TO)) {
290 const std::string fromID = attrs.get<std::string>(SUMO_ATTR_FROM, myVehicleParameter->id.c_str(), ok);
291 MSEdge* from = MSEdge::dictionary(fromID);
292 if (from == nullptr) {
293 throw ProcessError("The from edge '" + fromID + "' within a tranship of container '" + myVehicleParameter->id + "' is not known.");
294 }
295 const std::string toID = attrs.get<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok);
296 MSEdge* to = MSEdge::dictionary(toID);
297 if (to == nullptr) {
298 throw ProcessError("The to edge '" + toID + "' within a tranship of container '" + myVehicleParameter->id + "' is not known.");
299 }
300 //the route of the container's tranship stage consists only of the 'from' and the 'to' edge
301 myActiveRoute.push_back(from);
302 myActiveRoute.push_back(to);
303 if (myActiveRoute.empty()) {
304 const std::string error = "No connection found between '" + from->getID() + "' and '" + to->getID() + "' for container '" + myVehicleParameter->id + "'.";
305 if (!MSGlobals::gCheckRoutes) {
306 myActiveRoute.push_back(from);
307 } else {
308 WRITE_ERROR(error);
309 }
310 }
311 }
312 }
313 if (myActiveRoute.empty()) {
314 throw ProcessError("No edges to tranship container '" + myVehicleParameter->id + "'.");
315 }
316 if (!myActiveContainerPlan->empty() && myActiveContainerPlan->back()->getDestination() != myActiveRoute.front()) {
317 throw ProcessError("Disconnected plan for container '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + "!=" + myActiveContainerPlan->back()->getDestination()->getID() + ").");
318 }
319 if (myActiveContainerPlan->empty()) {
320 myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
321 myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, departPos, "start", true));
322 }
323 myActiveContainerPlan->push_back(new MSContainer::MSContainerStage_Tranship(myActiveRoute, cs, speed, departPos, arrivalPos));
324 myActiveRoute.clear();
325 break;
326 }
327 case SUMO_TAG_FLOW:
328 parseFromViaTo("flow", attrs);
329 break;
330 case SUMO_TAG_TRIP:
331 parseFromViaTo("trip", attrs);
332 break;
333 default:
334 break;
335 }
336 } catch (ProcessError&) {
337 delete myVehicleParameter;
338 myVehicleParameter = nullptr;
339 throw;
340 }
341 }
342
343
344 void
openVehicleTypeDistribution(const SUMOSAXAttributes & attrs)345 MSRouteHandler::openVehicleTypeDistribution(const SUMOSAXAttributes& attrs) {
346 bool ok = true;
347 myCurrentVTypeDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
348 if (ok) {
349 myCurrentVTypeDistribution = new RandomDistributor<MSVehicleType*>();
350 if (attrs.hasAttribute(SUMO_ATTR_VTYPES)) {
351 const std::string vTypes = attrs.get<std::string>(SUMO_ATTR_VTYPES, myCurrentVTypeDistributionID.c_str(), ok);
352 StringTokenizer st(vTypes);
353 while (st.hasNext()) {
354 std::string vtypeID = st.next();
355 MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(vtypeID, &myParsingRNG);
356 if (type == nullptr) {
357 throw ProcessError("Unknown vtype '" + vtypeID + "' in distribution '" + myCurrentVTypeDistributionID + "'.");
358 }
359 myCurrentVTypeDistribution->add(type, type->getDefaultProbability());
360 }
361 }
362 }
363 }
364
365
366 void
closeVehicleTypeDistribution()367 MSRouteHandler::closeVehicleTypeDistribution() {
368 if (myCurrentVTypeDistribution != nullptr) {
369 if (MSGlobals::gStateLoaded && MSNet::getInstance()->getVehicleControl().hasVTypeDistribution(myCurrentVTypeDistributionID)) {
370 delete myCurrentVTypeDistribution;
371 return;
372 }
373 if (myCurrentVTypeDistribution->getOverallProb() == 0) {
374 delete myCurrentVTypeDistribution;
375 throw ProcessError("Vehicle type distribution '" + myCurrentVTypeDistributionID + "' is empty.");
376 }
377 if (!MSNet::getInstance()->getVehicleControl().addVTypeDistribution(myCurrentVTypeDistributionID, myCurrentVTypeDistribution)) {
378 delete myCurrentVTypeDistribution;
379 throw ProcessError("Another vehicle type (or distribution) with the id '" + myCurrentVTypeDistributionID + "' exists.");
380 }
381 myCurrentVTypeDistribution = nullptr;
382 }
383 }
384
385
386 void
openRoute(const SUMOSAXAttributes & attrs)387 MSRouteHandler::openRoute(const SUMOSAXAttributes& attrs) {
388 myActiveRoute.clear();
389 myInsertStopEdgesAt = -1;
390 // check whether the id is really necessary
391 std::string rid;
392 if (myCurrentRouteDistribution != nullptr) {
393 myActiveRouteID = myCurrentRouteDistributionID + "#" + toString(myCurrentRouteDistribution->getProbs().size()); // !!! document this
394 rid = "distribution '" + myCurrentRouteDistributionID + "'";
395 } else if (myVehicleParameter != nullptr) {
396 // ok, a vehicle is wrapping the route,
397 // we may use this vehicle's id as default
398 myActiveRouteID = "!" + myVehicleParameter->id; // !!! document this
399 if (attrs.hasAttribute(SUMO_ATTR_ID)) {
400 WRITE_WARNING("Ids of internal routes are ignored (vehicle '" + myVehicleParameter->id + "').");
401 }
402 } else {
403 bool ok = true;
404 myActiveRouteID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok, false);
405 if (!ok) {
406 return;
407 }
408 rid = "'" + myActiveRouteID + "'";
409 }
410 if (myVehicleParameter != nullptr) { // have to do this here for nested route distributions
411 rid = "for vehicle '" + myVehicleParameter->id + "'";
412 }
413 bool ok = true;
414 if (attrs.hasAttribute(SUMO_ATTR_EDGES)) {
415 MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myActiveRouteID.c_str(), ok), myActiveRoute, rid);
416 }
417 myActiveRouteRefID = attrs.getOpt<std::string>(SUMO_ATTR_REFID, myActiveRouteID.c_str(), ok, "");
418 if (myActiveRouteRefID != "" && MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG) == nullptr) {
419 WRITE_ERROR("Invalid reference to route '" + myActiveRouteRefID + "' in route " + rid + ".");
420 }
421 myActiveRouteProbability = attrs.getOpt<double>(SUMO_ATTR_PROB, myActiveRouteID.c_str(), ok, DEFAULT_VEH_PROB);
422 myActiveRouteColor = attrs.hasAttribute(SUMO_ATTR_COLOR) ? new RGBColor(attrs.get<RGBColor>(SUMO_ATTR_COLOR, myActiveRouteID.c_str(), ok)) : nullptr;
423 myCurrentCosts = attrs.getOpt<double>(SUMO_ATTR_COST, myActiveRouteID.c_str(), ok, -1);
424 if (ok && myCurrentCosts != -1 && myCurrentCosts < 0) {
425 WRITE_ERROR("Invalid cost for route '" + myActiveRouteID + "'.");
426 }
427 }
428
429
430 void
openTrip(const SUMOSAXAttributes &)431 MSRouteHandler::openTrip(const SUMOSAXAttributes& /*attrs*/) {
432 }
433
434
435 void
closeRoute(const bool mayBeDisconnected)436 MSRouteHandler::closeRoute(const bool mayBeDisconnected) {
437 std::string type = "vehicle";
438 if (mayBeDisconnected) {
439 if (myVehicleParameter->repetitionNumber >= 0) {
440 type = "flow";
441 } else {
442 type = "trip";
443 }
444 }
445
446 try {
447 if (myActiveRoute.size() == 0) {
448 delete myActiveRouteColor;
449 myActiveRouteColor = nullptr;
450 if (myActiveRouteRefID != "" && myCurrentRouteDistribution != nullptr) {
451 const MSRoute* route = MSRoute::dictionary(myActiveRouteRefID, &myParsingRNG);
452 if (route != nullptr) {
453 if (myCurrentRouteDistribution->add(route, myActiveRouteProbability)) {
454 route->addReference();
455 }
456 }
457 myActiveRouteID = "";
458 myActiveRouteRefID = "";
459 return;
460 }
461 if (myVehicleParameter != nullptr) {
462 throw ProcessError("The route for " + type + " '" + myVehicleParameter->id + "' has no edges.");
463 } else {
464 throw ProcessError("Route '" + myActiveRouteID + "' has no edges.");
465 }
466 }
467 if (myActiveRoute.size() == 1 && myActiveRoute.front()->isTazConnector()) {
468 throw ProcessError("The routing information for " + type + " '" + myVehicleParameter->id + "' is insufficient.");
469 }
470 MSRoute* route = new MSRoute(myActiveRouteID, myActiveRoute,
471 myVehicleParameter == nullptr || myVehicleParameter->repetitionNumber >= 1,
472 myActiveRouteColor, myActiveRouteStops);
473 route->setCosts(myCurrentCosts);
474 myActiveRoute.clear();
475 if (!MSRoute::dictionary(myActiveRouteID, route)) {
476 delete route;
477 if (!MSGlobals::gStateLoaded) {
478 if (myVehicleParameter != nullptr) {
479 if (MSNet::getInstance()->getVehicleControl().getVehicle(myVehicleParameter->id) == nullptr) {
480 throw ProcessError("Another route for " + type + " '" + myVehicleParameter->id + "' exists.");
481 } else {
482 throw ProcessError("A vehicle with id '" + myVehicleParameter->id + "' already exists.");
483 }
484 } else {
485 throw ProcessError("Another route (or distribution) with the id '" + myActiveRouteID + "' exists.");
486 }
487 }
488 } else {
489 if (myCurrentRouteDistribution != nullptr) {
490 if (myCurrentRouteDistribution->add(route, myActiveRouteProbability)) {
491 route->addReference();
492 }
493 }
494 }
495 myActiveRouteID = "";
496 myActiveRouteColor = nullptr;
497 myActiveRouteStops.clear();
498 } catch (ProcessError&) {
499 delete myVehicleParameter;
500 throw;
501 }
502 }
503
504
505 void
openRouteDistribution(const SUMOSAXAttributes & attrs)506 MSRouteHandler::openRouteDistribution(const SUMOSAXAttributes& attrs) {
507 // check whether the id is really necessary
508 bool ok = true;
509 if (myVehicleParameter != nullptr) {
510 // ok, a vehicle is wrapping the route,
511 // we may use this vehicle's id as default
512 myCurrentRouteDistributionID = "!" + myVehicleParameter->id; // !!! document this
513 } else {
514 myCurrentRouteDistributionID = attrs.get<std::string>(SUMO_ATTR_ID, nullptr, ok);
515 if (!ok) {
516 return;
517 }
518 }
519 myCurrentRouteDistribution = new RandomDistributor<const MSRoute*>();
520 std::vector<double> probs;
521 if (attrs.hasAttribute(SUMO_ATTR_PROBS)) {
522 bool ok = true;
523 StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_PROBS, myCurrentRouteDistributionID.c_str(), ok));
524 while (st.hasNext()) {
525 probs.push_back(StringUtils::toDoubleSecure(st.next(), 1.0));
526 }
527 }
528 if (attrs.hasAttribute(SUMO_ATTR_ROUTES)) {
529 bool ok = true;
530 StringTokenizer st(attrs.get<std::string>(SUMO_ATTR_ROUTES, myCurrentRouteDistributionID.c_str(), ok));
531 int probIndex = 0;
532 while (st.hasNext()) {
533 std::string routeID = st.next();
534 const MSRoute* route = MSRoute::dictionary(routeID, &myParsingRNG);
535 if (route == nullptr) {
536 throw ProcessError("Unknown route '" + routeID + "' in distribution '" + myCurrentRouteDistributionID + "'.");
537 }
538 const double prob = ((int)probs.size() > probIndex ? probs[probIndex] : 1.0);
539 if (myCurrentRouteDistribution->add(route, prob, false)) {
540 route->addReference();
541 }
542 probIndex++;
543 }
544 if (probs.size() > 0 && probIndex != (int)probs.size()) {
545 WRITE_WARNING("Got " + toString(probs.size()) + " probabilities for " + toString(probIndex) +
546 " routes in routeDistribution '" + myCurrentRouteDistributionID + "'");
547 }
548 }
549 }
550
551
552 void
closeRouteDistribution()553 MSRouteHandler::closeRouteDistribution() {
554 if (myCurrentRouteDistribution != nullptr) {
555 const bool haveSameID = MSRoute::dictionary(myCurrentRouteDistributionID, &myParsingRNG) != nullptr;
556 if (MSGlobals::gStateLoaded && haveSameID) {
557 delete myCurrentRouteDistribution;
558 return;
559 }
560 if (haveSameID) {
561 delete myCurrentRouteDistribution;
562 throw ProcessError("Another route (or distribution) with the id '" + myCurrentRouteDistributionID + "' exists.");
563 }
564 if (myCurrentRouteDistribution->getOverallProb() == 0) {
565 delete myCurrentRouteDistribution;
566 throw ProcessError("Route distribution '" + myCurrentRouteDistributionID + "' is empty.");
567 }
568 MSRoute::dictionary(myCurrentRouteDistributionID, myCurrentRouteDistribution, myVehicleParameter == nullptr);
569 myCurrentRouteDistribution = nullptr;
570 }
571 }
572
573
574 void
closeVehicle()575 MSRouteHandler::closeVehicle() {
576 // get nested route
577 const MSRoute* route = MSRoute::dictionary("!" + myVehicleParameter->id, &myParsingRNG);
578 MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
579 if (myVehicleParameter->departProcedure == DEPART_GIVEN) {
580 // let's check whether this vehicle had to depart before the simulation starts
581 if (!(myAddVehiclesDirectly || checkLastDepart()) || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
582 if (route != nullptr) {
583 route->addReference();
584 route->release();
585 }
586 return;
587 }
588 }
589
590 // get the vehicle's type
591 MSVehicleType* vtype = nullptr;
592
593 try {
594 if (myVehicleParameter->vtypeid != "") {
595 vtype = vehControl.getVType(myVehicleParameter->vtypeid, &myParsingRNG);
596 if (vtype == nullptr) {
597 throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
598 }
599 if (vtype->getVehicleClass() == SVC_PEDESTRIAN) {
600 WRITE_WARNING("Vehicle type '" + vtype->getID() + "' with vClass=pedestrian should only be used for persons and not for vehicle '" + myVehicleParameter->id + "'.");
601 }
602 } else {
603 // there should be one (at least the default one)
604 vtype = vehControl.getVType(DEFAULT_VTYPE_ID, &myParsingRNG);
605 }
606 if (myVehicleParameter->wasSet(VEHPARS_ROUTE_SET)) {
607 // if the route id was given, prefer that one
608 if (route != nullptr && !myAmLoadingState) {
609 WRITE_WARNING("Ignoring child element 'route' for vehicle '" + myVehicleParameter->id + "' because attribute 'route' is set.");
610 }
611 route = MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG);
612 }
613 if (route == nullptr) {
614 // nothing found? -> error
615 if (myVehicleParameter->routeid != "") {
616 throw ProcessError("The route '" + myVehicleParameter->routeid + "' for vehicle '" + myVehicleParameter->id + "' is not known.");
617 } else {
618 throw ProcessError("Vehicle '" + myVehicleParameter->id + "' has no route.");
619 }
620 }
621 myActiveRouteID = "";
622
623 } catch (ProcessError&) {
624 delete myVehicleParameter;
625 throw;
626 }
627
628 // try to build the vehicle
629 SUMOVehicle* vehicle = nullptr;
630 if (vehControl.getVehicle(myVehicleParameter->id) == nullptr) {
631 try {
632 vehicle = vehControl.buildVehicle(myVehicleParameter, route, vtype, !MSGlobals::gCheckRoutes);
633 } catch (const ProcessError& e) {
634 if (!MSGlobals::gCheckRoutes) {
635 WRITE_WARNING(e.what());
636 vehControl.deleteVehicle(nullptr, true);
637 myVehicleParameter = nullptr;
638 vehicle = nullptr;
639 return;
640 } else {
641 throw e;
642 }
643 }
644 const SUMOTime origDepart = myVehicleParameter->depart;
645 // maybe we do not want this vehicle to be inserted due to scaling
646 int quota = myAmLoadingState ? 1 : vehControl.getQuota();
647 if (quota > 0) {
648 registerLastDepart();
649 myVehicleParameter->depart += MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
650 vehControl.addVehicle(myVehicleParameter->id, vehicle);
651 for (int i = 1; i < quota; i++) {
652 MSNet::getInstance()->getInsertionControl().add(vehicle);
653 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*myVehicleParameter);
654 newPars->id = myVehicleParameter->id + "." + toString(i);
655 newPars->depart = origDepart + MSNet::getInstance()->getInsertionControl().computeRandomDepartOffset();
656 vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes);
657 vehControl.addVehicle(newPars->id, vehicle);
658 }
659 myVehicleParameter = nullptr;
660 } else {
661 vehControl.deleteVehicle(vehicle, true);
662 myVehicleParameter = nullptr;
663 vehicle = nullptr;
664 }
665 } else {
666 // strange: another vehicle with the same id already exists
667 if (!MSGlobals::gStateLoaded) {
668 // and was not loaded while loading a simulation state
669 // -> error
670 std::string veh_id = myVehicleParameter->id;
671 delete myVehicleParameter;
672 myVehicleParameter = nullptr;
673 throw ProcessError("Another vehicle with the id '" + veh_id + "' exists.");
674 } else {
675 // ok, it seems to be loaded previously while loading a simulation state
676 vehicle = nullptr;
677 }
678 }
679 // check whether the vehicle shall be added directly to the network or
680 // shall stay in the internal buffer
681 if (vehicle != nullptr) {
682 if (vehicle->getParameter().departProcedure == DEPART_GIVEN) {
683 MSNet::getInstance()->getInsertionControl().add(vehicle);
684 }
685 }
686 }
687
688
689 void
closePerson()690 MSRouteHandler::closePerson() {
691 if (myActivePlan->size() == 0) {
692 const std::string error = "Person '" + myVehicleParameter->id + "' has no plan.";
693 delete myVehicleParameter;
694 myVehicleParameter = nullptr;
695 deleteActivePlans();
696 throw ProcessError(error);
697 }
698 // let's check whether this person had to depart before the simulation starts
699 if (!(myAddVehiclesDirectly || checkLastDepart())
700 || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
701 delete myVehicleParameter;
702 myVehicleParameter = nullptr;
703 deleteActivePlans();
704 return;
705 }
706 // type existence has been checked on opening
707 MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
708 MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(myVehicleParameter, type, myActivePlan, &myParsingRNG);
709 // @todo: consider myScale?
710 if (MSNet::getInstance()->getPersonControl().add(person)) {
711 registerLastDepart();
712 } else {
713 ProcessError error("Another person with the id '" + myVehicleParameter->id + "' exists.");
714 delete person;
715 throw error;
716 }
717 myVehicleParameter = nullptr;
718 myActivePlan = nullptr;
719 }
720
721
722 void
closePersonFlow()723 MSRouteHandler::closePersonFlow() {
724 if (myActivePlan->size() == 0) {
725 const std::string error = "personFlow '" + myVehicleParameter->id + "' has no plan.";
726 delete myVehicleParameter;
727 myVehicleParameter = nullptr;
728 deleteActivePlans();
729 throw ProcessError(error);
730 }
731 // let's check whether this person had to depart before the simulation starts
732 if (!(myAddVehiclesDirectly || checkLastDepart())
733 || (myVehicleParameter->depart < string2time(OptionsCont::getOptions().getString("begin")) && !myAmLoadingState)) {
734 delete myVehicleParameter;
735 myVehicleParameter = nullptr;
736 deleteActivePlans();
737 return;
738 }
739 // type existence has been checked on opening
740 MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG);
741 // instantiate all persons of this flow
742 int i = 0;
743 registerLastDepart();
744 std::string baseID = myVehicleParameter->id;
745 if (myVehicleParameter->repetitionProbability > 0) {
746 if (myVehicleParameter->repetitionEnd == SUMOTime_MAX) {
747 throw ProcessError("probabilistic personFlow '" + myVehicleParameter->id + "' must specify end time");
748 } else {
749 for (SUMOTime t = myVehicleParameter->depart; t < myVehicleParameter->repetitionEnd; t += TIME2STEPS(1)) {
750 if (RandHelper::rand(&myParsingRNG) < myVehicleParameter->repetitionProbability) {
751 addFlowPerson(t, type, baseID, i++);
752 }
753 }
754 }
755 } else {
756 SUMOTime depart = myVehicleParameter->depart;
757 for (; i < myVehicleParameter->repetitionNumber; i++) {
758 addFlowPerson(depart, type, baseID, i);
759 depart += myVehicleParameter->repetitionOffset;
760 }
761 }
762
763 myVehicleParameter = nullptr;
764 myActivePlan = nullptr;
765 }
766
767 void
addFlowPerson(SUMOTime depart,MSVehicleType * type,const std::string & baseID,int i)768 MSRouteHandler::addFlowPerson(SUMOTime depart, MSVehicleType* type, const std::string& baseID, int i) {
769 if (i > 0) {
770 // copy parameter and plan because the person takes over responsibility
771 SUMOVehicleParameter* copyParam = new SUMOVehicleParameter();
772 *copyParam = *myVehicleParameter;
773 myVehicleParameter = copyParam;
774 MSTransportable::MSTransportablePlan* copyPlan = new MSTransportable::MSTransportablePlan();
775 for (MSTransportable::Stage* s : *myActivePlan) {
776 copyPlan->push_back(s->clone());
777 }
778 myActivePlan = copyPlan;
779 }
780 myVehicleParameter->id = baseID + "." + toString(i);
781 myVehicleParameter->depart = depart;
782 MSTransportable* person = MSNet::getInstance()->getPersonControl().buildPerson(myVehicleParameter, type, myActivePlan, &myParsingRNG);
783 if (!MSNet::getInstance()->getPersonControl().add(person)) {
784 ProcessError error("Another person with the id '" + myVehicleParameter->id + "' exists.");
785 delete person;
786 throw error;
787 }
788 }
789
790 void
closeVType()791 MSRouteHandler::closeVType() {
792 MSVehicleType* vehType = MSVehicleType::build(*myCurrentVType);
793 delete myCurrentVType;
794 myCurrentVType = nullptr;
795 if (!MSNet::getInstance()->getVehicleControl().addVType(vehType)) {
796 const std::string id = vehType->getID();
797 delete vehType;
798 if (!MSGlobals::gStateLoaded) {
799 throw ProcessError("Another vehicle type (or distribution) with the id '" + id + "' exists.");
800 }
801 } else {
802 if (myCurrentVTypeDistribution != nullptr) {
803 myCurrentVTypeDistribution->add(vehType, vehType->getDefaultProbability());
804 }
805 }
806 }
807
808
809 void
closeContainer()810 MSRouteHandler::closeContainer() {
811 if (myActiveContainerPlan->size() == 0) {
812 throw ProcessError("Container '" + myVehicleParameter->id + "' has no plan.");
813 }
814 MSVehicleType* type = MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid);
815 if (type == nullptr) {
816 throw ProcessError("The type '" + myVehicleParameter->vtypeid + "' for container '" + myVehicleParameter->id + "' is not known.");
817 }
818 MSTransportable* container = MSNet::getInstance()->getContainerControl().buildContainer(myVehicleParameter, type, myActiveContainerPlan);
819 // @todo: consider myScale?
820 if (myAddVehiclesDirectly || checkLastDepart()) {
821 if (MSNet::getInstance()->getContainerControl().add(container)) {
822 registerLastDepart();
823 } else {
824 ProcessError error("Another container with the id '" + myVehicleParameter->id + "' exists.");
825 delete container;
826 throw error;
827 }
828 } else {
829 // warning already given
830 delete container;
831 }
832 myVehicleParameter = nullptr;
833 myActiveContainerPlan = nullptr;
834 }
835
836
837 void
closeFlow()838 MSRouteHandler::closeFlow() {
839 myInsertStopEdgesAt = -1;
840 if (myVehicleParameter->repetitionNumber == 0) {
841 delete myVehicleParameter;
842 myVehicleParameter = nullptr;
843 return;
844 }
845 // let's check whether vehicles had to depart before the simulation starts
846 myVehicleParameter->repetitionsDone = 0;
847 if (myVehicleParameter->repetitionProbability < 0) {
848 const SUMOTime offsetToBegin = string2time(OptionsCont::getOptions().getString("begin")) - myVehicleParameter->depart;
849 while (myVehicleParameter->repetitionsDone * myVehicleParameter->repetitionOffset < offsetToBegin) {
850 myVehicleParameter->repetitionsDone++;
851 if (myVehicleParameter->repetitionsDone == myVehicleParameter->repetitionNumber) {
852 delete myVehicleParameter;
853 myVehicleParameter = nullptr;
854 return;
855 }
856 }
857 }
858 if (MSNet::getInstance()->getVehicleControl().getVType(myVehicleParameter->vtypeid, &myParsingRNG) == nullptr) {
859 throw ProcessError("The vehicle type '" + myVehicleParameter->vtypeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
860 }
861 if (myVehicleParameter->routeid[0] == '!' && MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
862 myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
863 closeRoute(true);
864 }
865 if (MSRoute::dictionary(myVehicleParameter->routeid, &myParsingRNG) == nullptr) {
866 throw ProcessError("The route '" + myVehicleParameter->routeid + "' for flow '" + myVehicleParameter->id + "' is not known.");
867 }
868 myActiveRouteID = "";
869
870 // check whether the vehicle shall be added directly to the network or
871 // shall stay in the internal buffer
872 if (myAddVehiclesDirectly || checkLastDepart()) {
873 if (MSNet::getInstance()->getInsertionControl().addFlow(myVehicleParameter)) {
874 registerLastDepart();
875 } else {
876 throw ProcessError("Another flow with the id '" + myVehicleParameter->id + "' exists.");
877 }
878 }
879 myVehicleParameter = nullptr;
880 }
881
882
883 void
closeTrip()884 MSRouteHandler::closeTrip() {
885 myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
886 closeRoute(true);
887 closeVehicle();
888 }
889
890
891 void
addStop(const SUMOSAXAttributes & attrs)892 MSRouteHandler::addStop(const SUMOSAXAttributes& attrs) {
893 std::string errorSuffix;
894 if (myActivePlan != nullptr) {
895 errorSuffix = " in person '" + myVehicleParameter->id + "'.";
896 } else if (myVehicleParameter != nullptr) {
897 errorSuffix = " in vehicle '" + myVehicleParameter->id + "'.";
898 } else if (myActiveContainerPlan != nullptr) {
899 errorSuffix = " in container '" + myVehicleParameter->id + "'.";
900 } else {
901 errorSuffix = " in route '" + myActiveRouteID + "'.";
902 }
903 SUMOVehicleParameter::Stop stop;
904 bool ok = parseStop(stop, attrs, errorSuffix, MsgHandler::getErrorInstance());
905 if (!ok) {
906 return;
907 }
908 const MSEdge* edge = nullptr;
909 MSStoppingPlace* toStop = nullptr;
910 // try to parse the assigned bus stop
911 if (stop.busstop != "") {
912 // ok, we have a bus stop
913 MSStoppingPlace* bs = MSNet::getInstance()->getStoppingPlace(stop.busstop, SUMO_TAG_BUS_STOP);
914 if (bs == nullptr) {
915 WRITE_ERROR("The busStop '" + stop.busstop + "' is not known" + errorSuffix);
916 return;
917 }
918 toStop = bs;
919 const MSLane& l = bs->getLane();
920 stop.lane = l.getID();
921 stop.endPos = bs->getEndLanePosition();
922 stop.startPos = bs->getBeginLanePosition();
923 edge = &l.getEdge();
924 } //try to parse the assigned container stop
925 else if (stop.containerstop != "") {
926 // ok, we have obviously a container stop
927 MSStoppingPlace* cs = MSNet::getInstance()->getStoppingPlace(stop.containerstop, SUMO_TAG_CONTAINER_STOP);
928 if (cs == nullptr) {
929 WRITE_ERROR("The containerStop '" + stop.containerstop + "' is not known" + errorSuffix);
930 return;
931 }
932 toStop = cs;
933 const MSLane& l = cs->getLane();
934 stop.lane = l.getID();
935 stop.endPos = cs->getEndLanePosition();
936 stop.startPos = cs->getBeginLanePosition();
937 edge = &l.getEdge();
938 } //try to parse the assigned parking area
939 else if (stop.parkingarea != "") {
940 // ok, we have obviously a parking area
941 MSStoppingPlace* pa = MSNet::getInstance()->getStoppingPlace(stop.parkingarea, SUMO_TAG_PARKING_AREA);
942 if (pa == nullptr) {
943 WRITE_ERROR("The parkingArea '" + stop.parkingarea + "' is not known" + errorSuffix);
944 return;
945 }
946 toStop = pa;
947 const MSLane& l = pa->getLane();
948 stop.lane = l.getID();
949 stop.endPos = pa->getEndLanePosition();
950 stop.startPos = pa->getBeginLanePosition();
951 edge = &l.getEdge();
952 } else if (stop.chargingStation != "") {
953 // ok, we have a charging station
954 MSStoppingPlace* cs = MSNet::getInstance()->getStoppingPlace(stop.chargingStation, SUMO_TAG_CHARGING_STATION);
955 if (cs == nullptr) {
956 WRITE_ERROR("The chargingStation '" + stop.chargingStation + "' is not known" + errorSuffix);
957 return;
958 }
959 toStop = cs;
960 const MSLane& l = cs->getLane();
961 stop.lane = l.getID();
962 stop.endPos = cs->getEndLanePosition();
963 stop.startPos = cs->getBeginLanePosition();
964 edge = &l.getEdge();
965 } else {
966 // no, the lane and the position should be given
967 // get the lane
968 stop.lane = attrs.getOpt<std::string>(SUMO_ATTR_LANE, nullptr, ok, "");
969 if (ok && stop.lane != "") {
970 if (MSLane::dictionary(stop.lane) == nullptr) {
971 WRITE_ERROR("The lane '" + stop.lane + "' for a stop is not known" + errorSuffix);
972 return;
973 }
974 } else {
975 if (myActivePlan && !myActivePlan->empty()) {
976 const MSStoppingPlace* bs = myActivePlan->back()->getDestinationStop();
977 if (bs != nullptr) {
978 edge = &bs->getLane().getEdge();
979 stop.lane = bs->getLane().getID();
980 stop.endPos = bs->getEndLanePosition();
981 stop.startPos = bs->getBeginLanePosition();
982 } else {
983 edge = myActivePlan->back()->getDestination();
984 stop.lane = edge->getLanes()[0]->getID();
985 stop.endPos = myActivePlan->back()->getArrivalPos();
986 stop.startPos = stop.endPos - POSITION_EPS;
987 }
988 } else {
989 WRITE_ERROR("A stop must be placed on a busStop, a chargingStation, a containerStop a parkingArea or a lane" + errorSuffix);
990 return;
991 }
992 }
993 edge = &MSLane::dictionary(stop.lane)->getEdge();
994 stop.endPos = attrs.getOpt<double>(SUMO_ATTR_ENDPOS, nullptr, ok, MSLane::dictionary(stop.lane)->getLength());
995 if (attrs.hasAttribute(SUMO_ATTR_POSITION)) {
996 WRITE_WARNING("Deprecated attribute 'pos' in description of stop" + errorSuffix);
997 stop.endPos = attrs.getOpt<double>(SUMO_ATTR_POSITION, nullptr, ok, stop.endPos);
998 }
999 stop.startPos = attrs.getOpt<double>(SUMO_ATTR_STARTPOS, nullptr, ok, MAX2(0., stop.endPos - 2 * POSITION_EPS));
1000 const bool friendlyPos = attrs.getOpt<bool>(SUMO_ATTR_FRIENDLY_POS, nullptr, ok, false);
1001 if (!ok || !checkStopPos(stop.startPos, stop.endPos, MSLane::dictionary(stop.lane)->getLength(), POSITION_EPS, friendlyPos)) {
1002 WRITE_ERROR("Invalid start or end position for stop on lane '" + stop.lane + "'" + errorSuffix);
1003 return;
1004 }
1005 }
1006 if (myActivePlan) {
1007 if (myActivePlan->empty()) {
1008 double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
1009 ? myVehicleParameter->departPos
1010 : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
1011 myActivePlan->push_back(new MSTransportable::Stage_Waiting(
1012 edge, toStop, -1, myVehicleParameter->depart, departPos, "start", true));
1013 } else if (myActivePlan->back()->getDestination() != edge) {
1014 throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + edge->getID() + "!=" + myActivePlan->back()->getDestination()->getID() + ").");
1015 }
1016 std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "waiting");
1017 double pos = (stop.startPos + stop.endPos) / 2.;
1018 if (!myActivePlan->empty()) {
1019 pos = myActivePlan->back()->getArrivalPos();
1020 }
1021 myActivePlan->push_back(new MSTransportable::Stage_Waiting(
1022 &MSLane::dictionary(stop.lane)->getEdge(), toStop, stop.duration, stop.until, pos, actType, false));
1023
1024 } else if (myActiveContainerPlan) {
1025 if (myActiveContainerPlan->empty()) {
1026 double departPos = toStop == nullptr || myVehicleParameter->wasSet(VEHPARS_DEPARTPOS_SET)
1027 ? myVehicleParameter->departPos
1028 : (toStop->getBeginLanePosition() + toStop->getEndLanePosition()) / 2;
1029 myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
1030 &MSLane::dictionary(stop.lane)->getEdge(), toStop, -1, myVehicleParameter->depart, departPos, "start", true));
1031 } else if (myActiveContainerPlan->back()->getDestination() != &MSLane::dictionary(stop.lane)->getEdge()) {
1032 throw ProcessError("Disconnected plan for container '" + myVehicleParameter->id + "' (" + MSLane::dictionary(stop.lane)->getEdge().getID() + "!=" + myActiveContainerPlan->back()->getDestination()->getID() + ").");
1033 }
1034 std::string actType = attrs.getOpt<std::string>(SUMO_ATTR_ACTTYPE, nullptr, ok, "waiting");
1035 myActiveContainerPlan->push_back(new MSTransportable::Stage_Waiting(
1036 &MSLane::dictionary(stop.lane)->getEdge(), toStop, stop.duration, stop.until, stop.startPos, actType, false));
1037 } else if (myVehicleParameter != nullptr) {
1038 myVehicleParameter->stops.push_back(stop);
1039 } else {
1040 myActiveRouteStops.push_back(stop);
1041 }
1042 if (myInsertStopEdgesAt >= 0) {
1043 //std::cout << " myInsertStopEdgesAt=" << myInsertStopEdgesAt << " edge=" << edge->getID() << " myRoute=" << toString(myActiveRoute) << "\n";
1044 myActiveRoute.insert(myActiveRoute.begin() + myInsertStopEdgesAt, edge);
1045 myInsertStopEdgesAt++;
1046 }
1047 }
1048
1049
1050 void
parseWalkPositions(const SUMOSAXAttributes & attrs,const std::string & personID,const MSEdge * fromEdge,const MSEdge * & toEdge,double & departPos,double & arrivalPos,MSStoppingPlace * & bs,const MSTransportable::Stage * const lastStage,bool & ok)1051 MSRouteHandler::parseWalkPositions(const SUMOSAXAttributes& attrs, const std::string& personID,
1052 const MSEdge* fromEdge, const MSEdge*& toEdge,
1053 double& departPos, double& arrivalPos, MSStoppingPlace*& bs,
1054 const MSTransportable::Stage* const lastStage, bool& ok) {
1055 const std::string description = "person '" + personID + "' walking from " + fromEdge->getID();
1056
1057 if (attrs.hasAttribute(SUMO_ATTR_DEPARTPOS)) {
1058 WRITE_WARNING("The attribute departPos is no longer supported for walks, please use the person attribute, the arrivalPos of the previous step or explicit stops.");
1059 }
1060 departPos = 0.;
1061 if (lastStage != nullptr) {
1062 if (lastStage->getDestinationStop() != nullptr) {
1063 departPos = lastStage->getDestinationStop()->getAccessPos(fromEdge);
1064 } else if (lastStage->getDestination() == fromEdge) {
1065 departPos = lastStage->getArrivalPos();
1066 } else if (lastStage->getDestination()->getToJunction() == fromEdge->getToJunction()) {
1067 departPos = fromEdge->getLength();
1068 }
1069 }
1070
1071 std::string bsID = attrs.getOpt<std::string>(SUMO_ATTR_BUS_STOP, nullptr, ok, "");
1072 if (bsID != "") {
1073 bs = MSNet::getInstance()->getStoppingPlace(bsID, SUMO_TAG_BUS_STOP);
1074 if (bs == nullptr) {
1075 throw ProcessError("Unknown bus stop '" + bsID + "' for " + description + ".");
1076 }
1077 arrivalPos = bs->getAccessPos(toEdge != nullptr ? toEdge : &bs->getLane().getEdge());
1078 if (arrivalPos < 0) {
1079 throw ProcessError("Bus stop '" + bsID + "' is not connected to arrival edge '" + toEdge->getID() + "' for " + description + ".");
1080 }
1081 if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1082 const double length = toEdge != nullptr ? toEdge->getLength() : bs->getLane().getLength();
1083 const double arrPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, description, length,
1084 attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
1085 if (arrPos >= bs->getBeginLanePosition() && arrPos < bs->getEndLanePosition()) {
1086 arrivalPos = arrPos;
1087 } else {
1088 WRITE_WARNING("Ignoring arrivalPos for " + description + " because it is outside the given stop '" + toString(SUMO_ATTR_BUS_STOP) + "'.");
1089 arrivalPos = bs->getAccessPos(&bs->getLane().getEdge());
1090 }
1091 }
1092 } else {
1093 if (toEdge == nullptr) {
1094 throw ProcessError("No destination edge for " + description + ".");
1095 }
1096 if (attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS)) {
1097 arrivalPos = SUMOVehicleParserHelper::parseWalkPos(SUMO_ATTR_ARRIVALPOS, description, toEdge->getLength(),
1098 attrs.get<std::string>(SUMO_ATTR_ARRIVALPOS, description.c_str(), ok), &myParsingRNG);
1099 } else {
1100 arrivalPos = toEdge->getLength() / 2.;
1101 }
1102 }
1103 }
1104
1105
1106 void
addPersonTrip(const SUMOSAXAttributes & attrs)1107 MSRouteHandler::addPersonTrip(const SUMOSAXAttributes& attrs) {
1108 myActiveRoute.clear();
1109 bool ok = true;
1110 MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
1111 const char* const id = myVehicleParameter->id.c_str();
1112 const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, id, ok, -1);
1113 if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1114 throw ProcessError("Non-positive walking duration for '" + myVehicleParameter->id + "'.");
1115 }
1116 const std::string fromID = attrs.getOpt<std::string>(SUMO_ATTR_FROM, id, ok, "");
1117 const MSEdge* from = fromID != "" || myActivePlan->empty() ? MSEdge::dictionary(fromID) : myActivePlan->back()->getDestination();
1118 if (from == nullptr) {
1119 throw ProcessError("The from edge '" + fromID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
1120 }
1121 const std::string toID = attrs.getOpt<std::string>(SUMO_ATTR_TO, myVehicleParameter->id.c_str(), ok, "");
1122 const MSEdge* to = MSEdge::dictionary(toID);
1123 if (toID != "" && to == nullptr) {
1124 throw ProcessError("The to edge '" + toID + "' within a walk of person '" + myVehicleParameter->id + "' is not known.");
1125 }
1126 double departPos = 0;
1127 double arrivalPos = 0;
1128 MSStoppingPlace* stoppingPlace = nullptr;
1129 parseWalkPositions(attrs, myVehicleParameter->id, from, to, departPos, arrivalPos, stoppingPlace, nullptr, ok);
1130
1131 const std::string modes = attrs.getOpt<std::string>(SUMO_ATTR_MODES, id, ok, "");
1132 SVCPermissions modeSet = 0;
1133 for (StringTokenizer st(modes); st.hasNext();) {
1134 const std::string mode = st.next();
1135 if (mode == "car") {
1136 modeSet |= SVC_PASSENGER;
1137 } else if (mode == "bicycle") {
1138 modeSet |= SVC_BICYCLE;
1139 } else if (mode == "public") {
1140 modeSet |= SVC_BUS;
1141 } else {
1142 throw InvalidArgument("Unknown person mode '" + mode + "'.");
1143 }
1144 }
1145 const std::string types = attrs.getOpt<std::string>(SUMO_ATTR_VTYPES, id, ok, "");
1146 for (StringTokenizer st(types); st.hasNext();) {
1147 const std::string vtypeid = st.next();
1148 if (vehControl.getVType(vtypeid) == nullptr) {
1149 throw InvalidArgument("The vehicle type '" + vtypeid + "' in a trip for person '" + myVehicleParameter->id + "' is not known.");
1150 }
1151 modeSet |= SVC_PASSENGER;
1152 }
1153 const double speed = attrs.getOpt<double>(SUMO_ATTR_SPEED, id, ok, -1.);
1154 if (attrs.hasAttribute(SUMO_ATTR_SPEED) && speed <= 0) {
1155 throw ProcessError("Non-positive walking speed for '" + myVehicleParameter->id + "'.");
1156 }
1157 const double walkFactor = attrs.getOpt<double>(SUMO_ATTR_WALKFACTOR, id, ok, OptionsCont::getOptions().getFloat("persontrip.walkfactor"));
1158 const double departPosLat = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, 0);
1159 if (ok) {
1160 myVehicleParameter->parametersSet |= VEHPARS_FORCE_REROUTE;
1161 MSStoppingPlace* fromStop = myActivePlan->empty() ? nullptr : myActivePlan->back()->getDestinationStop();
1162 myActivePlan->push_back(new MSTransportable::Stage_Trip(from, fromStop, to == nullptr ? &stoppingPlace->getLane().getEdge() : to, stoppingPlace, duration, modeSet, types, speed, walkFactor, departPosLat, attrs.hasAttribute(SUMO_ATTR_ARRIVALPOS), arrivalPos));
1163 }
1164 myActiveRoute.clear();
1165 }
1166
1167
1168 void
addWalk(const SUMOSAXAttributes & attrs)1169 MSRouteHandler::addWalk(const SUMOSAXAttributes& attrs) {
1170 try {
1171 myActiveRoute.clear();
1172 bool ok = true;
1173 const SUMOTime duration = attrs.getOptSUMOTimeReporting(SUMO_ATTR_DURATION, nullptr, ok, -1);
1174 if (attrs.hasAttribute(SUMO_ATTR_DURATION) && duration <= 0) {
1175 throw ProcessError("Non-positive walking duration for '" + myVehicleParameter->id + "'.");
1176 }
1177 double speed = -1; // default to vType speed
1178 if (attrs.hasAttribute(SUMO_ATTR_SPEED)) {
1179 speed = attrs.get<double>(SUMO_ATTR_SPEED, nullptr, ok);
1180 if (speed <= 0) {
1181 throw ProcessError("Non-positive walking speed for '" + myVehicleParameter->id + "'.");
1182 }
1183 }
1184 double departPos = 0;
1185 double arrivalPos = 0;
1186 MSStoppingPlace* bs = nullptr;
1187 if (attrs.hasAttribute(SUMO_ATTR_ROUTE)) {
1188 const std::string routeID = attrs.get<std::string>(SUMO_ATTR_ROUTE, myVehicleParameter->id.c_str(), ok);
1189 const MSRoute* route = MSRoute::dictionary(routeID, &myParsingRNG);
1190 if (route == nullptr) {
1191 throw ProcessError("The route '" + routeID + "' for walk of person '" + myVehicleParameter->id + "' is not known.");
1192 }
1193 myActiveRoute = route->getEdges();
1194 } else {
1195 MSEdge::parseEdgesList(attrs.get<std::string>(SUMO_ATTR_EDGES, myVehicleParameter->id.c_str(), ok), myActiveRoute, myActiveRouteID);
1196 }
1197 if (myActivePlan->empty()) {
1198 double initialDepartPos = myVehicleParameter->departPos;
1199 if (myVehicleParameter->departPosProcedure == DEPART_POS_RANDOM) {
1200 initialDepartPos = RandHelper::rand(myActiveRoute.front()->getLength(), &myParsingRNG);
1201 }
1202 myActivePlan->push_back(new MSTransportable::Stage_Waiting(myActiveRoute.front(), nullptr, -1, myVehicleParameter->depart, initialDepartPos, "start", true));
1203 }
1204 parseWalkPositions(attrs, myVehicleParameter->id, myActiveRoute.front(), myActiveRoute.back(), departPos, arrivalPos, bs, myActivePlan->back(), ok);
1205 if (myActiveRoute.empty()) {
1206 throw ProcessError("No edges to walk for person '" + myVehicleParameter->id + "'.");
1207 }
1208 if (myActivePlan->back()->getDestination() != myActiveRoute.front() &&
1209 myActivePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getFromJunction() &&
1210 myActivePlan->back()->getDestination()->getToJunction() != myActiveRoute.front()->getToJunction()) {
1211 if (myActivePlan->back()->getDestinationStop() == nullptr || myActivePlan->back()->getDestinationStop()->getAccessPos(myActiveRoute.front()) < 0.) {
1212 throw ProcessError("Disconnected plan for person '" + myVehicleParameter->id + "' (" + myActiveRoute.front()->getID() + " not connected to " + myActivePlan->back()->getDestination()->getID() + ").");
1213 }
1214 }
1215 const double departPosLat = attrs.getOpt<double>(SUMO_ATTR_DEPARTPOS_LAT, nullptr, ok, 0);
1216 myActivePlan->push_back(new MSPerson::MSPersonStage_Walking(myVehicleParameter->id, myActiveRoute, bs, duration, speed, departPos, arrivalPos, departPosLat));
1217 myActiveRoute.clear();
1218 } catch (ProcessError&) {
1219 deleteActivePlans();
1220 throw;
1221 }
1222 }
1223
1224
1225 void
addPerson(const SUMOSAXAttributes &)1226 MSRouteHandler::addPerson(const SUMOSAXAttributes& /*attrs*/) {
1227 }
1228
1229
1230 void
addContainer(const SUMOSAXAttributes &)1231 MSRouteHandler::addContainer(const SUMOSAXAttributes& /*attrs*/) {
1232 }
1233
1234
1235 void
addRide(const SUMOSAXAttributes &)1236 MSRouteHandler::addRide(const SUMOSAXAttributes& /*attrs*/) {
1237 }
1238
1239
1240 void
addTransport(const SUMOSAXAttributes &)1241 MSRouteHandler::addTransport(const SUMOSAXAttributes& /*attrs*/) {
1242 }
1243
1244
1245 void
addTranship(const SUMOSAXAttributes &)1246 MSRouteHandler::addTranship(const SUMOSAXAttributes& /*attrs*/) {
1247 }
1248
1249 /****************************************************************************/
1250