Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2013-2026 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file MSDevice_Taxi.cpp
15 : /// @author Jakob Erdmann
16 : /// @date 16.12.2019
17 : ///
18 : // A device which controls a taxi
19 : /****************************************************************************/
20 : #include <config.h>
21 :
22 : #include <utils/common/StringUtils.h>
23 : #include <utils/common/StaticCommand.h>
24 : #include <utils/common/StringTokenizer.h>
25 : #include <utils/options/OptionsCont.h>
26 : #include <utils/iodevices/OutputDevice.h>
27 : #include <utils/vehicle/SUMOVehicle.h>
28 : #include <utils/router/SUMOAbstractRouter.h>
29 : #include <microsim/transportables/MSTransportable.h>
30 : #include <microsim/MSEventControl.h>
31 : #include <microsim/MSGlobals.h>
32 : #include <microsim/MSVehicle.h>
33 : #include <microsim/MSEdge.h>
34 : #include <microsim/MSLane.h>
35 : #include <microsim/MSStop.h>
36 : #include <microsim/MSStoppingPlace.h>
37 : #include <microsim/trigger/MSTriggeredRerouter.h>
38 :
39 : #include "MSDispatch.h"
40 : #include "MSDispatch_Greedy.h"
41 : #include "MSDispatch_GreedyShared.h"
42 : #include "MSDispatch_RouteExtension.h"
43 : #include "MSDispatch_TraCI.h"
44 :
45 : #include "MSIdling.h"
46 :
47 : #include "MSRoutingEngine.h"
48 : #include "MSDevice_Routing.h"
49 : #include "MSDevice_Taxi.h"
50 :
51 : //#define DEBUG_DISPATCH
52 : //#define DEBUG_CANCEL
53 :
54 : //#define DEBUG_COND (myHolder.isSelected())
55 : #define DEBUG_COND (true)
56 :
57 : // ===========================================================================
58 : // static member variables
59 : // ===========================================================================
60 : SUMOTime MSDevice_Taxi::myDispatchPeriod(0);
61 : /// @brief the dispatch algorithm
62 : MSDispatch* MSDevice_Taxi::myDispatcher(nullptr);
63 : /// @brief The repeated call to the dispatcher
64 : Command* MSDevice_Taxi::myDispatchCommand(nullptr);
65 : // @brief the list of available taxis
66 : std::vector<MSDevice_Taxi*> MSDevice_Taxi::myFleet;
67 : int MSDevice_Taxi::myMaxCapacity(0);
68 : int MSDevice_Taxi::myMaxContainerCapacity(0);
69 : std::set<std::string> MSDevice_Taxi::myVClassWarningVTypes;
70 :
71 : #define TAXI_SERVICE "taxi"
72 : #define TAXI_SERVICE_PREFIX "taxi:"
73 : #define SWAP_THRESHOLD 5
74 :
75 : // ===========================================================================
76 : // method definitions
77 : // ===========================================================================
78 : // ---------------------------------------------------------------------------
79 : // static initialisation methods
80 : // ---------------------------------------------------------------------------
81 : void
82 42243 : MSDevice_Taxi::insertOptions(OptionsCont& oc) {
83 42243 : oc.addOptionSubTopic("Taxi Device");
84 84486 : insertDefaultAssignmentOptions("taxi", "Taxi Device", oc);
85 :
86 84486 : oc.doRegister("device.taxi.dispatch-algorithm", new Option_String("greedy"));
87 84486 : oc.addDescription("device.taxi.dispatch-algorithm", "Taxi Device", TL("The dispatch algorithm [greedy|greedyClosest|greedyShared|routeExtension|traci]"));
88 :
89 42243 : oc.doRegister("device.taxi.dispatch-algorithm.output", new Option_FileName());
90 84486 : oc.addDescription("device.taxi.dispatch-algorithm.output", "Taxi Device", TL("Write information from the dispatch algorithm to FILE"));
91 :
92 84486 : oc.doRegister("device.taxi.dispatch-algorithm.params", new Option_String(""));
93 84486 : oc.addDescription("device.taxi.dispatch-algorithm.params", "Taxi Device", TL("Load dispatch algorithm parameters in format KEY1:VALUE1[,KEY2:VALUE]"));
94 :
95 84486 : oc.doRegister("device.taxi.dispatch-period", new Option_String("60", "TIME"));
96 84486 : oc.addDescription("device.taxi.dispatch-period", "Taxi Device", TL("The period between successive calls to the dispatcher"));
97 :
98 84486 : oc.doRegister("device.taxi.dispatch-keep-unreachable", new Option_String("3600", "TIME"));
99 84486 : oc.addDescription("device.taxi.dispatch-keep-unreachable", "Taxi Device", TL("The time before aborting unreachable reservations"));
100 :
101 84486 : oc.doRegister("device.taxi.idle-algorithm", new Option_String("stop"));
102 84486 : oc.addDescription("device.taxi.idle-algorithm", "Taxi Device", TL("The behavior of idle taxis [stop|randomCircling|taxistand]"));
103 :
104 42243 : oc.doRegister("device.taxi.idle-algorithm.output", new Option_FileName());
105 84486 : oc.addDescription("device.taxi.idle-algorithm.output", "Taxi Device", TL("Write information from the idling algorithm to FILE"));
106 42243 : }
107 :
108 :
109 : void
110 5537919 : MSDevice_Taxi::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
111 5537919 : OptionsCont& oc = OptionsCont::getOptions();
112 11075838 : if (equippedByDefaultAssignmentOptions(oc, "taxi", v, false)) {
113 : // build the device
114 6733 : MSDevice_Taxi* device = new MSDevice_Taxi(v, "taxi_" + v.getID());
115 6721 : into.push_back(device);
116 6721 : myFleet.push_back(device);
117 6721 : if (v.getParameter().line == "") {
118 : // automatically set the line so that persons are willing to enter
119 : // (see MSStageDriving::isWaitingFor)
120 6624 : const_cast<SUMOVehicleParameter&>(v.getParameter()).line = TAXI_SERVICE;
121 : }
122 6721 : if (v.getVClass() != SVC_TAXI && myVClassWarningVTypes.count(v.getVehicleType().getID()) == 0) {
123 180 : WRITE_WARNINGF(TL("Vehicle '%' with device.taxi should have vClass taxi instead of '%'."), v.getID(), toString(v.getVClass()));
124 60 : myVClassWarningVTypes.insert(v.getVehicleType().getID());
125 : }
126 6721 : const int personCapacity = v.getVehicleType().getPersonCapacity();
127 6721 : const int containerCapacity = v.getVehicleType().getContainerCapacity();
128 6721 : myMaxCapacity = MAX2(myMaxCapacity, personCapacity);
129 6721 : myMaxContainerCapacity = MAX2(myMaxContainerCapacity, containerCapacity);
130 6721 : if (personCapacity < 1 && containerCapacity < 1) {
131 0 : WRITE_WARNINGF(TL("Vehicle '%' with personCapacity % and containerCapacity % is not usable as taxi."), v.getID(), toString(personCapacity), toString(containerCapacity));
132 : }
133 : }
134 5537913 : }
135 :
136 :
137 : void
138 815 : MSDevice_Taxi::initDispatch() {
139 815 : OptionsCont& oc = OptionsCont::getOptions();
140 815 : myDispatchPeriod = string2time(oc.getString("device.taxi.dispatch-period"));
141 : // init dispatch algorithm
142 815 : std::string algo = oc.getString("device.taxi.dispatch-algorithm");
143 815 : Parameterised params;
144 1630 : params.setParametersStr(OptionsCont::getOptions().getString("device.taxi.dispatch-algorithm.params"), ":", ",");
145 815 : if (algo == "greedy") {
146 510 : myDispatcher = new MSDispatch_Greedy(params.getParametersMap());
147 305 : } else if (algo == "greedyClosest") {
148 36 : myDispatcher = new MSDispatch_GreedyClosest(params.getParametersMap());
149 287 : } else if (algo == "greedyShared") {
150 114 : myDispatcher = new MSDispatch_GreedyShared(params.getParametersMap());
151 173 : } else if (algo == "routeExtension") {
152 122 : myDispatcher = new MSDispatch_RouteExtension(params.getParametersMap());
153 112 : } else if (algo == "traci") {
154 224 : myDispatcher = new MSDispatch_TraCI(params.getParametersMap());
155 : } else {
156 0 : throw ProcessError(TLF("Dispatch algorithm '%' is not known", algo));
157 : }
158 815 : myDispatchCommand = new StaticCommand<MSDevice_Taxi>(&MSDevice_Taxi::triggerDispatch);
159 : // round to next multiple of myDispatchPeriod
160 815 : const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
161 815 : const SUMOTime begin = string2time(oc.getString("begin"));
162 815 : const SUMOTime delay = (myDispatchPeriod - ((now - begin) % myDispatchPeriod)) % myDispatchPeriod;
163 815 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myDispatchCommand, now + delay);
164 1630 : }
165 :
166 : bool
167 77967 : MSDevice_Taxi::isReservation(const std::set<std::string>& lines) {
168 77967 : return lines.size() == 1 && (
169 77953 : *lines.begin() == TAXI_SERVICE
170 150348 : || StringUtils::startsWith(*lines.begin(), TAXI_SERVICE_PREFIX));
171 : }
172 :
173 : void
174 2846 : MSDevice_Taxi::addReservation(MSTransportable* person,
175 : const std::set<std::string>& lines,
176 : SUMOTime reservationTime,
177 : SUMOTime pickupTime,
178 : SUMOTime earliestPickupTime,
179 : const MSEdge* from, double fromPos,
180 : const MSStoppingPlace* fromStop,
181 : const MSEdge* to, double toPos,
182 : const MSStoppingPlace* toStop,
183 : const std::string& group) {
184 2846 : if (!isReservation(lines)) {
185 : return;
186 : }
187 2846 : if ((to->getPermissions() & SVC_TAXI) == 0) {
188 0 : throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
189 0 : + " '" + person->getID() + "' because destination edge '" + to->getID() + "'"
190 0 : + " does not permit taxi access");
191 : }
192 2846 : if ((from->getPermissions() & SVC_TAXI) == 0) {
193 0 : throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
194 0 : + " '" + person->getID() + "' because origin edge '" + from->getID() + "'"
195 0 : + " does not permit taxi access");
196 : }
197 2846 : if (myDispatchCommand == nullptr) {
198 815 : initDispatch();
199 : }
200 2846 : if (fromStop != nullptr && &fromStop->getLane().getEdge() == from) {
201 : // pickup position should be at the stop-endPos
202 888 : fromPos = fromStop->getEndLanePosition();
203 : }
204 5692 : myDispatcher->addReservation(person, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, *lines.begin(), myMaxCapacity, myMaxContainerCapacity);
205 : }
206 :
207 : void
208 93 : MSDevice_Taxi::removeReservation(MSTransportable* person,
209 : const std::set<std::string>& lines,
210 : const MSEdge* from, double fromPos,
211 : const MSEdge* to, double toPos,
212 : const std::string& group) {
213 93 : if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
214 154 : myDispatcher->removeReservation(person, from, fromPos, to, toPos, group);
215 : }
216 93 : }
217 :
218 : void
219 162 : MSDevice_Taxi::updateReservationFromPos(MSTransportable* person,
220 : const std::set<std::string>& lines,
221 : const MSEdge* from, double fromPos,
222 : const MSEdge* to, double toPos,
223 : const std::string& group, double newFromPos) {
224 162 : if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
225 300 : myDispatcher->updateReservationFromPos(person, from, fromPos, to, toPos, group, newFromPos);
226 : }
227 162 : }
228 :
229 :
230 : SUMOTime
231 15222 : MSDevice_Taxi::triggerDispatch(SUMOTime currentTime) {
232 : std::vector<MSDevice_Taxi*> active;
233 44165 : for (MSDevice_Taxi* taxi : myFleet) {
234 28943 : if (taxi->getHolder().hasDeparted()) {
235 28841 : active.push_back(taxi);
236 : }
237 : }
238 15222 : myDispatcher->computeDispatch(currentTime, active);
239 15220 : return myDispatchPeriod;
240 15222 : }
241 :
242 : bool
243 8734243 : MSDevice_Taxi::hasServableReservations() {
244 8734243 : return myDispatcher != nullptr && myDispatcher->hasServableReservations();
245 : }
246 :
247 : void
248 40907 : MSDevice_Taxi::cleanup() {
249 40907 : if (myDispatcher != nullptr) {
250 815 : delete myDispatcher;
251 815 : myDispatcher = nullptr;
252 : }
253 40907 : myDispatchCommand = nullptr;
254 : myVClassWarningVTypes.clear();
255 40907 : }
256 :
257 :
258 : void
259 591 : MSDevice_Taxi::allCustomersErased() {
260 1480 : for (MSDevice_Taxi* taxi : myFleet) {
261 : // disable taskSwap
262 889 : taxi->myState = EMPTY;
263 : }
264 591 : }
265 :
266 : // ---------------------------------------------------------------------------
267 : // MSDevice_Taxi-methods
268 : // ---------------------------------------------------------------------------
269 6727 : MSDevice_Taxi::MSDevice_Taxi(SUMOVehicle& holder, const std::string& id) :
270 6727 : MSVehicleDevice(holder, id) {
271 6727 : std::string defaultServiceEnd = toString(1e15);
272 13460 : const std::string algo = holder.getStringParam("device.taxi.idle-algorithm");
273 6727 : if (algo == "stop") {
274 6429 : myIdleAlgorithm = new MSIdling_Stop();
275 298 : } else if (algo == "randomCircling") {
276 94 : myIdleAlgorithm = new MSIdling_RandomCircling();
277 : // make sure simulation terminates
278 94 : defaultServiceEnd = toString(STEPS2TIME(
279 : myHolder.getParameter().departProcedure == DepartDefinition::GIVEN
280 : ? myHolder.getParameter().depart
281 188 : : MSNet::getInstance()->getCurrentTimeStep()) + (3600 * 8));
282 204 : } else if (algo == "taxistand") {
283 396 : const std::string rerouterID = holder.getStringParam("device.taxi.stands-rerouter");
284 198 : if (rerouterID.empty()) {
285 0 : throw ProcessError("Idle algorithm '" + algo + "' requires a rerouter id to be defined using device param 'stands-rerouter' for vehicle '" + myHolder.getID() + "'");
286 : }
287 : if (MSTriggeredRerouter::getInstances().count(rerouterID) == 0) {
288 0 : throw ProcessError("Unknown rerouter '" + rerouterID + "' when loading taxi stands for vehicle '" + myHolder.getID() + "'");
289 : }
290 198 : MSTriggeredRerouter* rerouter = MSTriggeredRerouter::getInstances().find(rerouterID)->second;
291 198 : myIdleAlgorithm = new MSIdling_TaxiStand(rerouter);
292 : } else {
293 18 : throw ProcessError("Idle algorithm '" + algo + "' is not known for vehicle '" + myHolder.getID() + "'");
294 : }
295 6727 : myServiceEnd = string2time(holder.getStringParam("device.taxi.end", false, defaultServiceEnd));
296 6721 : myRoutingDevice = static_cast<MSDevice_Routing*>(myHolder.getDevice(typeid(MSDevice_Routing)));
297 6727 : }
298 :
299 :
300 13442 : MSDevice_Taxi::~MSDevice_Taxi() {
301 6721 : myFleet.erase(std::find(myFleet.begin(), myFleet.end(), this));
302 : // recompute myMaxCapacity
303 6721 : myMaxCapacity = 0;
304 6721 : myMaxContainerCapacity = 0;
305 17282 : for (MSDevice_Taxi* taxi : myFleet) {
306 10561 : myMaxCapacity = MAX2(myMaxCapacity, taxi->getHolder().getVehicleType().getPersonCapacity());
307 10561 : myMaxContainerCapacity = MAX2(myMaxContainerCapacity, taxi->getHolder().getVehicleType().getContainerCapacity());
308 : }
309 6721 : delete myIdleAlgorithm;
310 13442 : }
311 :
312 :
313 : bool
314 11404 : MSDevice_Taxi::hasFleet() {
315 11404 : return myFleet.size() > 0;;
316 : }
317 :
318 :
319 : void
320 1315 : MSDevice_Taxi::dispatch(const Reservation& res) {
321 1315 : dispatchShared({&res, &res});
322 1313 : }
323 :
324 :
325 : void
326 1660 : MSDevice_Taxi::dispatchShared(std::vector<const Reservation*> reservations) {
327 : #ifdef DEBUG_DISPATCH
328 : if (DEBUG_COND) {
329 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " dispatch:\n";
330 : for (const Reservation* res : reservations) {
331 : std::cout << " persons=" << toString(res->persons) << "\n";
332 : }
333 : }
334 : #endif
335 1660 : myLastDispatch = reservations;
336 : ConstMSEdgeVector tmpEdges;
337 : StopParVector stops;
338 1660 : double lastPos = myHolder.getPositionOnLane();
339 1660 : const MSEdge* rerouteOrigin = *myHolder.getRerouteOrigin();
340 1660 : if (isEmpty()) {
341 : // start fresh from the current edge
342 1569 : if (myHolder.isStoppedParking()) {
343 : // parking stop must be ended normally
344 860 : MSStop& stop = myHolder.getNextStopMutable();
345 860 : stop.duration = 0;
346 860 : lastPos = stop.pars.endPos;
347 860 : if (myHolder.isStoppedTriggered()) {
348 748 : stop.triggered = false;
349 748 : stop.containerTriggered = false;
350 748 : stop.joinTriggered = false;
351 748 : myHolder.unregisterWaiting();
352 : }
353 : // prevent unauthorized/premature entry
354 860 : const_cast<SUMOVehicleParameter::Stop&>(stop.pars).permitted.insert("");
355 884 : while (myHolder.getStops().size() > 1) {
356 24 : myHolder.abortNextStop(1);
357 : }
358 : } else {
359 1333 : while (myHolder.hasStops()) {
360 : // in meso there might be more than 1 stop at this point
361 624 : myHolder.abortNextStop();
362 : }
363 : assert(!myHolder.hasStops());
364 : }
365 1569 : tmpEdges.push_back(myHolder.getEdge());
366 1569 : if (myHolder.getEdge() != rerouteOrigin) {
367 8 : tmpEdges.push_back(rerouteOrigin);
368 : }
369 : } else {
370 : assert(myHolder.hasStops());
371 : // check how often existing customers appear in the new reservations
372 : std::map<const MSTransportable*, int> nOccur;
373 629 : for (const Reservation* res : reservations) {
374 1076 : for (const MSTransportable* person : res->persons) {
375 : if (myCustomers.count(person) != 0) {
376 298 : nOccur[person] += 1;
377 : if (myCurrentReservations.count(res) == 0) {
378 0 : throw ProcessError(TLF("Invalid Re-dispatch for existing customer '%' with a new reservation", person->getID()));
379 : }
380 : }
381 : }
382 : }
383 : #ifdef DEBUG_DISPATCH
384 : if (DEBUG_COND) {
385 : for (auto item : nOccur) {
386 : std::cout << " previousCustomer=" << item.first->getID() << " occurs=" << item.second << "\n";
387 : }
388 : }
389 : #endif
390 91 : if (nOccur.size() == 0) {
391 : // no overlap with existing customers - extend route
392 10 : tmpEdges = myHolder.getRoute().getEdges();
393 10 : lastPos = myHolder.getStops().back().pars.endPos;
394 : #ifdef DEBUG_DISPATCH
395 : if (DEBUG_COND) {
396 : std::cout << " re-dispatch with route-extension\n";
397 : }
398 : #endif
399 81 : } else if (nOccur.size() == myCustomers.size()) {
400 : // redefine route (verify correct number of mentions)
401 : std::set<const MSTransportable*> onBoard;
402 81 : const std::vector<MSTransportable*>& onBoardP = myHolder.getPersons();
403 81 : const std::vector<MSTransportable*>& onBoardC = myHolder.getContainers();
404 : onBoard.insert(onBoardP.begin(), onBoardP.end());
405 : onBoard.insert(onBoardC.begin(), onBoardC.end());
406 : std::set<const MSTransportable*> redundantPickup;
407 244 : for (auto item : nOccur) {
408 167 : if (item.second == 1) {
409 : // customers must already be on board
410 : if (onBoard.count(item.first) == 0) {
411 12 : throw ProcessError(TLF("Re-dispatch did not mention pickup for existing customer '%'", item.first->getID()));
412 : }
413 131 : } else if (item.second == 2) {
414 : if (onBoard.count(item.first) == 0) {
415 : // treat like a new customer
416 : // TODO: need to be checked
417 : myCustomers.erase(item.first);
418 : } else {
419 : redundantPickup.insert(item.first);
420 : }
421 : } else {
422 0 : throw ProcessError("Re-dispatch mentions existing customer '" + item.first->getID() + "' " + toString(item.second) + " times");
423 : }
424 : }
425 : // remove redundancy
426 77 : if (!redundantPickup.empty()) {
427 183 : for (auto it = reservations.begin(); it != reservations.end();) {
428 : bool isRedundant = false;
429 262 : for (const MSTransportable* person : (*it)->persons) {
430 : if (redundantPickup.count(person) != 0) {
431 : isRedundant = true;
432 : break;
433 : }
434 : }
435 154 : if (isRedundant) {
436 92 : for (const MSTransportable* person : (*it)->persons) {
437 : redundantPickup.erase(person);
438 : }
439 : it = reservations.erase(it);
440 : } else {
441 : it++;
442 : }
443 : }
444 : }
445 317 : while (myHolder.hasStops()) {
446 240 : myHolder.abortNextStop();
447 : }
448 81 : tmpEdges.push_back(myHolder.getEdge());
449 77 : if (myHolder.getEdge() != rerouteOrigin) {
450 10 : tmpEdges.push_back(rerouteOrigin);
451 : }
452 : #ifdef DEBUG_DISPATCH
453 : if (DEBUG_COND) {
454 : std::cout << " re-dispatch from scratch\n";
455 : }
456 : #endif
457 : } else {
458 : // inconsistent re-dispatch
459 : std::vector<std::string> missing;
460 0 : for (const MSTransportable* c : myCustomers) {
461 : if (nOccur.count(c) == 0) {
462 0 : missing.push_back(c->getID());
463 : }
464 : }
465 0 : throw ProcessError("Re-dispatch did mention some customers but failed to mention " + joinToStringSorting(missing, " "));
466 0 : }
467 : }
468 :
469 1656 : const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
470 : bool hasPickup = false;
471 6374 : for (const Reservation* res : reservations) {
472 : myCurrentReservations.insert(res);
473 : bool isPickup = false;
474 9660 : for (const MSTransportable* person : res->persons) {
475 : if (myCustomers.count(person) == 0) {
476 : myCustomers.insert(person);
477 : isPickup = true;
478 : hasPickup = true;
479 : }
480 : }
481 4718 : if (isPickup) {
482 6960 : prepareStop(tmpEdges, stops, lastPos, res->from, res->fromPos, res->fromStop, "pickup " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
483 4752 : for (const MSTransportable* const transportable : res->persons) {
484 2432 : if (transportable->isPerson()) {
485 2258 : stops.back().triggered = true;
486 : } else {
487 174 : stops.back().containerTriggered = true;
488 : }
489 : stops.back().permitted.insert(transportable->getID());
490 : }
491 : // proof this lines: Is needed for pre-booking?
492 : std::set<const MSTransportable*> persons = res->persons;
493 4752 : for (auto itr = persons.begin(); itr != persons.end(); itr++) {
494 2432 : stops.back().awaitedPersons.insert((*itr)->getID());
495 : }
496 :
497 2320 : stops.back().parametersSet |= STOP_PERMITTED_SET;
498 2320 : if (stops.back().duration == -1) {
499 : // keep dropOffDuration if the stop is dropOff and pickUp
500 1966 : stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.pickUpDuration", false, 0));
501 : }
502 : } else {
503 4796 : prepareStop(tmpEdges, stops, lastPos, res->to, res->toPos, res->toStop, "dropOff " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
504 2398 : stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.dropOffDuration", false, 60)); // pay and collect bags
505 : }
506 : }
507 : #ifdef DEBUG_DISPATCH
508 : if (DEBUG_COND) {
509 : std::cout << " tmpEdges=" << toString(tmpEdges) << "\n";
510 : }
511 : #endif
512 3312 : if (!myHolder.replaceRouteEdges(tmpEdges, -1, 0, "taxi:prepare_dispatch", false, false, false)) {
513 0 : throw ProcessError("Route replacement for taxi dispatch failed for vehicle '" + myHolder.getID()
514 0 : + "' at time=" + time2string(t) + ".");
515 : }
516 : #ifdef DEBUG_DISPATCH
517 : if (DEBUG_COND) std::cout << " replacedRoute=" << toString(tmpEdges)
518 : << "\n actualRoute=" << toString(myHolder.getRoute().getEdges()) << "\n";
519 : #endif
520 5519 : for (SUMOVehicleParameter::Stop& stop : stops) {
521 : std::string error;
522 3863 : myHolder.addStop(stop, error);
523 3863 : if (error != "") {
524 0 : WRITE_WARNINGF(TL("Could not add taxi stop, time=%, error=%"), myHolder.getID(), stop.actType, time2string(t), error)
525 : }
526 : }
527 1656 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
528 : // SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myHolder.getInfluencer().getRouterTT(veh->getRNGIndex())
529 1656 : myHolder.reroute(t, "taxi:dispatch", router, false);
530 : #ifdef DEBUG_DISPATCH
531 : if (DEBUG_COND) {
532 : std::cout << "\n finalRoute=" << toString(myHolder.getRoute().getEdges()) << " routeIndex=" << myHolder.getRoutePosition() << "\n";
533 : }
534 : #endif
535 1654 : if (hasPickup) {
536 1622 : myState |= PICKUP;
537 : }
538 1666 : }
539 :
540 :
541 : void
542 88 : MSDevice_Taxi::cancelCurrentCustomers() {
543 : // check if taxi has stopped
544 88 : if (myHolder.getNextStopParameter() == nullptr) {
545 0 : return;
546 : }
547 : // find customers of the current stop
548 : std::set<const MSTransportable*> customersToBeRemoved;
549 : std::set<const MSTransportable*> onBoard;
550 88 : onBoard.insert(myHolder.getPersons().begin(), myHolder.getPersons().end());
551 88 : onBoard.insert(myHolder.getContainers().begin(), myHolder.getContainers().end());
552 142 : for (std::string tID : myHolder.getNextStopParameter()->permitted) {
553 220 : for (auto t : myCustomers) {
554 166 : if (t->getID() == tID && onBoard.count(t) == 0) {
555 : customersToBeRemoved.insert(t);
556 : }
557 : }
558 : }
559 88 : if (!customersToBeRemoved.empty()) {
560 90 : WRITE_WARNINGF(TL("Taxi '%' aborts waiting for customers: % at time=%."),
561 : myHolder.getID(), toString(customersToBeRemoved), time2string(SIMSTEP));
562 : }
563 122 : for (auto t : customersToBeRemoved) {
564 34 : cancelCustomer(t);
565 : }
566 : }
567 :
568 :
569 : bool
570 44 : MSDevice_Taxi::cancelCustomer(const MSTransportable* t) {
571 : #ifdef DEBUG_CANCEL
572 : if (DEBUG_COND) {
573 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " cancelCustomer " << t->getID() << "\n";
574 : }
575 : #endif
576 :
577 : // is the given transportable a customer of the reservations?
578 : if (myCustomers.count(t) == 0) {
579 : return false;
580 : }
581 : myCustomers.erase(t);
582 : // check whether a single reservation has been fulfilled or another customer is part of the reservation
583 132 : for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
584 : bool fulfilled = false;
585 88 : if ((*resIt)->persons.size() == 1 && (*resIt)->persons.count(t) != 0) {
586 : // the reservation contains only the customer
587 : fulfilled = true;
588 : }
589 : if (fulfilled) {
590 : const Reservation* res = *resIt;
591 : // remove reservation from the current dispatch
592 220 : for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
593 176 : if (*it == res) {
594 88 : it = myLastDispatch.erase(it);
595 : } else {
596 : ++it;
597 : }
598 : }
599 : // remove reservation from the served reservations
600 : resIt = myCurrentReservations.erase(resIt);
601 : // delete the reservation
602 44 : myDispatcher->fulfilledReservation(res);
603 : } else {
604 : ++resIt;
605 : }
606 : }
607 44 : myState &= ~PICKUP; // remove state PICKUP
608 88 : for (const Reservation* res : myCurrentReservations) {
609 : // if there is another pickup in the dispatch left, add the state PICKUP
610 44 : if (std::count(myLastDispatch.begin(), myLastDispatch.end(), res) == 2) {
611 44 : myState |= PICKUP; // add state PICKUP
612 : }
613 : }
614 : // we also have to clean reservations from myLastDispatch where the customers arrived in the meantime
615 132 : for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
616 : if (myCurrentReservations.count(*it) == 0) {
617 0 : it = myLastDispatch.erase(it);
618 : } else {
619 : ++it;
620 : }
621 : }
622 : // if there are reservations left, go on with the dispatch
623 : // in meso, wait for the next dispatch cycle to avoid updating stops in this stage
624 44 : if (!MSGlobals::gUseMesoSim) {
625 36 : dispatchShared(myLastDispatch);
626 : }
627 : return true;
628 : }
629 :
630 :
631 : void
632 58 : MSDevice_Taxi::addCustomer(const MSTransportable* t, const Reservation* res) {
633 : myCustomers.insert(t);
634 58 : MSBaseVehicle& veh = dynamic_cast<MSBaseVehicle&>(myHolder);
635 116 : for (const MSStop& stop : veh.getStops()) {
636 : SUMOVehicleParameter::Stop& pars = const_cast<SUMOVehicleParameter::Stop&>(stop.pars);
637 : //std::cout << " sE=" << (*stop.edge)->getID() << " sStart=" << pars.startPos << " sEnd=" << pars.endPos << " rFrom=" <<
638 : // res->from->getID() << " rTo=" << res->to->getID() << " rFromPos=" << res->fromPos << " resToPos=" << res->toPos << "\n";
639 116 : if (*stop.edge == res->from
640 58 : && pars.startPos <= res->fromPos
641 174 : && pars.endPos >= res->fromPos) {
642 58 : pars.awaitedPersons.insert(t->getID());
643 : pars.permitted.insert(t->getID());
644 116 : pars.actType += " +" + t->getID();
645 58 : } else if (*stop.edge == res->to
646 58 : && pars.startPos <= res->toPos
647 116 : && pars.endPos >= res->toPos) {
648 58 : pars.actType += " +" + t->getID();
649 58 : break;
650 : }
651 : }
652 58 : }
653 :
654 :
655 : void
656 4718 : MSDevice_Taxi::prepareStop(ConstMSEdgeVector& edges,
657 : StopParVector& stops,
658 : double& lastPos, const MSEdge* stopEdge, double stopPos,
659 : const MSStoppingPlace* stopPlace,
660 : const std::string& action, const Reservation* res, const bool isPickup) {
661 : assert(!edges.empty());
662 4718 : if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
663 863 : stopPos = stopPlace->getEndLanePosition();
664 : }
665 4718 : if (stopPos < lastPos && stopPos + NUMERICAL_EPS >= lastPos) {
666 : stopPos = lastPos;
667 : }
668 : bool addedEdge = false;
669 :
670 4718 : if (stops.empty()) {
671 : // check brakeGap
672 1636 : double distToStop = stopPos - lastPos;
673 1636 : const double brakeGap = myHolder.getBrakeGap();
674 1636 : if (myHolder.getLane() != nullptr && myHolder.getLane()->isInternal()) {
675 4 : distToStop += myHolder.getLane()->getLength();
676 : }
677 1636 : if (stopEdge != edges.back()) {
678 1376 : distToStop += edges.back()->getLength();
679 1376 : if (distToStop < brakeGap) {
680 : // the distance between current edge and stop edge may be small
681 14 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
682 : ConstMSEdgeVector toFirstStop;
683 7 : router.compute(edges.back(), stopEdge, &myHolder, SIMSTEP, toFirstStop, true);
684 31 : for (int i = 1; i < (int)toFirstStop.size() - 1; i++) {
685 24 : distToStop += toFirstStop[i]->getLength();
686 : }
687 7 : }
688 : }
689 1636 : if (distToStop < brakeGap) {
690 : // circle back to stopEdge
691 : //std::cout << SIMTIME << " taxi=" << getID() << " brakeGap=" << brakeGap << " distToStop=" << distToStop << "\n";
692 185 : edges.push_back(stopEdge);
693 : addedEdge = true;
694 : }
695 : }
696 :
697 4718 : if (stopEdge == edges.back() && !stops.empty()) {
698 1026 : if (stopPos >= lastPos && stopPos <= stops.back().endPos) {
699 : // no new stop and no adaption needed
700 855 : stops.back().actType += "," + action;
701 855 : return;
702 : }
703 171 : if (stopPos >= lastPos && stopPos <= lastPos + myHolder.getVehicleType().getLength()) {
704 : // stop length adaption needed
705 0 : stops.back().endPos = MIN2(lastPos + myHolder.getVehicleType().getLength(), stopEdge->getLength());
706 0 : stops.back().actType += "," + action;
707 0 : return;
708 : }
709 : }
710 3863 : if (!addedEdge && (stopEdge != edges.back() || stopPos < lastPos)) {
711 : //std::cout << SIMTIME << " stopPos=" << stopPos << " lastPos=" << lastPos << "\n";
712 3450 : edges.push_back(stopEdge);
713 : }
714 3863 : lastPos = stopPos;
715 3863 : SUMOVehicleParameter::Stop stop;
716 3863 : stop.lane = getStopLane(stopEdge, action)->getID();
717 3863 : if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
718 628 : stop.startPos = stopPlace->getBeginLanePosition();
719 628 : stop.endPos = stopPlace->getEndLanePosition();
720 : const SumoXMLTag tag = stopPlace->getElement();
721 628 : if (tag == SUMO_TAG_BUS_STOP || tag == SUMO_TAG_TRAIN_STOP) {
722 : stop.busstop = stopPlace->getID();
723 245 : } else if (tag == SUMO_TAG_PARKING_AREA) {
724 : stop.parkingarea = stopPlace->getID();
725 240 : } else if (tag == SUMO_TAG_CONTAINER_STOP) {
726 : stop.containerstop = stopPlace->getID();
727 : }
728 : } else {
729 3235 : stop.startPos = stopPos;
730 6470 : stop.endPos = MAX2(stopPos, MIN2(myHolder.getVehicleType().getLength(), stopEdge->getLength()));
731 : }
732 7726 : stop.parking = SUMOVehicleParameter::parseParkingType(myHolder.getStringParam("device.taxi.parking", false, "true"));
733 : stop.actType = action;
734 3863 : stop.index = STOP_INDEX_END;
735 : // In case of prebooking if person is not there/ comes to late for pickup set maximum waiting time:
736 3863 : SUMOTime earliestPickupTime = res->earliestPickupTime;
737 3863 : if (isPickup && earliestPickupTime >= 0) {
738 138 : stop.waitUntil = earliestPickupTime;
739 : // TODO: replace hard coded extension with parameter
740 138 : stop.extension = static_cast<SUMOTime>(3 * 60 * 1000); // 3mins
741 : }
742 3863 : stops.push_back(stop);
743 3863 : }
744 :
745 :
746 : MSLane*
747 3863 : MSDevice_Taxi::getStopLane(const MSEdge* edge, const std::string& action) {
748 3863 : const std::vector<MSLane*>* allowedLanes = edge->allowedLanes(myHolder.getVClass());
749 3863 : if (allowedLanes == nullptr) {
750 0 : throw ProcessError("Taxi vehicle '" + myHolder.getID() + "' cannot stop on edge '" + edge->getID() + "' (" + action + ")");
751 : }
752 3863 : return allowedLanes->front();
753 : }
754 :
755 : bool
756 1075058 : MSDevice_Taxi::isEmpty() {
757 1075058 : return myState == EMPTY;
758 : }
759 :
760 :
761 : bool
762 27569 : MSDevice_Taxi::allowsBoarding(const MSTransportable* t) const {
763 27569 : return myCustomers.count(t) != 0;
764 : }
765 :
766 :
767 : void
768 697266 : MSDevice_Taxi::updateMove(const SUMOTime traveltime, const double travelledDist) {
769 697266 : if (myHolder.getPersonNumber() > 0 || myHolder.getContainerNumber() > 0) {
770 118499 : myOccupiedDistance += travelledDist;
771 118499 : myOccupiedTime += traveltime;
772 : }
773 697266 : if (isEmpty()) {
774 530409 : if (MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
775 528575 : myIdleAlgorithm->idle(this);
776 528575 : if (myRoutingDevice != nullptr) {
777 : // prevent rerouting during idling (#11079)
778 : myRoutingDevice->setActive(false);
779 : }
780 1834 : } else if (!myReachedServiceEnd) {
781 228 : WRITE_WARNINGF(TL("Taxi '%' reaches scheduled end of service at time=%."), myHolder.getID(), time2string(SIMSTEP));
782 76 : myReachedServiceEnd = true;
783 : }
784 166857 : } else if (myRoutingDevice != nullptr) {
785 : myRoutingDevice->setActive(true);
786 : }
787 697266 : if (myHolder.isStopped() && (isEmpty() || MSGlobals::gUseMesoSim) && myHolder.getNextStop().endBoarding > myServiceEnd) {
788 : // limit duration of stop (but only for idling-related stops)
789 3277 : myHolder.getNextStopMutable().endBoarding = myServiceEnd;
790 : }
791 : #ifdef DEBUG_DISPATCH
792 : if (DEBUG_COND && myIsStopped != myHolder.isStopped()) {
793 : std::cout << SIMTIME << " updateMove veh=" << myHolder.getID() << " myIsStopped=" << myIsStopped << " myHolderStopped=" << myHolder.isStopped() << " myState=" << myState << "\n";
794 : }
795 : #endif
796 697266 : myIsStopped = myHolder.isStopped();
797 697266 : }
798 :
799 :
800 : bool
801 676393 : MSDevice_Taxi::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
802 : double newPos, double /*newSpeed*/) {
803 676393 : updateMove(DELTA_T, newPos - oldPos);
804 676393 : return true; // keep the device
805 : }
806 :
807 :
808 : void
809 20873 : MSDevice_Taxi::notifyMoveInternal(const SUMOTrafficObject& /* veh */,
810 : const double /* frontOnLane */,
811 : const double timeOnLane,
812 : const double /* meanSpeedFrontOnLane */,
813 : const double /* meanSpeedVehicleOnLane */,
814 : const double travelledDistanceFrontOnLane,
815 : const double /* travelledDistanceVehicleOnLane */,
816 : const double /* meanLengthOnLane */) {
817 20873 : updateMove(TIME2STEPS(timeOnLane), travelledDistanceFrontOnLane);
818 20873 : }
819 :
820 :
821 : bool
822 67581 : MSDevice_Taxi::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
823 67581 : if (isEmpty() && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
824 44649 : myIdleAlgorithm->idle(this);
825 : }
826 67581 : return true; // keep the device
827 : }
828 :
829 :
830 : void
831 2309 : MSDevice_Taxi::customerEntered(const MSTransportable* t) {
832 2309 : myState |= OCCUPIED;
833 2309 : if (!hasFuturePickup()) {
834 1841 : myState &= ~PICKUP;
835 : }
836 11568 : for (const Reservation* res : myCurrentReservations) {
837 16685 : for (const MSTransportable* cand : res->persons) {
838 9735 : if (cand == t) {
839 2309 : const_cast<Reservation*>(res)->state = Reservation::ONBOARD;
840 2309 : break;
841 : }
842 : }
843 : }
844 2309 : }
845 :
846 :
847 : void
848 2309 : MSDevice_Taxi::customerArrived(const MSTransportable* person) {
849 2309 : myCustomersServed++;
850 : myCustomers.erase(person);
851 2309 : if (myHolder.getPersonNumber() == 0 && myHolder.getContainerNumber() == 0) {
852 1528 : myState &= ~OCCUPIED;
853 1528 : if (myHolder.getStops().size() > 1 && (myState & PICKUP) == 0) {
854 0 : WRITE_WARNINGF(TL("All customers left vehicle '%' at time=% but there are % remaining stops"),
855 : myHolder.getID(), time2string(SIMSTEP), myHolder.getStops().size() - 1);
856 0 : while (myHolder.getStops().size() > 1) {
857 0 : myHolder.abortNextStop(1);
858 : }
859 : }
860 : }
861 2309 : if (isEmpty()) {
862 : // cleanup
863 2994 : for (const Reservation* res : myCurrentReservations) {
864 1503 : myDispatcher->fulfilledReservation(res);
865 : }
866 : myCurrentReservations.clear();
867 1491 : checkTaskSwap();
868 1491 : if (isEmpty()) {
869 1487 : if (MSGlobals::gUseMesoSim && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
870 454 : myIdleAlgorithm->idle(this);
871 : }
872 : }
873 : } else {
874 : // check whether a single reservation has been fulfilled
875 5171 : for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
876 : bool fulfilled = true;
877 5443 : for (const MSTransportable* t : (*resIt)->persons) {
878 : if (myCustomers.count(t) != 0) {
879 : fulfilled = false;
880 : break;
881 : }
882 : }
883 4353 : if (fulfilled) {
884 648 : myDispatcher->fulfilledReservation(*resIt);
885 : resIt = myCurrentReservations.erase(resIt);
886 : } else {
887 : ++resIt;
888 : }
889 : }
890 : }
891 2309 : }
892 :
893 :
894 : void
895 1491 : MSDevice_Taxi::checkTaskSwap() {
896 2982 : const std::string swapGroup = myHolder.getStringParam("device.taxi.swapGroup", false, "");
897 1491 : if (swapGroup != "") {
898 12 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myDispatcher->getRouter();
899 12 : const double stopTime = myHolder.isStopped() ? MAX2(0.0, STEPS2TIME(myHolder.getNextStop().duration)) : 0;
900 : double maxSaving = 0;
901 : MSDevice_Taxi* bestSwap = nullptr;
902 36 : for (MSDevice_Taxi* taxi : myFleet) {
903 48 : if (taxi->getHolder().hasDeparted() && taxi->getState() == PICKUP
904 60 : && taxi->getHolder().getStringParam("device.taxi.swapGroup", false, "") == swapGroup) {
905 : SUMOVehicle& veh = taxi->getHolder();
906 4 : const MSStop& stop = veh.getNextStop();
907 4 : ConstMSEdgeVector toPickup(veh.getCurrentRouteEdge(), stop.edge + 1);
908 4 : const double cost = router.recomputeCostsPos(toPickup, &veh, veh.getPositionOnLane(), stop.pars.endPos, SIMSTEP);
909 : ConstMSEdgeVector toPickup2;
910 4 : router.compute(myHolder.getEdge(), myHolder.getPositionOnLane(), *stop.edge, stop.pars.endPos, &myHolder, SIMSTEP, toPickup2, true);
911 4 : if (!toPickup2.empty()) {
912 4 : const double cost2 = router.recomputeCostsPos(toPickup2, &myHolder, myHolder.getPositionOnLane(), stop.pars.endPos, SIMSTEP);
913 4 : const double saving = cost - cost2 - stopTime;
914 : //std::cout << SIMTIME << " taxi=" << getID() << " other=" << veh.getID() << " cost=" << cost << " cost2=" << cost2 << " stopTime=" << stopTime << " saving=" << saving << " route1=" << toString(toPickup) << " route2=" << toString(toPickup2) << "\n";
915 4 : if (saving > maxSaving) {
916 : maxSaving = saving;
917 : bestSwap = taxi;
918 : }
919 : }
920 4 : }
921 : }
922 12 : if (maxSaving > SWAP_THRESHOLD) {
923 : #ifdef DEBUG_DISPATCH
924 : if (DEBUG_COND) {
925 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " swapWith=" << bestSwap->getHolder().getID() << " saving=" << maxSaving << " lastDispatch=";
926 : for (const Reservation* res : bestSwap->myLastDispatch) {
927 : std::cout << toString(res->persons) << "; ";
928 : }
929 : std::cout << "\n";
930 : }
931 : #endif
932 4 : dispatchShared(bestSwap->myLastDispatch);
933 : bestSwap->myCurrentReservations.clear();
934 : bestSwap->myCustomers.clear();
935 4 : bestSwap->myState = EMPTY;
936 12 : while (bestSwap->getHolder().hasStops()) {
937 8 : bestSwap->getHolder().abortNextStop();
938 : }
939 8 : for (const Reservation* res : myCurrentReservations) {
940 4 : myDispatcher->swappedRunning(res, this);
941 : }
942 : }
943 : }
944 1491 : }
945 :
946 :
947 : bool
948 2309 : MSDevice_Taxi::hasFuturePickup() {
949 6647 : for (const auto& stop : myHolder.getStops()) {
950 4806 : if (stop.reached) {
951 2311 : continue;
952 : }
953 2495 : if (stop.pars.permitted.size() > 0) {
954 : return true;
955 : }
956 : }
957 : return false;
958 : }
959 :
960 : void
961 1006 : MSDevice_Taxi::generateOutput(OutputDevice* tripinfoOut) const {
962 1006 : if (tripinfoOut != nullptr) {
963 982 : tripinfoOut->openTag("taxi");
964 1964 : tripinfoOut->writeAttr("customers", toString(myCustomersServed));
965 1964 : tripinfoOut->writeAttr("occupiedDistance", toString(myOccupiedDistance));
966 1964 : tripinfoOut->writeAttr("occupiedTime", time2string(myOccupiedTime));
967 1964 : tripinfoOut->closeTag();
968 : }
969 1006 : }
970 :
971 : std::string
972 1884 : MSDevice_Taxi::getParameter(const std::string& key) const {
973 1884 : if (key == "customers") {
974 0 : return toString(myCustomersServed);
975 1884 : } else if (key == "occupiedDistance") {
976 0 : return toString(myOccupiedDistance);
977 1884 : } else if (key == "occupiedTime") {
978 0 : return toString(STEPS2TIME(myOccupiedTime));
979 1884 : } else if (key == "state") {
980 1805 : return toString(myState);
981 79 : } else if (key == "currentCustomers") {
982 15 : return joinNamedToStringSorting(myCustomers, " ");
983 64 : } else if (key == "pickUpDuration") {
984 64 : return myHolder.getStringParam("device.taxi.pickUpDuration", false, "0");
985 32 : } else if (key == "dropOffDuration") {
986 64 : return myHolder.getStringParam("device.taxi.dropOffDuration", false, "60");
987 : }
988 0 : throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
989 : }
990 :
991 :
992 : void
993 20 : MSDevice_Taxi::setParameter(const std::string& key, const std::string& value) {
994 : double doubleValue;
995 : try {
996 20 : doubleValue = StringUtils::toDouble(value);
997 0 : } catch (NumberFormatException&) {
998 0 : throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
999 0 : }
1000 20 : if (key == "pickUpDuration" || key == "dropOffDuration") {
1001 : // store as generic vehicle parameters
1002 20 : ((SUMOVehicleParameter&)myHolder.getParameter()).setParameter("device.taxi." + key, value);
1003 : } else {
1004 : UNUSED_PARAMETER(doubleValue);
1005 0 : throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
1006 : }
1007 20 : }
1008 :
1009 : bool
1010 63143 : MSDevice_Taxi::compatibleLine(const std::string& taxiLine, const std::string& rideLine) {
1011 72304 : return ((taxiLine == rideLine && StringUtils::startsWith(rideLine, "taxi") && StringUtils::startsWith(taxiLine, "taxi"))
1012 117125 : || (taxiLine == TAXI_SERVICE && StringUtils::startsWith(rideLine, "taxi:"))
1013 191598 : || (rideLine == TAXI_SERVICE && StringUtils::startsWith(taxiLine, "taxi:")));
1014 : }
1015 :
1016 : bool
1017 4332 : MSDevice_Taxi::compatibleLine(const Reservation* res) {
1018 4332 : return compatibleLine(myHolder.getParameter().line, res->line);
1019 : }
1020 :
1021 :
1022 : /****************************************************************************/
|