Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2013-2024 German Aerospace Center (DLR) and others.
4 : // This program and the accompanying materials are made available under the
5 : // terms of the Eclipse Public License 2.0 which is available at
6 : // https://www.eclipse.org/legal/epl-2.0/
7 : // This Source Code may also be made available under the following Secondary
8 : // Licenses when the conditions for such availability set forth in the Eclipse
9 : // Public License 2.0 are satisfied: GNU General Public License, version 2
10 : // or later which is available at
11 : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 : /****************************************************************************/
14 : /// @file 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 :
53 : //#define DEBUG_COND (myHolder.isSelected())
54 : #define DEBUG_COND (true)
55 :
56 : // ===========================================================================
57 : // static member variables
58 : // ===========================================================================
59 : SUMOTime MSDevice_Taxi::myDispatchPeriod(0);
60 : /// @brief the dispatch algorithm
61 : MSDispatch* MSDevice_Taxi::myDispatcher(nullptr);
62 : /// @brief The repeated call to the dispatcher
63 : Command* MSDevice_Taxi::myDispatchCommand(nullptr);
64 : // @brief the list of available taxis
65 : std::vector<MSDevice_Taxi*> MSDevice_Taxi::myFleet;
66 : int MSDevice_Taxi::myMaxCapacity(0);
67 : int MSDevice_Taxi::myMaxContainerCapacity(0);
68 :
69 : #define TAXI_SERVICE "taxi"
70 : #define TAXI_SERVICE_PREFIX "taxi:"
71 :
72 : // ===========================================================================
73 : // method definitions
74 : // ===========================================================================
75 : // ---------------------------------------------------------------------------
76 : // static initialisation methods
77 : // ---------------------------------------------------------------------------
78 : void
79 36322 : MSDevice_Taxi::insertOptions(OptionsCont& oc) {
80 36322 : oc.addOptionSubTopic("Taxi Device");
81 72644 : insertDefaultAssignmentOptions("taxi", "Taxi Device", oc);
82 :
83 72644 : oc.doRegister("device.taxi.dispatch-algorithm", new Option_String("greedy"));
84 72644 : oc.addDescription("device.taxi.dispatch-algorithm", "Taxi Device", TL("The dispatch algorithm [greedy|greedyClosest|greedyShared|routeExtension|traci]"));
85 :
86 72644 : oc.doRegister("device.taxi.dispatch-algorithm.output", new Option_FileName());
87 72644 : oc.addDescription("device.taxi.dispatch-algorithm.output", "Taxi Device", TL("Write information from the dispatch algorithm to FILE"));
88 :
89 72644 : oc.doRegister("device.taxi.dispatch-algorithm.params", new Option_String(""));
90 72644 : oc.addDescription("device.taxi.dispatch-algorithm.params", "Taxi Device", TL("Load dispatch algorithm parameters in format KEY1:VALUE1[,KEY2:VALUE]"));
91 :
92 72644 : oc.doRegister("device.taxi.dispatch-period", new Option_String("60", "TIME"));
93 72644 : oc.addDescription("device.taxi.dispatch-period", "Taxi Device", TL("The period between successive calls to the dispatcher"));
94 :
95 72644 : oc.doRegister("device.taxi.idle-algorithm", new Option_String("stop"));
96 72644 : oc.addDescription("device.taxi.idle-algorithm", "Taxi Device", TL("The behavior of idle taxis [stop|randomCircling]"));
97 :
98 72644 : oc.doRegister("device.taxi.idle-algorithm.output", new Option_FileName());
99 72644 : oc.addDescription("device.taxi.idle-algorithm.output", "Taxi Device", TL("Write information from the idling algorithm to FILE"));
100 36322 : }
101 :
102 :
103 : void
104 4655769 : MSDevice_Taxi::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
105 4655769 : OptionsCont& oc = OptionsCont::getOptions();
106 9311538 : if (equippedByDefaultAssignmentOptions(oc, "taxi", v, false)) {
107 : // build the device
108 12759 : MSDevice_Taxi* device = new MSDevice_Taxi(v, "taxi_" + v.getID());
109 12753 : into.push_back(device);
110 12753 : myFleet.push_back(device);
111 12753 : if (v.getParameter().line == "") {
112 : // automatically set the line so that persons are willing to enter
113 : // (see MSStageDriving::isWaitingFor)
114 12633 : const_cast<SUMOVehicleParameter&>(v.getParameter()).line = TAXI_SERVICE;
115 : }
116 12753 : if (v.getVClass() != SVC_TAXI) {
117 126 : WRITE_WARNINGF(TL("Vehicle '%' with device.taxi should have vClass taxi instead of '%'."), v.getID(), toString(v.getVClass()));
118 : }
119 12753 : const int personCapacity = v.getVehicleType().getPersonCapacity();
120 12753 : const int containerCapacity = v.getVehicleType().getContainerCapacity();
121 12753 : myMaxCapacity = MAX2(myMaxCapacity, personCapacity);
122 12753 : myMaxContainerCapacity = MAX2(myMaxContainerCapacity, containerCapacity);
123 12753 : if (personCapacity < 1 && containerCapacity < 1) {
124 0 : WRITE_WARNINGF(TL("Vehicle '%' with personCapacity % and containerCapacity % is not usable as taxi."), v.getID(), toString(personCapacity), toString(containerCapacity));
125 : }
126 : }
127 4655763 : }
128 :
129 :
130 : void
131 607 : MSDevice_Taxi::initDispatch() {
132 607 : OptionsCont& oc = OptionsCont::getOptions();
133 607 : myDispatchPeriod = string2time(oc.getString("device.taxi.dispatch-period"));
134 : // init dispatch algorithm
135 607 : std::string algo = oc.getString("device.taxi.dispatch-algorithm");
136 607 : Parameterised params;
137 1821 : params.setParametersStr(OptionsCont::getOptions().getString("device.taxi.dispatch-algorithm.params"), ":", ",");
138 607 : if (algo == "greedy") {
139 351 : myDispatcher = new MSDispatch_Greedy(params.getParametersMap());
140 256 : } else if (algo == "greedyClosest") {
141 36 : myDispatcher = new MSDispatch_GreedyClosest(params.getParametersMap());
142 238 : } else if (algo == "greedyShared") {
143 96 : myDispatcher = new MSDispatch_GreedyShared(params.getParametersMap());
144 142 : } else if (algo == "routeExtension") {
145 28 : myDispatcher = new MSDispatch_RouteExtension(params.getParametersMap());
146 128 : } else if (algo == "traci") {
147 256 : myDispatcher = new MSDispatch_TraCI(params.getParametersMap());
148 : } else {
149 0 : throw ProcessError(TLF("Dispatch algorithm '%' is not known", algo));
150 : }
151 607 : myDispatchCommand = new StaticCommand<MSDevice_Taxi>(&MSDevice_Taxi::triggerDispatch);
152 : // round to next multiple of myDispatchPeriod
153 607 : const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
154 1214 : const SUMOTime begin = string2time(oc.getString("begin"));
155 607 : const SUMOTime delay = (myDispatchPeriod - ((now - begin) % myDispatchPeriod)) % myDispatchPeriod;
156 607 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myDispatchCommand, now + delay);
157 1214 : }
158 :
159 : bool
160 42917 : MSDevice_Taxi::isReservation(const std::set<std::string>& lines) {
161 85820 : return lines.size() == 1 && (
162 : *lines.begin() == TAXI_SERVICE
163 80156 : || StringUtils::startsWith(*lines.begin(), TAXI_SERVICE_PREFIX));
164 : }
165 :
166 : void
167 2952 : MSDevice_Taxi::addReservation(MSTransportable* person,
168 : const std::set<std::string>& lines,
169 : SUMOTime reservationTime,
170 : SUMOTime pickupTime,
171 : SUMOTime earliestPickupTime,
172 : const MSEdge* from, double fromPos,
173 : const MSStoppingPlace* fromStop,
174 : const MSEdge* to, double toPos,
175 : const MSStoppingPlace* toStop,
176 : const std::string& group) {
177 2952 : if (!isReservation(lines)) {
178 : return;
179 : }
180 2952 : if ((to->getPermissions() & SVC_TAXI) == 0) {
181 0 : throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
182 0 : + " '" + person->getID() + "' because destination edge '" + to->getID() + "'"
183 0 : + " does not permit taxi access");
184 : }
185 2952 : if ((from->getPermissions() & SVC_TAXI) == 0) {
186 0 : throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
187 0 : + " '" + person->getID() + "' because origin edge '" + from->getID() + "'"
188 0 : + " does not permit taxi access");
189 : }
190 2952 : if (myDispatchCommand == nullptr) {
191 607 : initDispatch();
192 : }
193 5904 : myDispatcher->addReservation(person, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, *lines.begin(), myMaxCapacity, myMaxContainerCapacity);
194 : }
195 :
196 : void
197 135 : MSDevice_Taxi::removeReservation(MSTransportable* person,
198 : const std::set<std::string>& lines,
199 : const MSEdge* from, double fromPos,
200 : const MSEdge* to, double toPos,
201 : const std::string& group) {
202 254 : if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
203 238 : myDispatcher->removeReservation(person, from, fromPos, to, toPos, group);
204 : }
205 135 : }
206 :
207 : void
208 78 : MSDevice_Taxi::updateReservationFromPos(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, double newFromPos) {
213 144 : if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
214 132 : myDispatcher->updateReservationFromPos(person, from, fromPos, to, toPos, group, newFromPos);
215 : }
216 78 : }
217 :
218 :
219 : SUMOTime
220 6035 : MSDevice_Taxi::triggerDispatch(SUMOTime currentTime) {
221 : std::vector<MSDevice_Taxi*> active;
222 14930 : for (MSDevice_Taxi* taxi : myFleet) {
223 8895 : if (taxi->getHolder().hasDeparted()) {
224 8799 : active.push_back(taxi);
225 : }
226 : }
227 6035 : myDispatcher->computeDispatch(currentTime, active);
228 11733 : return myDispatchPeriod;
229 : }
230 :
231 : bool
232 41140 : MSDevice_Taxi::hasServableReservations() {
233 41140 : return myDispatcher != nullptr && myDispatcher->hasServableReservations();
234 : }
235 :
236 : void
237 35160 : MSDevice_Taxi::cleanup() {
238 35160 : if (myDispatcher != nullptr) {
239 607 : delete myDispatcher;
240 607 : myDispatcher = nullptr;
241 : }
242 35160 : myDispatchCommand = nullptr;
243 35160 : }
244 :
245 : // ---------------------------------------------------------------------------
246 : // MSDevice_Taxi-methods
247 : // ---------------------------------------------------------------------------
248 12759 : MSDevice_Taxi::MSDevice_Taxi(SUMOVehicle& holder, const std::string& id) :
249 12759 : MSVehicleDevice(holder, id) {
250 12765 : std::string defaultServiceEnd = toString(1e15);
251 25524 : const std::string algo = getStringParam(holder, OptionsCont::getOptions(), "taxi.idle-algorithm", "", false);
252 12759 : if (algo == "stop") {
253 12625 : myIdleAlgorithm = new MSIdling_Stop();
254 134 : } else if (algo == "randomCircling") {
255 74 : myIdleAlgorithm = new MSIdling_RandomCircling();
256 : // make sure simulation terminates
257 74 : defaultServiceEnd = toString(STEPS2TIME(
258 : myHolder.getParameter().departProcedure == DepartDefinition::GIVEN
259 : ? myHolder.getParameter().depart
260 148 : : MSNet::getInstance()->getCurrentTimeStep()) + (3600 * 8));
261 60 : } else if (algo == "taxistand") {
262 108 : const std::string rerouterID = getStringParam(holder, OptionsCont::getOptions(), "taxi.stands-rerouter", "", false);
263 54 : if (rerouterID.empty()) {
264 0 : throw ProcessError("Idle algorithm '" + algo + "' requires a rerouter id to be defined using device param 'stands-rerouter' for vehicle '" + myHolder.getID() + "'");
265 : }
266 : if (MSTriggeredRerouter::getInstances().count(rerouterID) == 0) {
267 0 : throw ProcessError("Unknown rerouter '" + rerouterID + "' when loading taxi stands for vehicle '" + myHolder.getID() + "'");
268 : }
269 54 : MSTriggeredRerouter* rerouter = MSTriggeredRerouter::getInstances().find(rerouterID)->second;
270 54 : myIdleAlgorithm = new MSIdling_TaxiStand(rerouter);
271 : } else {
272 12 : throw ProcessError("Idle algorithm '" + algo + "' is not known for vehicle '" + myHolder.getID() + "'");
273 : }
274 25390 : myServiceEnd = string2time(getStringParam(holder, OptionsCont::getOptions(), "taxi.end", defaultServiceEnd, false));
275 12753 : myRoutingDevice = static_cast<MSDevice_Routing*>(myHolder.getDevice(typeid(MSDevice_Routing)));
276 12759 : }
277 :
278 :
279 25506 : MSDevice_Taxi::~MSDevice_Taxi() {
280 12753 : myFleet.erase(std::find(myFleet.begin(), myFleet.end(), this));
281 : // recompute myMaxCapacity
282 12753 : myMaxCapacity = 0;
283 12753 : myMaxContainerCapacity = 0;
284 34262 : for (MSDevice_Taxi* taxi : myFleet) {
285 21509 : myMaxCapacity = MAX2(myMaxCapacity, taxi->getHolder().getVehicleType().getPersonCapacity());
286 21509 : myMaxContainerCapacity = MAX2(myMaxContainerCapacity, taxi->getHolder().getVehicleType().getContainerCapacity());
287 : }
288 12753 : delete myIdleAlgorithm;
289 25506 : }
290 :
291 :
292 : SUMOVehicle*
293 3398 : MSDevice_Taxi::getTaxi() {
294 3398 : if (myFleet.size() > 0) {
295 150 : return &myFleet[0]->getHolder();
296 : } else {
297 : return nullptr;
298 : }
299 : }
300 :
301 :
302 : void
303 873 : MSDevice_Taxi::dispatch(const Reservation& res) {
304 873 : dispatchShared({&res, &res});
305 871 : }
306 :
307 :
308 : void
309 1217 : MSDevice_Taxi::dispatchShared(std::vector<const Reservation*> reservations) {
310 : #ifdef DEBUG_DISPATCH
311 : if (DEBUG_COND) {
312 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " dispatch:\n";
313 : for (const Reservation* res : reservations) {
314 : std::cout << " persons=" << toString(res->persons) << "\n";
315 : }
316 : }
317 : #endif
318 1217 : myLastDispatch = reservations;
319 : ConstMSEdgeVector tmpEdges;
320 : std::vector<SUMOVehicleParameter::Stop> stops;
321 1217 : double lastPos = myHolder.getPositionOnLane();
322 1217 : const MSEdge* rerouteOrigin = *myHolder.getRerouteOrigin();
323 1217 : if (isEmpty()) {
324 : // start fresh from the current edge
325 2126 : while (myHolder.hasStops()) {
326 : // in meso there might be more than 1 stop at this point
327 1023 : myHolder.abortNextStop();
328 : }
329 : assert(!myHolder.hasStops());
330 1103 : tmpEdges.push_back(myHolder.getEdge());
331 1103 : if (myHolder.getEdge() != rerouteOrigin) {
332 8 : tmpEdges.push_back(rerouteOrigin);
333 : }
334 : } else {
335 : assert(myHolder.hasStops());
336 : // check how often existing customers appear in the new reservations
337 : std::map<const MSTransportable*, int> nOccur;
338 782 : for (const Reservation* res : reservations) {
339 1336 : for (const MSTransportable* person : res->persons) {
340 : if (myCustomers.count(person) != 0) {
341 326 : nOccur[person] += 1;
342 : if (myCurrentReservations.count(res) == 0) {
343 0 : throw ProcessError(TLF("Invalid Re-dispatch for existing customer '%' with a new reservation", person->getID()));
344 : }
345 : }
346 : }
347 : }
348 : #ifdef DEBUG_DISPATCH
349 : if (DEBUG_COND) {
350 : for (auto item : nOccur) {
351 : std::cout << " previousCustomer=" << item.first->getID() << " occurs=" << item.second << "\n";
352 : }
353 : }
354 : #endif
355 114 : if (nOccur.size() == 0) {
356 : // no overlap with existing customers - extend route
357 12 : tmpEdges = myHolder.getRoute().getEdges();
358 12 : lastPos = myHolder.getStops().back().pars.endPos;
359 : #ifdef DEBUG_DISPATCH
360 : if (DEBUG_COND) {
361 : std::cout << " re-dispatch with route-extension\n";
362 : }
363 : #endif
364 102 : } else if (nOccur.size() == myCustomers.size()) {
365 : // redefine route (verify correct number of mentions)
366 : std::set<const MSTransportable*> onBoard;
367 102 : const std::vector<MSTransportable*>& onBoardP = myHolder.getPersons();
368 102 : const std::vector<MSTransportable*>& onBoardC = myHolder.getContainers();
369 : onBoard.insert(onBoardP.begin(), onBoardP.end());
370 : onBoard.insert(onBoardC.begin(), onBoardC.end());
371 : std::set<const MSTransportable*> redundantPickup;
372 295 : for (auto item : nOccur) {
373 198 : if (item.second == 1) {
374 : // customers must already be on board
375 : if (onBoard.count(item.first) == 0) {
376 20 : throw ProcessError(TLF("Re-dispatch did not mention pickup for existing customer '%'", item.first->getID()));
377 : }
378 128 : } else if (item.second == 2) {
379 : if (onBoard.count(item.first) == 0) {
380 : // treat like a new customer
381 : // TODO: need to be checked
382 : myCustomers.erase(item.first);
383 : } else {
384 : redundantPickup.insert(item.first);
385 : }
386 : } else {
387 0 : throw ProcessError("Re-dispatch mentions existing customer '" + item.first->getID() + "' " + toString(item.second) + " times");
388 : }
389 : }
390 : // remove redundancy
391 97 : if (!redundantPickup.empty()) {
392 95 : for (auto it = reservations.begin(); it != reservations.end();) {
393 : bool isRedundant = false;
394 138 : for (const MSTransportable* person : (*it)->persons) {
395 : if (redundantPickup.count(person) != 0) {
396 : isRedundant = true;
397 : break;
398 : }
399 : }
400 78 : if (isRedundant) {
401 36 : for (const MSTransportable* person : (*it)->persons) {
402 : redundantPickup.erase(person);
403 : }
404 : it = reservations.erase(it);
405 : } else {
406 : it++;
407 : }
408 : }
409 : }
410 407 : while (myHolder.hasStops()) {
411 310 : myHolder.abortNextStop();
412 : }
413 102 : tmpEdges.push_back(myHolder.getEdge());
414 97 : if (myHolder.getEdge() != rerouteOrigin) {
415 11 : tmpEdges.push_back(rerouteOrigin);
416 : }
417 : #ifdef DEBUG_DISPATCH
418 : if (DEBUG_COND) {
419 : std::cout << " re-dispatch from scratch\n";
420 : }
421 : #endif
422 : } else {
423 : // inconsistent re-dispatch
424 : std::vector<std::string> missing;
425 0 : for (const MSTransportable* c : myCustomers) {
426 : if (nOccur.count(c) == 0) {
427 0 : missing.push_back(c->getID());
428 : }
429 : }
430 0 : throw ProcessError("Re-dispatch did mention some customers but failed to mention " + joinToStringSorting(missing, " "));
431 0 : }
432 : }
433 :
434 1212 : const SUMOTime t = MSNet::getInstance()->getCurrentTimeStep();
435 : bool hasPickup = false;
436 5623 : for (const Reservation* res : reservations) {
437 : myCurrentReservations.insert(res);
438 : bool isPickup = false;
439 9026 : for (const MSTransportable* person : res->persons) {
440 : if (myCustomers.count(person) == 0) {
441 : myCustomers.insert(person);
442 : isPickup = true;
443 : hasPickup = true;
444 : }
445 : }
446 4411 : if (isPickup) {
447 4328 : prepareStop(tmpEdges, stops, lastPos, res->from, res->fromPos, res->fromStop, "pickup " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
448 4430 : for (const MSTransportable* const transportable : res->persons) {
449 2266 : if (transportable->isPerson()) {
450 2224 : stops.back().triggered = true;
451 : } else {
452 42 : stops.back().containerTriggered = true;
453 : }
454 : stops.back().permitted.insert(transportable->getID());
455 : }
456 : // proof this lines: Is needed for pre-booking?
457 2164 : std::set<const MSTransportable*> persons = res->persons;
458 4430 : for (auto itr = persons.begin(); itr != persons.end(); itr++) {
459 2266 : stops.back().awaitedPersons.insert((*itr)->getID());
460 : }
461 :
462 2164 : stops.back().parametersSet |= STOP_PERMITTED_SET;
463 2164 : if (stops.back().duration == -1) {
464 : // keep dropOffDuration if the stop is dropOff and pickUp
465 4932 : stops.back().duration = TIME2STEPS(getFloatParam(myHolder, OptionsCont::getOptions(), "taxi.pickUpDuration", 0, false));
466 : }
467 : } else {
468 4494 : prepareStop(tmpEdges, stops, lastPos, res->to, res->toPos, res->toStop, "dropOff " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
469 6741 : stops.back().duration = TIME2STEPS(getFloatParam(myHolder, OptionsCont::getOptions(), "taxi.dropOffDuration", 60, false)); // pay and collect bags
470 : }
471 : }
472 : #ifdef DEBUG_DISPATCH
473 : if (DEBUG_COND) {
474 : std::cout << " tmpEdges=" << toString(tmpEdges) << "\n";
475 : }
476 : #endif
477 2424 : if (!myHolder.replaceRouteEdges(tmpEdges, -1, 0, "taxi:prepare_dispatch", false, false, false)) {
478 0 : throw ProcessError("Route replacement for taxi dispatch failed for vehicle '" + myHolder.getID()
479 0 : + "' at time=" + time2string(t) + ".");
480 : }
481 : #ifdef DEBUG_DISPATCH
482 : if (DEBUG_COND) std::cout << " replacedRoute=" << toString(tmpEdges)
483 : << "\n actualRoute=" << toString(myHolder.getRoute().getEdges()) << "\n";
484 : #endif
485 4360 : for (SUMOVehicleParameter::Stop& stop : stops) {
486 : std::string error;
487 3148 : myHolder.addStop(stop, error);
488 3148 : if (error != "") {
489 0 : WRITE_WARNINGF(TL("Could not add taxi stop for vehicle '%' to %. time=% error=%."), myHolder.getID(), stop.actType, time2string(t), error)
490 : }
491 : }
492 1212 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
493 : // SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myHolder.getInfluencer().getRouterTT(veh->getRNGIndex())
494 1212 : myHolder.reroute(t, "taxi:dispatch", router, false);
495 : #ifdef DEBUG_DISPATCH
496 : if (DEBUG_COND) {
497 : std::cout << "\n finalRoute=" << toString(myHolder.getRoute().getEdges()) << " routeIndex=" << myHolder.getRoutePosition() << "\n";
498 : }
499 : #endif
500 1210 : if (hasPickup) {
501 1194 : myState |= PICKUP;
502 : }
503 2427 : }
504 :
505 :
506 : void
507 100 : MSDevice_Taxi::cancelCurrentCustomers() {
508 : // check if taxi has stopped
509 100 : if (myHolder.getNextStopParameter() == nullptr) {
510 0 : return;
511 : }
512 : // find customers of the current stop
513 : std::set<const MSTransportable*> customersToBeRemoved;
514 112 : for (std::string tID : myHolder.getNextStopParameter()->permitted) {
515 : //for (const MSTransportable* t : myCustomers) {
516 24 : for (auto t : myCustomers) {
517 : //bool removed = false;
518 : //if (t->getID() == tID) {
519 12 : if (t->getID() == tID) {
520 : //cancelCustomer(t);
521 : customersToBeRemoved.insert(t);
522 : //removed = cancelCustomer((*tIt));
523 :
524 : }
525 : //if (!removed) {
526 : // tIt++;
527 : //}
528 : }
529 : }
530 112 : for (auto t : customersToBeRemoved) {
531 12 : cancelCustomer(t);
532 : }
533 : }
534 :
535 :
536 : bool
537 12 : MSDevice_Taxi::cancelCustomer(const MSTransportable* t) {
538 : // is the given transportable a customer of the reservations?
539 : if (myCustomers.count(t) == 0) {
540 : return false;
541 : }
542 : myCustomers.erase(t);
543 : // check whether a single reservation has been fulfilled or another customer is part of the reservation
544 24 : for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
545 : bool fulfilled = false;
546 12 : if ((*resIt)->persons.size() == 1 && (*resIt)->persons.count(t) != 0) {
547 : // the reservation contains only the customer
548 : fulfilled = true;
549 : }
550 : if (fulfilled) {
551 : // delete the reservation
552 12 : myDispatcher->fulfilledReservation(*resIt);
553 : // remove reservation from the current dispatch
554 36 : for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
555 24 : if (*it == *resIt)
556 24 : it = myLastDispatch.erase(it);
557 : else
558 : ++it;
559 : }
560 : // remove reservation from the served reservations
561 : resIt = myCurrentReservations.erase(resIt);
562 : }
563 : else {
564 : ++resIt;
565 : }
566 : }
567 12 : myState &= ~PICKUP; // remove state PICKUP
568 12 : for (const Reservation* res : myCurrentReservations) {
569 : // if there is another pickup in the dispatch left, add the state PICKUP
570 0 : if (std::count(myLastDispatch.begin(), myLastDispatch.end(), res) == 2) {
571 0 : myState |= PICKUP; // add state PICKUP
572 : }
573 : }
574 : // if there are reservations left, go on with the dispatch
575 : // in meso, wait for the next dispatch cycle to avoid updating stops in this stage
576 12 : if (!MSGlobals::gUseMesoSim) {
577 16 : dispatchShared(myLastDispatch);
578 : }
579 : return true;
580 : }
581 :
582 :
583 : void
584 4411 : MSDevice_Taxi::prepareStop(ConstMSEdgeVector& edges,
585 : std::vector<SUMOVehicleParameter::Stop>& stops,
586 : double& lastPos, const MSEdge* stopEdge, double stopPos,
587 : const MSStoppingPlace* stopPlace,
588 : const std::string& action, const Reservation* res, const bool isPickup) {
589 : assert(!edges.empty());
590 4411 : if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
591 822 : stopPos = stopPlace->getEndLanePosition();
592 : }
593 4411 : if (stopPos < lastPos && stopPos + NUMERICAL_EPS >= lastPos) {
594 : stopPos = lastPos;
595 : }
596 :
597 4411 : if (stops.empty()) {
598 : // check brakeGap
599 1204 : double distToStop = stopPos - lastPos;
600 1204 : const double brakeGap = myHolder.getBrakeGap();
601 1204 : if (myHolder.getLane() != nullptr && myHolder.getLane()->isInternal()) {
602 6 : distToStop += myHolder.getLane()->getLength();
603 : }
604 1204 : if (stopEdge != edges.back()) {
605 1133 : distToStop += edges.back()->getLength();
606 1133 : if (distToStop < brakeGap) {
607 : // the distance between current edge and stop edge may be small
608 12 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
609 : ConstMSEdgeVector toFirstStop;
610 6 : router.compute(edges.back(), stopEdge, &myHolder, SIMSTEP, toFirstStop, true);
611 30 : for (int i = 1; i < (int)toFirstStop.size() - 1; i++) {
612 24 : distToStop += toFirstStop[i]->getLength();
613 : }
614 : }
615 : }
616 1204 : if (distToStop < brakeGap) {
617 : // circle back to stopEdge
618 : //std::cout << SIMTIME << " taxi=" << getID() << " brakeGap=" << brakeGap << " distToStop=" << distToStop << "\n";
619 31 : edges.push_back(stopEdge);
620 : }
621 : }
622 :
623 4411 : if (stopEdge == edges.back() && !stops.empty()) {
624 1315 : if (stopPos >= lastPos && stopPos <= stops.back().endPos) {
625 : // no new stop and no adaption needed
626 1263 : stops.back().actType += "," + action;
627 1263 : return;
628 : }
629 52 : if (stopPos >= lastPos && stopPos <= lastPos + myHolder.getVehicleType().getLength()) {
630 : // stop length adaption needed
631 0 : stops.back().endPos = MIN2(lastPos + myHolder.getVehicleType().getLength(), stopEdge->getLength());
632 0 : stops.back().actType += "," + action;
633 0 : return;
634 : }
635 : }
636 3148 : if (stopEdge != edges.back() || stopPos < lastPos) {
637 3076 : edges.push_back(stopEdge);
638 : }
639 3148 : lastPos = stopPos;
640 3148 : SUMOVehicleParameter::Stop stop;
641 3148 : stop.lane = getStopLane(stopEdge, action)->getID();
642 3148 : if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
643 364 : stop.startPos = stopPlace->getBeginLanePosition();
644 364 : stop.endPos = stopPlace->getEndLanePosition();
645 : } else {
646 2784 : stop.startPos = stopPos;
647 5568 : stop.endPos = MAX2(stopPos, MIN2(myHolder.getVehicleType().getLength(), stopEdge->getLength()));
648 : }
649 3148 : stop.parking = SUMOVehicleParameter::parseParkingType(getStringParam(myHolder, OptionsCont::getOptions(), "taxi.parking", "true", false));
650 : stop.actType = action;
651 3148 : stop.index = STOP_INDEX_END;
652 : // In case of prebooking if person is not there/ comes to late for pickup set maximum waiting time:
653 3148 : SUMOTime earliestPickupTime = res->earliestPickupTime;
654 3148 : if (isPickup && earliestPickupTime >= 0) {
655 78 : stop.waitUntil = earliestPickupTime;
656 : // TODO: replace hard coded extension with parameter
657 78 : stop.extension = static_cast<SUMOTime>(3 * 60 * 1000); // 3mins
658 : }
659 3148 : stops.push_back(stop);
660 3148 : }
661 :
662 :
663 : MSLane*
664 3148 : MSDevice_Taxi::getStopLane(const MSEdge* edge, const std::string& action) {
665 3148 : const std::vector<MSLane*>* allowedLanes = edge->allowedLanes(myHolder.getVClass());
666 3148 : if (allowedLanes == nullptr) {
667 0 : throw ProcessError("Taxi vehicle '" + myHolder.getID() + "' cannot stop on edge '" + edge->getID() + "' (" + action + ")");
668 : }
669 3148 : return allowedLanes->front();
670 : }
671 :
672 : bool
673 273855 : MSDevice_Taxi::isEmpty() {
674 273855 : return myState == EMPTY;
675 : }
676 :
677 :
678 : bool
679 4859 : MSDevice_Taxi::allowsBoarding(const MSTransportable* t) const {
680 4859 : return myCustomers.count(t) != 0;
681 : }
682 :
683 :
684 : void
685 226316 : MSDevice_Taxi::updateMove(const SUMOTime traveltime, const double travelledDist) {
686 226316 : if (myHolder.getPersonNumber() > 0 || myHolder.getContainerNumber() > 0) {
687 85265 : myOccupiedDistance += travelledDist;
688 85265 : myOccupiedTime += traveltime;
689 : }
690 226316 : if (isEmpty()) {
691 107704 : if (MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
692 106352 : myIdleAlgorithm->idle(this);
693 106352 : if (myRoutingDevice != nullptr) {
694 : // prevent rerouting during idling (#11079)
695 : myRoutingDevice->setActive(false);
696 : }
697 1352 : } else if (!myReachedServiceEnd) {
698 180 : WRITE_WARNINGF(TL("Taxi '%' reaches scheduled end of service at time=%."), myHolder.getID(), time2string(SIMSTEP));
699 60 : myReachedServiceEnd = true;
700 : }
701 118612 : } else if (myRoutingDevice != nullptr) {
702 : myRoutingDevice->setActive(true);
703 : }
704 226316 : if (myHolder.isStopped()) {
705 5444 : if (!myIsStopped) {
706 : // limit duration of stop
707 : // @note: stops are not yet added to the vehicle so we can change the loaded parameters. Stops added from a route are not affected
708 : // @note: that's not true for pre-booking, so check whether endBoarding is already set
709 : //myHolder.getNextStop().endBoarding = myServiceEnd;
710 3224 : if (myHolder.getNextStop().endBoarding > myServiceEnd) {
711 3146 : myHolder.getNextStop().endBoarding = myServiceEnd;
712 : }
713 : }
714 : }
715 : #ifdef DEBUG_DISPATCH
716 : if (DEBUG_COND && myIsStopped != myHolder.isStopped()) {
717 : std::cout << SIMTIME << " updateMove veh=" << myHolder.getID() << " myIsStopped=" << myIsStopped << " myHolderStopped=" << myHolder.isStopped() << " myState=" << myState << "\n";
718 : }
719 : #endif
720 226316 : myIsStopped = myHolder.isStopped();
721 226316 : }
722 :
723 :
724 : bool
725 217138 : MSDevice_Taxi::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
726 : double newPos, double /*newSpeed*/) {
727 217138 : updateMove(DELTA_T, newPos - oldPos);
728 217138 : return true; // keep the device
729 : }
730 :
731 :
732 : void
733 9178 : MSDevice_Taxi::notifyMoveInternal(const SUMOTrafficObject& /* veh */,
734 : const double /* frontOnLane */,
735 : const double timeOnLane,
736 : const double /* meanSpeedFrontOnLane */,
737 : const double /* meanSpeedVehicleOnLane */,
738 : const double travelledDistanceFrontOnLane,
739 : const double /* travelledDistanceVehicleOnLane */,
740 : const double /* meanLengthOnLane */) {
741 9178 : updateMove(TIME2STEPS(timeOnLane), travelledDistanceFrontOnLane);
742 9178 : }
743 :
744 :
745 : bool
746 37642 : MSDevice_Taxi::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
747 37642 : if (isEmpty() && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
748 18516 : myIdleAlgorithm->idle(this);
749 : }
750 37642 : return true; // keep the device
751 : }
752 :
753 :
754 : void
755 2108 : MSDevice_Taxi::customerEntered(const MSTransportable* t) {
756 2108 : myState |= OCCUPIED;
757 2108 : if (!hasFuturePickup()) {
758 1408 : myState &= ~PICKUP;
759 : }
760 17002 : for (const Reservation* res : myCurrentReservations) {
761 27872 : for (const MSTransportable* cand : res->persons) {
762 15086 : if (cand == t) {
763 2108 : const_cast<Reservation*>(res)->state = Reservation::ONBOARD;
764 2108 : break;
765 : }
766 : }
767 : }
768 2108 : }
769 :
770 :
771 : void
772 2108 : MSDevice_Taxi::customerArrived(const MSTransportable* person) {
773 2108 : myCustomersServed++;
774 : myCustomers.erase(person);
775 2108 : if (myHolder.getPersonNumber() == 0 && myHolder.getContainerNumber() == 0) {
776 1111 : myState &= ~OCCUPIED;
777 1111 : if (myHolder.getStops().size() > 1 && (myState & PICKUP) == 0) {
778 0 : WRITE_WARNINGF(TL("All customers left vehicle '%' at time=% but there are % remaining stops"),
779 : myHolder.getID(), time2string(SIMSTEP), myHolder.getStops().size() - 1);
780 0 : while (myHolder.getStops().size() > 1) {
781 0 : myHolder.abortNextStop(1);
782 : }
783 : }
784 : }
785 2108 : if (isEmpty()) {
786 : // cleanup
787 2128 : for (const Reservation* res : myCurrentReservations) {
788 1064 : myDispatcher->fulfilledReservation(res);
789 : }
790 : myCurrentReservations.clear();
791 1064 : if (MSGlobals::gUseMesoSim && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
792 302 : myIdleAlgorithm->idle(this);
793 : }
794 : } else {
795 : // check whether a single reservation has been fulfilled
796 8601 : for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
797 : bool fulfilled = true;
798 8687 : for (const MSTransportable* t : (*resIt)->persons) {
799 : if (myCustomers.count(t) != 0) {
800 : fulfilled = false;
801 : break;
802 : }
803 : }
804 7557 : if (fulfilled) {
805 942 : myDispatcher->fulfilledReservation(*resIt);
806 : resIt = myCurrentReservations.erase(resIt);
807 : } else {
808 : ++resIt;
809 : }
810 : }
811 : }
812 2108 : }
813 :
814 :
815 : bool
816 2108 : MSDevice_Taxi::hasFuturePickup() {
817 5818 : for (const auto& stop : myHolder.getStops()) {
818 4410 : if (stop.reached) {
819 2110 : continue;
820 : }
821 2300 : if (stop.pars.permitted.size() > 0) {
822 : return true;
823 : }
824 : }
825 : return false;
826 : }
827 :
828 : void
829 693 : MSDevice_Taxi::generateOutput(OutputDevice* tripinfoOut) const {
830 693 : if (tripinfoOut != nullptr) {
831 693 : tripinfoOut->openTag("taxi");
832 1386 : tripinfoOut->writeAttr("customers", toString(myCustomersServed));
833 1386 : tripinfoOut->writeAttr("occupiedDistance", toString(myOccupiedDistance));
834 1386 : tripinfoOut->writeAttr("occupiedTime", time2string(myOccupiedTime));
835 1386 : tripinfoOut->closeTag();
836 : }
837 693 : }
838 :
839 : std::string
840 1424 : MSDevice_Taxi::getParameter(const std::string& key) const {
841 1424 : if (key == "customers") {
842 0 : return toString(myCustomersServed);
843 1424 : } else if (key == "occupiedDistance") {
844 0 : return toString(myOccupiedDistance);
845 1424 : } else if (key == "occupiedTime") {
846 0 : return toString(STEPS2TIME(myOccupiedTime));
847 1424 : } else if (key == "state") {
848 1288 : return toString(myState);
849 136 : } else if (key == "currentCustomers") {
850 18 : return joinNamedToStringSorting(myCustomers, " ");
851 118 : } else if (key == "pickUpDuration") {
852 118 : return getStringParam(myHolder, OptionsCont::getOptions(), "taxi.pickUpDuration", "0", false);
853 59 : } else if (key == "dropOffDuration") {
854 118 : return getStringParam(myHolder, OptionsCont::getOptions(), "taxi.dropOffDuration", "60", false);
855 : }
856 0 : throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
857 : }
858 :
859 :
860 : void
861 12 : MSDevice_Taxi::setParameter(const std::string& key, const std::string& value) {
862 : double doubleValue;
863 : try {
864 12 : doubleValue = StringUtils::toDouble(value);
865 0 : } catch (NumberFormatException&) {
866 0 : throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
867 0 : }
868 18 : if (key == "pickUpDuration" || key == "dropOffDuration") {
869 : // store as generic vehicle parameters
870 12 : ((SUMOVehicleParameter&)myHolder.getParameter()).setParameter("device.taxi." + key, value);
871 : } else {
872 : UNUSED_PARAMETER(doubleValue);
873 0 : throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
874 : }
875 12 : }
876 :
877 : bool
878 58376 : MSDevice_Taxi::compatibleLine(const std::string& taxiLine, const std::string& rideLine) {
879 62100 : return ((taxiLine == rideLine && StringUtils::startsWith(rideLine, "taxi") && StringUtils::startsWith(taxiLine, "taxi"))
880 113793 : || (taxiLine == TAXI_SERVICE && StringUtils::startsWith(rideLine, "taxi:"))
881 176940 : || (rideLine == TAXI_SERVICE && StringUtils::startsWith(taxiLine, "taxi:")));
882 : }
883 :
884 : bool
885 2533 : MSDevice_Taxi::compatibleLine(const Reservation* res) {
886 2533 : return compatibleLine(myHolder.getParameter().line, res->line);
887 : }
888 :
889 :
890 : /****************************************************************************/
|