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/transportables/MSTransportableControl.h>
31 : #include <microsim/MSEventControl.h>
32 : #include <microsim/MSGlobals.h>
33 : #include <microsim/MSVehicle.h>
34 : #include <microsim/MSEdge.h>
35 : #include <microsim/MSLane.h>
36 : #include <microsim/MSStop.h>
37 : #include <microsim/MSStoppingPlace.h>
38 : #include <microsim/trigger/MSTriggeredRerouter.h>
39 :
40 : #include "MSDispatch.h"
41 : #include "MSDispatch_Greedy.h"
42 : #include "MSDispatch_GreedyShared.h"
43 : #include "MSDispatch_RouteExtension.h"
44 : #include "MSDispatch_TraCI.h"
45 :
46 : #include "MSIdling.h"
47 :
48 : #include "MSRoutingEngine.h"
49 : #include "MSDevice_Routing.h"
50 : #include "MSDevice_Taxi.h"
51 :
52 : //#define DEBUG_DISPATCH
53 : //#define DEBUG_CANCEL
54 :
55 : //#define DEBUG_COND (myHolder.isSelected())
56 : #define DEBUG_COND (true)
57 :
58 : // ===========================================================================
59 : // static member variables
60 : // ===========================================================================
61 : SUMOTime MSDevice_Taxi::myDispatchPeriod(0);
62 : /// @brief the dispatch algorithm
63 : MSDispatch* MSDevice_Taxi::myDispatcher(nullptr);
64 : /// @brief The repeated call to the dispatcher
65 : Command* MSDevice_Taxi::myDispatchCommand(nullptr);
66 : // @brief the list of available taxis
67 : std::vector<MSDevice_Taxi*> MSDevice_Taxi::myFleet;
68 : int MSDevice_Taxi::myMaxCapacity(0);
69 : int MSDevice_Taxi::myMaxContainerCapacity(0);
70 : std::map<SUMOVehicleClass, std::string> MSDevice_Taxi::myTaxiTypes;
71 : SUMOTime MSDevice_Taxi::myNextDispatchTime(-1);
72 : std::map<std::string, MSDevice_Taxi*> MSDevice_Taxi::myStateLoadedCustomers;
73 : std::map<std::string, MSDevice_Taxi*> MSDevice_Taxi::myStateLoadedReservations;
74 :
75 : #define TAXI_SERVICE "taxi"
76 : #define TAXI_SERVICE_PREFIX "taxi:"
77 : #define SWAP_THRESHOLD 5
78 :
79 : // ===========================================================================
80 : // method definitions
81 : // ===========================================================================
82 : // ---------------------------------------------------------------------------
83 : // static initialisation methods
84 : // ---------------------------------------------------------------------------
85 : void
86 44671 : MSDevice_Taxi::insertOptions(OptionsCont& oc) {
87 44671 : oc.addOptionSubTopic("Taxi Device");
88 89342 : insertDefaultAssignmentOptions("taxi", "Taxi Device", oc);
89 :
90 89342 : oc.doRegister("device.taxi.dispatch-algorithm", new Option_String("greedy"));
91 89342 : oc.addDescription("device.taxi.dispatch-algorithm", "Taxi Device", TL("The dispatch algorithm [greedy|greedyClosest|greedyShared|routeExtension|traci]"));
92 :
93 44671 : oc.doRegister("device.taxi.dispatch-algorithm.output", new Option_FileName());
94 89342 : oc.addDescription("device.taxi.dispatch-algorithm.output", "Taxi Device", TL("Write information from the dispatch algorithm to FILE"));
95 :
96 89342 : oc.doRegister("device.taxi.dispatch-algorithm.params", new Option_String(""));
97 89342 : oc.addDescription("device.taxi.dispatch-algorithm.params", "Taxi Device", TL("Load dispatch algorithm parameters in format KEY1:VALUE1[,KEY2:VALUE]"));
98 :
99 89342 : oc.doRegister("device.taxi.dispatch-period", new Option_String("60", "TIME"));
100 89342 : oc.addDescription("device.taxi.dispatch-period", "Taxi Device", TL("The period between successive calls to the dispatcher"));
101 :
102 89342 : oc.doRegister("device.taxi.dispatch-keep-unreachable", new Option_String("3600", "TIME"));
103 89342 : oc.addDescription("device.taxi.dispatch-keep-unreachable", "Taxi Device", TL("The time before aborting unreachable reservations"));
104 :
105 89342 : oc.doRegister("device.taxi.idle-algorithm", new Option_String("stop"));
106 89342 : oc.addDescription("device.taxi.idle-algorithm", "Taxi Device", TL("The behavior of idle taxis [stop|randomCircling|taxistand]"));
107 :
108 44671 : oc.doRegister("device.taxi.idle-algorithm.output", new Option_FileName());
109 89342 : oc.addDescription("device.taxi.idle-algorithm.output", "Taxi Device", TL("Write information from the idling algorithm to FILE"));
110 :
111 89342 : oc.doRegister("device.taxi.vclasses", new Option_StringVector({"taxi"}));
112 89342 : oc.addSynonyme("device.taxi.vclasses", "taxi.vclasses");
113 89342 : oc.addDescription("device.taxi.vclasses", "Taxi Device", TL("Network permissions that can be accessed by taxis"));
114 44671 : }
115 :
116 :
117 : void
118 5232261 : MSDevice_Taxi::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into) {
119 5232261 : OptionsCont& oc = OptionsCont::getOptions();
120 10464522 : if (equippedByDefaultAssignmentOptions(oc, "taxi", v, false)) {
121 : // build the device
122 7013 : MSDevice_Taxi* device = new MSDevice_Taxi(v, "taxi_" + v.getID());
123 7001 : into.push_back(device);
124 7001 : myFleet.push_back(device);
125 7001 : if (v.getParameter().line == "") {
126 : // automatically set the line so that persons are willing to enter
127 : // (see MSStageDriving::isWaitingFor)
128 6724 : const_cast<SUMOVehicleParameter&>(v.getParameter()).line = TAXI_SERVICE;
129 : }
130 7001 : const int personCapacity = v.getVehicleType().getPersonCapacity();
131 7001 : const int containerCapacity = v.getVehicleType().getContainerCapacity();
132 7001 : myMaxCapacity = MAX2(myMaxCapacity, personCapacity);
133 7001 : myMaxContainerCapacity = MAX2(myMaxContainerCapacity, containerCapacity);
134 7001 : if (myTaxiTypes[v.getVClass()] == "") {
135 1748 : myTaxiTypes[v.getVClass()] = v.getVehicleType().getID();
136 : }
137 7001 : if ((gTaxiClasses & v.getVClass()) == 0) {
138 80 : gTaxiClasses |= v.getVClass();
139 80 : MSNet::getInstance()->resetIntermodalRouter();
140 : }
141 7001 : if (personCapacity < 1 && containerCapacity < 1) {
142 0 : WRITE_WARNINGF(TL("Vehicle '%' with personCapacity % and containerCapacity % is not usable as taxi."), v.getID(), toString(personCapacity), toString(containerCapacity));
143 : }
144 : }
145 5232255 : }
146 :
147 : SUMOTime
148 16 : MSDevice_Taxi::getNextDispatchTime() {
149 16 : return myNextDispatchTime;
150 : }
151 :
152 : void
153 907 : MSDevice_Taxi::initDispatch(SUMOTime next) {
154 907 : OptionsCont& oc = OptionsCont::getOptions();
155 907 : myDispatchPeriod = string2time(oc.getString("device.taxi.dispatch-period"));
156 : // init dispatch algorithm
157 907 : std::string algo = oc.getString("device.taxi.dispatch-algorithm");
158 907 : Parameterised params;
159 1814 : params.setParametersStr(OptionsCont::getOptions().getString("device.taxi.dispatch-algorithm.params"), ":", ",");
160 907 : if (algo == "greedy") {
161 603 : myDispatcher = new MSDispatch_Greedy(params.getParametersMap());
162 304 : } else if (algo == "greedyClosest") {
163 36 : myDispatcher = new MSDispatch_GreedyClosest(params.getParametersMap());
164 286 : } else if (algo == "greedyShared") {
165 114 : myDispatcher = new MSDispatch_GreedyShared(params.getParametersMap());
166 172 : } else if (algo == "routeExtension") {
167 122 : myDispatcher = new MSDispatch_RouteExtension(params.getParametersMap());
168 111 : } else if (algo == "traci") {
169 222 : myDispatcher = new MSDispatch_TraCI(params.getParametersMap());
170 : } else {
171 0 : throw ProcessError(TLF("Dispatch algorithm '%' is not known", algo));
172 : }
173 907 : myDispatchCommand = new StaticCommand<MSDevice_Taxi>(&MSDevice_Taxi::triggerDispatch);
174 : // round to next multiple of myDispatchPeriod
175 907 : if (next < 0) {
176 891 : const SUMOTime now = MSNet::getInstance()->getCurrentTimeStep();
177 891 : const SUMOTime begin = string2time(oc.getString("begin"));
178 891 : const SUMOTime delay = (myDispatchPeriod - ((now - begin) % myDispatchPeriod)) % myDispatchPeriod;
179 891 : next = now + delay;
180 : }
181 907 : myNextDispatchTime = next;
182 907 : MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(myDispatchCommand, next);
183 1814 : }
184 :
185 : bool
186 78724 : MSDevice_Taxi::isReservation(const std::set<std::string>& lines) {
187 78724 : return lines.size() == 1 && (
188 78710 : *lines.begin() == TAXI_SERVICE
189 151058 : || StringUtils::startsWith(*lines.begin(), TAXI_SERVICE_PREFIX));
190 : }
191 :
192 : void
193 3248 : MSDevice_Taxi::addReservation(MSTransportable* person,
194 : const std::set<std::string>& lines,
195 : SUMOTime reservationTime,
196 : SUMOTime pickupTime,
197 : SUMOTime earliestPickupTime,
198 : const MSEdge* from, double fromPos,
199 : const MSStoppingPlace* fromStop,
200 : const MSEdge* to, double toPos,
201 : const MSStoppingPlace* toStop,
202 : const std::string& group) {
203 3248 : if (!isReservation(lines)) {
204 : return;
205 : }
206 3248 : if ((to->getPermissions() & gTaxiClasses) == 0) {
207 0 : throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
208 0 : + " '" + person->getID() + "' because destination edge '" + to->getID() + "'"
209 0 : + " does not permit taxi access");
210 : }
211 3248 : if ((from->getPermissions() & gTaxiClasses) == 0) {
212 0 : throw ProcessError("Cannot add taxi reservation for " + std::string(person->isPerson() ? "person" : "container")
213 0 : + " '" + person->getID() + "' because origin edge '" + from->getID() + "'"
214 0 : + " does not permit taxi access");
215 : }
216 3248 : if (myDispatchCommand == nullptr) {
217 891 : initDispatch();
218 : }
219 3248 : if (fromStop != nullptr && &fromStop->getLane().getEdge() == from) {
220 : // pickup position should be at the stop-endPos
221 930 : fromPos = fromStop->getEndLanePosition();
222 : }
223 6496 : Reservation* res = myDispatcher->addReservation(person, reservationTime, pickupTime, earliestPickupTime, from, fromPos, fromStop, to, toPos, toStop, group, *lines.begin(), myMaxCapacity, myMaxContainerCapacity);
224 3248 : if (myStateLoadedCustomers.size() > 0) {
225 : auto it = myStateLoadedCustomers.find(person->getID());
226 76 : if (it != myStateLoadedCustomers.end()) {
227 : //std::cout << SIMTIME << " loadedServed p=" << person->getID() << " res=" << res->getID() << " taxi=" << it->second->getID() << "\n";
228 28 : myDispatcher->servedReservation(res, it->second);
229 : }
230 : }
231 : }
232 :
233 : void
234 93 : MSDevice_Taxi::removeReservation(MSTransportable* person,
235 : const std::set<std::string>& lines,
236 : const MSEdge* from, double fromPos,
237 : const MSEdge* to, double toPos,
238 : const std::string& group) {
239 93 : if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
240 154 : myDispatcher->removeReservation(person, from, fromPos, to, toPos, group);
241 : }
242 93 : }
243 :
244 : void
245 162 : MSDevice_Taxi::updateReservationFromPos(MSTransportable* person,
246 : const std::set<std::string>& lines,
247 : const MSEdge* from, double fromPos,
248 : const MSEdge* to, double toPos,
249 : const std::string& group, double newFromPos) {
250 162 : if (myDispatcher != nullptr && lines.size() == 1 && *lines.begin() == TAXI_SERVICE) {
251 300 : myDispatcher->updateReservationFromPos(person, from, fromPos, to, toPos, group, newFromPos);
252 : }
253 162 : }
254 :
255 :
256 : SUMOTime
257 15996 : MSDevice_Taxi::triggerDispatch(SUMOTime currentTime) {
258 : std::vector<MSDevice_Taxi*> active;
259 46641 : for (MSDevice_Taxi* taxi : myFleet) {
260 30645 : if (taxi->getHolder().hasDeparted()) {
261 30542 : active.push_back(taxi);
262 : }
263 : }
264 15996 : myDispatcher->computeDispatch(currentTime, active);
265 15993 : myNextDispatchTime = currentTime + myDispatchPeriod;
266 15993 : return myDispatchPeriod;
267 15996 : }
268 :
269 : bool
270 8650485 : MSDevice_Taxi::hasServableReservations() {
271 8650485 : return myDispatcher != nullptr && myDispatcher->hasServableReservations();
272 : }
273 :
274 : void
275 41529 : MSDevice_Taxi::cleanup() {
276 41529 : if (myDispatcher != nullptr) {
277 907 : delete myDispatcher;
278 907 : myDispatcher = nullptr;
279 : }
280 41529 : myDispatchCommand = nullptr;
281 : myTaxiTypes.clear();
282 41529 : }
283 :
284 :
285 : void
286 640 : MSDevice_Taxi::allCustomersErased() {
287 1606 : for (MSDevice_Taxi* taxi : myFleet) {
288 : // disable taskSwap
289 966 : taxi->myState = EMPTY;
290 : }
291 640 : }
292 :
293 :
294 : const std::map<SUMOVehicleClass, std::string>&
295 229 : MSDevice_Taxi::getTaxiTypes() {
296 229 : if (myTaxiTypes.size() < std::bitset<64>(gTaxiClasses).count()) {
297 240 : for (const std::string& vClassName : OptionsCont::getOptions().getStringVector("device.taxi.vclasses")) {
298 80 : SUMOVehicleClass svc = (SUMOVehicleClass)parseVehicleClasses(vClassName);
299 80 : if (myTaxiTypes[svc] == "") {
300 80 : switch(svc) {
301 : // @see MSVehicleControl::initDefaultTypes()
302 80 : case SVC_TAXI:
303 80 : myTaxiTypes[svc] = DEFAULT_TAXITYPE_ID;
304 : break;
305 0 : case SVC_RAIL:
306 0 : myTaxiTypes[svc] = DEFAULT_RAILTYPE_ID;
307 : break;
308 0 : case SVC_BICYCLE:
309 0 : myTaxiTypes[svc] = DEFAULT_BIKETYPE_ID;
310 : break;
311 0 : case SVC_PASSENGER:
312 0 : myTaxiTypes[svc] = DEFAULT_VTYPE_ID;
313 : break;
314 0 : default: {
315 0 : const std::string typeID = "DEFAULT_" + StringUtils::to_upper_case(vClassName) + "TYPE";
316 0 : MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
317 0 : if (!vc.hasVType(typeID)) {
318 0 : SUMOVTypeParameter tp(typeID, svc);
319 0 : MSVehicleType* t = MSVehicleType::build(tp);
320 0 : vc.addVType(t);
321 0 : }
322 0 : myTaxiTypes[svc] = typeID;;
323 : }
324 : }
325 : }
326 : }
327 : }
328 229 : return myTaxiTypes;
329 : }
330 :
331 :
332 : // ---------------------------------------------------------------------------
333 : // MSDevice_Taxi-methods
334 : // ---------------------------------------------------------------------------
335 7007 : MSDevice_Taxi::MSDevice_Taxi(SUMOVehicle& holder, const std::string& id) :
336 7007 : MSVehicleDevice(holder, id) {
337 7007 : std::string defaultServiceEnd = toString(1e15);
338 14020 : const std::string algo = holder.getStringParam("device.taxi.idle-algorithm");
339 7007 : if (algo == "stop") {
340 6694 : myIdleAlgorithm = new MSIdling_Stop();
341 313 : } else if (algo == "randomCircling") {
342 108 : myIdleAlgorithm = new MSIdling_RandomCircling();
343 : // make sure simulation terminates
344 108 : defaultServiceEnd = toString(STEPS2TIME(
345 : myHolder.getParameter().departProcedure == DepartDefinition::GIVEN
346 : ? myHolder.getParameter().depart
347 216 : : MSNet::getInstance()->getCurrentTimeStep()) + (3600 * 8));
348 205 : } else if (algo == "taxistand") {
349 398 : const std::string rerouterID = holder.getStringParam("device.taxi.stands-rerouter");
350 199 : if (rerouterID.empty()) {
351 0 : throw ProcessError("Idle algorithm '" + algo + "' requires a rerouter id to be defined using device param 'stands-rerouter' for vehicle '" + myHolder.getID() + "'");
352 : }
353 : if (MSTriggeredRerouter::getInstances().count(rerouterID) == 0) {
354 0 : throw ProcessError("Unknown rerouter '" + rerouterID + "' when loading taxi stands for vehicle '" + myHolder.getID() + "'");
355 : }
356 199 : MSTriggeredRerouter* rerouter = MSTriggeredRerouter::getInstances().find(rerouterID)->second;
357 199 : myIdleAlgorithm = new MSIdling_TaxiStand(rerouter);
358 : } else {
359 18 : throw ProcessError("Idle algorithm '" + algo + "' is not known for vehicle '" + myHolder.getID() + "'");
360 : }
361 7007 : myServiceEnd = string2time(holder.getStringParam("device.taxi.end", false, defaultServiceEnd));
362 7001 : myRoutingDevice = static_cast<MSDevice_Routing*>(myHolder.getDevice(typeid(MSDevice_Routing)));
363 7007 : }
364 :
365 :
366 14002 : MSDevice_Taxi::~MSDevice_Taxi() {
367 7001 : myFleet.erase(std::find(myFleet.begin(), myFleet.end(), this));
368 : // recompute myMaxCapacity
369 7001 : myMaxCapacity = 0;
370 7001 : myMaxContainerCapacity = 0;
371 17828 : for (MSDevice_Taxi* taxi : myFleet) {
372 10827 : myMaxCapacity = MAX2(myMaxCapacity, taxi->getHolder().getVehicleType().getPersonCapacity());
373 10827 : myMaxContainerCapacity = MAX2(myMaxContainerCapacity, taxi->getHolder().getVehicleType().getContainerCapacity());
374 : }
375 7001 : delete myIdleAlgorithm;
376 14002 : }
377 :
378 :
379 : bool
380 11606 : MSDevice_Taxi::hasFleet() {
381 11606 : return myFleet.size() > 0;;
382 : }
383 :
384 :
385 : void
386 1608 : MSDevice_Taxi::dispatch(const Reservation& res) {
387 1608 : dispatchShared({&res, &res});
388 1605 : }
389 :
390 :
391 : void
392 1952 : MSDevice_Taxi::dispatchShared(std::vector<const Reservation*> reservations) {
393 : #ifdef DEBUG_DISPATCH
394 : if (DEBUG_COND) {
395 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " dispatch:\n";
396 : for (const Reservation* res : reservations) {
397 : std::cout << " res=" << res->getID();
398 : std::cout << " persons=" << toString(res->persons) << "\n";
399 : }
400 : }
401 : #endif
402 1952 : myLastDispatch = reservations;
403 : ConstMSEdgeVector tmpEdges;
404 : StopParVector stops;
405 1952 : double lastPos = myHolder.getPositionOnLane();
406 1952 : const MSEdge* rerouteOrigin = *myHolder.getRerouteOrigin();
407 1952 : if (isEmpty()) {
408 : // start fresh from the current edge
409 1861 : if (myHolder.isStoppedParking()) {
410 : // parking stop must be ended normally
411 1110 : MSStop& stop = myHolder.getNextStopMutable();
412 1110 : stop.duration = 0;
413 1110 : lastPos = stop.pars.endPos;
414 1110 : if (myHolder.isStoppedTriggered()) {
415 994 : stop.triggered = false;
416 994 : stop.containerTriggered = false;
417 994 : stop.joinTriggered = false;
418 994 : myHolder.unregisterWaiting();
419 : }
420 : // prevent unauthorized/premature entry
421 1110 : const_cast<SUMOVehicleParameter::Stop&>(stop.pars).permitted.insert("");
422 1134 : while (myHolder.getStops().size() > 1) {
423 24 : myHolder.abortNextStop(1);
424 : }
425 : } else {
426 1416 : while (myHolder.hasStops()) {
427 : // in meso there might be more than 1 stop at this point
428 665 : myHolder.abortNextStop();
429 : }
430 : assert(!myHolder.hasStops());
431 : }
432 1861 : tmpEdges.push_back(myHolder.getEdge());
433 1861 : if (myHolder.getEdge() != rerouteOrigin) {
434 8 : tmpEdges.push_back(rerouteOrigin);
435 : }
436 : } else {
437 : assert(myHolder.hasStops());
438 : // check how often existing customers appear in the new reservations
439 : std::map<const MSTransportable*, int> nOccur;
440 629 : for (const Reservation* res : reservations) {
441 1076 : for (const MSTransportable* person : res->persons) {
442 : if (myCustomers.count(person) != 0) {
443 298 : nOccur[person] += 1;
444 : if (myCurrentReservations.count(res) == 0) {
445 0 : throw ProcessError(TLF("Invalid Re-dispatch for existing customer '%' with a new reservation", person->getID()));
446 : }
447 : }
448 : }
449 : }
450 : #ifdef DEBUG_DISPATCH
451 : if (DEBUG_COND) {
452 : for (auto item : nOccur) {
453 : std::cout << " previousCustomer=" << item.first->getID() << " occurs=" << item.second << "\n";
454 : }
455 : }
456 : #endif
457 91 : if (nOccur.size() == 0) {
458 : // no overlap with existing customers - extend route
459 10 : tmpEdges = myHolder.getRoute().getEdges();
460 10 : lastPos = myHolder.getStops().back().pars.endPos;
461 : #ifdef DEBUG_DISPATCH
462 : if (DEBUG_COND) {
463 : std::cout << " re-dispatch with route-extension\n";
464 : }
465 : #endif
466 81 : } else if (nOccur.size() == myCustomers.size()) {
467 : // redefine route (verify correct number of mentions)
468 : std::set<const MSTransportable*> onBoard;
469 81 : const std::vector<MSTransportable*>& onBoardP = myHolder.getPersons();
470 81 : const std::vector<MSTransportable*>& onBoardC = myHolder.getContainers();
471 : onBoard.insert(onBoardP.begin(), onBoardP.end());
472 : onBoard.insert(onBoardC.begin(), onBoardC.end());
473 : std::set<const MSTransportable*> redundantPickup;
474 244 : for (auto item : nOccur) {
475 167 : if (item.second == 1) {
476 : // customers must already be on board
477 : if (onBoard.count(item.first) == 0) {
478 12 : throw ProcessError(TLF("Re-dispatch did not mention pickup for existing customer '%'", item.first->getID()));
479 : }
480 131 : } else if (item.second == 2) {
481 : if (onBoard.count(item.first) == 0) {
482 : // treat like a new customer
483 : // TODO: need to be checked
484 : myCustomers.erase(item.first);
485 : } else {
486 : redundantPickup.insert(item.first);
487 : }
488 : } else {
489 0 : throw ProcessError("Re-dispatch mentions existing customer '" + item.first->getID() + "' " + toString(item.second) + " times");
490 : }
491 : }
492 : // remove redundancy
493 77 : if (!redundantPickup.empty()) {
494 183 : for (auto it = reservations.begin(); it != reservations.end();) {
495 : bool isRedundant = false;
496 262 : for (const MSTransportable* person : (*it)->persons) {
497 : if (redundantPickup.count(person) != 0) {
498 : isRedundant = true;
499 : break;
500 : }
501 : }
502 154 : if (isRedundant) {
503 92 : for (const MSTransportable* person : (*it)->persons) {
504 : redundantPickup.erase(person);
505 : }
506 : it = reservations.erase(it);
507 : } else {
508 : it++;
509 : }
510 : }
511 : }
512 321 : while (myHolder.hasStops()) {
513 244 : myHolder.abortNextStop();
514 : }
515 81 : tmpEdges.push_back(myHolder.getEdge());
516 77 : if (myHolder.getEdge() != rerouteOrigin) {
517 10 : tmpEdges.push_back(rerouteOrigin);
518 : }
519 : #ifdef DEBUG_DISPATCH
520 : if (DEBUG_COND) {
521 : std::cout << " re-dispatch from scratch\n";
522 : }
523 : #endif
524 : } else {
525 : // inconsistent re-dispatch
526 : std::vector<std::string> missing;
527 0 : for (const MSTransportable* c : myCustomers) {
528 : if (nOccur.count(c) == 0) {
529 0 : missing.push_back(c->getID());
530 : }
531 : }
532 0 : throw ProcessError("Re-dispatch did mention some customers but failed to mention " + joinToStringSorting(missing, " "));
533 0 : }
534 : }
535 :
536 : bool hasPickup = false;
537 7244 : for (const Reservation* res : reservations) {
538 : myCurrentReservations.insert(res);
539 : bool isPickup = false;
540 10952 : for (const MSTransportable* person : res->persons) {
541 : if (myCustomers.count(person) == 0) {
542 : myCustomers.insert(person);
543 : isPickup = true;
544 : hasPickup = true;
545 : }
546 : }
547 5296 : if (isPickup) {
548 7827 : prepareStop(tmpEdges, stops, lastPos, res->from, res->fromPos, res->fromStop, "pickup " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
549 5398 : for (const MSTransportable* const transportable : res->persons) {
550 2789 : if (transportable->isPerson()) {
551 2615 : stops.back().triggered = true;
552 : } else {
553 174 : stops.back().containerTriggered = true;
554 : }
555 : stops.back().permitted.insert(transportable->getID());
556 2789 : stops.back().parametersSet |= STOP_PERMITTED_SET | STOP_TRIGGER_SET;
557 : }
558 5398 : for (const MSTransportable* t : res->persons) {
559 : stops.back().awaitedPersons.insert(t->getID());
560 : }
561 :
562 2609 : if (stops.back().duration == -1) {
563 : // keep dropOffDuration if the stop is dropOff and pickUp
564 2256 : stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.pickUpDuration", false, 0));
565 : }
566 : } else {
567 5374 : prepareStop(tmpEdges, stops, lastPos, res->to, res->toPos, res->toStop, "dropOff " + toString(res->persons) + " (" + res->id + ")", res, isPickup);
568 2687 : stops.back().duration = TIME2STEPS(myHolder.getFloatParam("device.taxi.dropOffDuration", false, 60)); // pay and collect bags
569 : }
570 5296 : stops.back().parametersSet |= STOP_DURATION_SET | STOP_PARKING_SET;
571 : }
572 : #ifdef DEBUG_DISPATCH
573 : if (DEBUG_COND) {
574 : std::cout << " tmpEdges=" << toString(tmpEdges) << "\n";
575 : }
576 : #endif
577 1948 : const SUMOTime t = SIMSTEP;
578 3896 : if (!myHolder.replaceRouteEdges(tmpEdges, -1, 0, "taxi:prepare_dispatch", false, false, false)) {
579 0 : throw ProcessError("Route replacement for taxi dispatch failed for vehicle '" + myHolder.getID()
580 0 : + "' at time=" + time2string(t) + ".");
581 : }
582 : #ifdef DEBUG_DISPATCH
583 : if (DEBUG_COND) std::cout << " replacedRoute=" << toString(tmpEdges)
584 : << "\n actualRoute=" << toString(myHolder.getRoute().getEdges()) << "\n";
585 : #endif
586 6392 : for (SUMOVehicleParameter::Stop& stop : stops) {
587 : std::string error;
588 4444 : if (!myHolder.addStop(stop, error)) {
589 0 : WRITE_WARNINGF(TL("Could not add taxi stop for %, desc=% time=% error=%"), myHolder.getID(), stop.actType, time2string(t), error)
590 : }
591 : }
592 1948 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
593 : // SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myHolder.getInfluencer().getRouterTT(veh->getRNGIndex())
594 1948 : myHolder.reroute(t, "taxi:dispatch", router, false);
595 : #ifdef DEBUG_DISPATCH
596 : if (DEBUG_COND) {
597 : std::cout << "\n finalRoute=" << toString(myHolder.getRoute().getEdges()) << " routeIndex=" << myHolder.getRoutePosition() << "\n";
598 : }
599 : #endif
600 1945 : if (hasPickup) {
601 1913 : myState |= PICKUP;
602 : }
603 1959 : }
604 :
605 :
606 : void
607 88 : MSDevice_Taxi::cancelCurrentCustomers() {
608 : // check if taxi has stopped
609 88 : if (myHolder.getNextStopParameter() == nullptr) {
610 0 : return;
611 : }
612 : // find customers of the current stop
613 : std::set<const MSTransportable*> customersToBeRemoved;
614 : std::set<const MSTransportable*> onBoard;
615 88 : onBoard.insert(myHolder.getPersons().begin(), myHolder.getPersons().end());
616 88 : onBoard.insert(myHolder.getContainers().begin(), myHolder.getContainers().end());
617 142 : for (std::string tID : myHolder.getNextStopParameter()->permitted) {
618 220 : for (auto t : myCustomers) {
619 166 : if (t->getID() == tID && onBoard.count(t) == 0) {
620 : customersToBeRemoved.insert(t);
621 : }
622 : }
623 : }
624 88 : if (!customersToBeRemoved.empty()) {
625 90 : WRITE_WARNINGF(TL("Taxi '%' aborts waiting for customers: % at time=%."),
626 : myHolder.getID(), toString(customersToBeRemoved), time2string(SIMSTEP));
627 : }
628 122 : for (auto t : customersToBeRemoved) {
629 34 : cancelCustomer(t);
630 : }
631 : }
632 :
633 :
634 : bool
635 44 : MSDevice_Taxi::cancelCustomer(const MSTransportable* t) {
636 : #ifdef DEBUG_CANCEL
637 : if (DEBUG_COND) {
638 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " cancelCustomer " << t->getID() << "\n";
639 : }
640 : #endif
641 :
642 : // is the given transportable a customer of the reservations?
643 : if (myCustomers.count(t) == 0) {
644 : return false;
645 : }
646 : myCustomers.erase(t);
647 : // check whether a single reservation has been fulfilled or another customer is part of the reservation
648 132 : for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
649 : bool fulfilled = false;
650 88 : if ((*resIt)->persons.size() == 1 && (*resIt)->persons.count(t) != 0) {
651 : // the reservation contains only the customer
652 : fulfilled = true;
653 : }
654 : if (fulfilled) {
655 44 : const Reservation* res = *resIt;
656 : // remove reservation from the current dispatch
657 220 : for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
658 176 : if (*it == res) {
659 88 : it = myLastDispatch.erase(it);
660 : } else {
661 : ++it;
662 : }
663 : }
664 : // remove reservation from the served reservations
665 : resIt = myCurrentReservations.erase(resIt);
666 : // delete the reservation
667 44 : myDispatcher->fulfilledReservation(res);
668 : } else {
669 : ++resIt;
670 : }
671 : }
672 44 : myState &= ~PICKUP; // remove state PICKUP
673 88 : for (const Reservation* res : myCurrentReservations) {
674 : // if there is another pickup in the dispatch left, add the state PICKUP
675 44 : if (std::count(myLastDispatch.begin(), myLastDispatch.end(), res) == 2) {
676 44 : myState |= PICKUP; // add state PICKUP
677 : }
678 : }
679 : // we also have to clean reservations from myLastDispatch where the customers arrived in the meantime
680 132 : for (auto it = myLastDispatch.begin(); it != myLastDispatch.end();) {
681 : if (myCurrentReservations.count(*it) == 0) {
682 0 : it = myLastDispatch.erase(it);
683 : } else {
684 : ++it;
685 : }
686 : }
687 : // if there are reservations left, go on with the dispatch
688 : // in meso, wait for the next dispatch cycle to avoid updating stops in this stage
689 44 : if (!MSGlobals::gUseMesoSim) {
690 36 : dispatchShared(myLastDispatch);
691 : }
692 : return true;
693 : }
694 :
695 :
696 : void
697 60 : MSDevice_Taxi::addCustomer(const MSTransportable* t, const Reservation* res) {
698 : myCustomers.insert(t);
699 60 : MSBaseVehicle& veh = dynamic_cast<MSBaseVehicle&>(myHolder);
700 122 : for (const MSStop& stop : veh.getStops()) {
701 : SUMOVehicleParameter::Stop& pars = const_cast<SUMOVehicleParameter::Stop&>(stop.pars);
702 : //std::cout << " sE=" << (*stop.edge)->getID() << " sStart=" << pars.startPos << " sEnd=" << pars.endPos << " rFrom=" <<
703 : // res->from->getID() << " rTo=" << res->to->getID() << " rFromPos=" << res->fromPos << " resToPos=" << res->toPos << "\n";
704 122 : if (*stop.edge == res->from
705 60 : && pars.startPos <= res->fromPos
706 182 : && pars.endPos >= res->fromPos) {
707 60 : pars.awaitedPersons.insert(t->getID());
708 : pars.permitted.insert(t->getID());
709 120 : pars.actType += " +" + t->getID();
710 62 : } else if (*stop.edge == res->to
711 60 : && pars.startPos <= res->toPos
712 122 : && pars.endPos >= res->toPos) {
713 60 : pars.actType += " +" + t->getID();
714 60 : break;
715 : }
716 : }
717 60 : }
718 :
719 :
720 : void
721 5296 : MSDevice_Taxi::prepareStop(ConstMSEdgeVector& edges,
722 : StopParVector& stops,
723 : double& lastPos, const MSEdge* stopEdge, double stopPos,
724 : const MSStoppingPlace* stopPlace,
725 : const std::string& action, const Reservation* res, const bool isPickup) {
726 : assert(!edges.empty());
727 5296 : if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
728 907 : stopPos = stopPlace->getEndLanePosition();
729 : }
730 5296 : if (stopPos < lastPos && stopPos + NUMERICAL_EPS >= lastPos) {
731 : stopPos = lastPos;
732 : }
733 : bool addedEdge = false;
734 :
735 5296 : if (stops.empty()) {
736 : // check brakeGap
737 1928 : double distToStop = stopPos - lastPos;
738 1928 : const double brakeGap = myHolder.getBrakeGap();
739 1928 : if (myHolder.getLane() != nullptr && myHolder.getLane()->isInternal()) {
740 6 : distToStop += myHolder.getLane()->getLength();
741 : }
742 1928 : if (stopEdge != edges.back()) {
743 1654 : distToStop += edges.back()->getLength();
744 1654 : if (distToStop < brakeGap) {
745 : // the distance between current edge and stop edge may be small
746 22 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = MSRoutingEngine::getRouterTT(myHolder.getRNGIndex(), myHolder.getVClass());
747 : ConstMSEdgeVector toFirstStop;
748 11 : router.compute(edges.back(), stopEdge, &myHolder, SIMSTEP, toFirstStop, true);
749 43 : for (int i = 1; i < (int)toFirstStop.size() - 1; i++) {
750 32 : distToStop += toFirstStop[i]->getLength();
751 : }
752 11 : }
753 : }
754 1928 : if (distToStop < brakeGap) {
755 : // circle back to stopEdge
756 : //std::cout << SIMTIME << " taxi=" << getID() << " brakeGap=" << brakeGap << " distToStop=" << distToStop << "\n";
757 190 : edges.push_back(stopEdge);
758 : addedEdge = true;
759 : }
760 : }
761 :
762 5296 : if (stopEdge == edges.back() && !stops.empty()) {
763 1023 : if (stopPos >= lastPos && stopPos <= stops.back().endPos) {
764 : // no new stop and no adaption needed
765 852 : stops.back().actType += "," + action;
766 852 : return;
767 : }
768 171 : if (stopPos >= lastPos && stopPos <= lastPos + myHolder.getVehicleType().getLength()) {
769 : // stop length adaption needed
770 0 : stops.back().endPos = MIN2(lastPos + myHolder.getVehicleType().getLength(), stopEdge->getLength());
771 0 : stops.back().actType += "," + action;
772 0 : return;
773 : }
774 : }
775 4444 : if (!addedEdge && (stopEdge != edges.back() || stopPos < lastPos)) {
776 : //std::cout << SIMTIME << " stopPos=" << stopPos << " lastPos=" << lastPos << "\n";
777 4017 : edges.push_back(stopEdge);
778 : }
779 4444 : lastPos = stopPos;
780 4444 : SUMOVehicleParameter::Stop stop;
781 4444 : stop.lane = getStopLane(stopEdge, action)->getID();
782 4444 : if (stopPlace != nullptr && &stopPlace->getLane().getEdge() == stopEdge) {
783 672 : stop.startPos = stopPlace->getBeginLanePosition();
784 672 : stop.endPos = stopPlace->getEndLanePosition();
785 : const SumoXMLTag tag = stopPlace->getElement();
786 672 : if (tag == SUMO_TAG_BUS_STOP || tag == SUMO_TAG_TRAIN_STOP) {
787 : stop.busstop = stopPlace->getID();
788 245 : } else if (tag == SUMO_TAG_PARKING_AREA) {
789 : stop.parkingarea = stopPlace->getID();
790 240 : } else if (tag == SUMO_TAG_CONTAINER_STOP) {
791 : stop.containerstop = stopPlace->getID();
792 : }
793 : } else {
794 3772 : stop.startPos = stopPos;
795 3772 : stop.endPos = MAX2(stopPos, MIN2(myHolder.getVehicleType().getLength(), stopEdge->getLength()));
796 3772 : stop.parametersSet |= STOP_START_SET | STOP_END_SET;
797 : }
798 8888 : stop.parking = SUMOVehicleParameter::parseParkingType(myHolder.getStringParam("device.taxi.parking", false, "true"));
799 : stop.actType = action;
800 4444 : stop.index = STOP_INDEX_END;
801 : // In case of prebooking if person is not there/ comes to late for pickup set maximum waiting time:
802 4444 : SUMOTime earliestPickupTime = res->earliestPickupTime;
803 4444 : if (isPickup && earliestPickupTime >= 0) {
804 138 : stop.waitUntil = earliestPickupTime;
805 : // TODO: replace hard coded extension with parameter
806 138 : stop.extension = static_cast<SUMOTime>(3 * 60 * 1000); // 3mins
807 : }
808 4444 : stops.push_back(stop);
809 4444 : }
810 :
811 :
812 : MSLane*
813 4444 : MSDevice_Taxi::getStopLane(const MSEdge* edge, const std::string& action) {
814 4444 : const std::vector<MSLane*>* allowedLanes = edge->allowedLanes(myHolder.getVClass());
815 4444 : if (allowedLanes == nullptr) {
816 0 : throw ProcessError("Taxi vehicle '" + myHolder.getID() + "' cannot stop on edge '" + edge->getID() + "' (" + action + ")");
817 : }
818 4444 : return allowedLanes->front();
819 : }
820 :
821 : bool
822 1134195 : MSDevice_Taxi::isEmpty() {
823 1134195 : return myState == EMPTY;
824 : }
825 :
826 :
827 : bool
828 29249 : MSDevice_Taxi::allowsBoarding(const MSTransportable* t) const {
829 29249 : return myCustomers.count(t) != 0;
830 : }
831 :
832 :
833 : void
834 737573 : MSDevice_Taxi::updateMove(const SUMOTime traveltime, const double travelledDist) {
835 737573 : if (myHolder.getPersonNumber() > 0 || myHolder.getContainerNumber() > 0) {
836 138504 : myOccupiedDistance += travelledDist;
837 138504 : myOccupiedTime += traveltime;
838 : }
839 737573 : if (isEmpty()) {
840 539594 : if (MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
841 537536 : myIdleAlgorithm->idle(this);
842 537535 : if (myRoutingDevice != nullptr) {
843 : // prevent rerouting during idling (#11079)
844 : myRoutingDevice->setActive(false);
845 : }
846 2058 : } else if (!myReachedServiceEnd) {
847 252 : WRITE_WARNINGF(TL("Taxi '%' reaches scheduled end of service at time=%."), myHolder.getID(), time2string(SIMSTEP));
848 84 : myReachedServiceEnd = true;
849 : }
850 197979 : } else if (myRoutingDevice != nullptr) {
851 : myRoutingDevice->setActive(true);
852 : }
853 737572 : if (myHolder.isStopped() && (isEmpty() || MSGlobals::gUseMesoSim) && myHolder.getNextStop().endBoarding > myServiceEnd) {
854 : // limit duration of stop (but only for idling-related stops)
855 3722 : myHolder.getNextStopMutable().endBoarding = myServiceEnd;
856 : }
857 : #ifdef DEBUG_DISPATCH
858 : if (DEBUG_COND && myIsStopped != myHolder.isStopped()) {
859 : std::cout << SIMTIME << " updateMove veh=" << myHolder.getID() << " myIsStopped=" << myIsStopped << " myHolderStopped=" << myHolder.isStopped() << " myState=" << myState << "\n";
860 : }
861 : #endif
862 737572 : myIsStopped = myHolder.isStopped();
863 737572 : }
864 :
865 :
866 : bool
867 705118 : MSDevice_Taxi::notifyMove(SUMOTrafficObject& /*tObject*/, double oldPos,
868 : double newPos, double /*newSpeed*/) {
869 705118 : updateMove(DELTA_T, newPos - oldPos);
870 705117 : return true; // keep the device
871 : }
872 :
873 :
874 : void
875 32455 : MSDevice_Taxi::notifyMoveInternal(const SUMOTrafficObject& /* veh */,
876 : const double /* frontOnLane */,
877 : const double timeOnLane,
878 : const double /* meanSpeedFrontOnLane */,
879 : const double /* meanSpeedVehicleOnLane */,
880 : const double travelledDistanceFrontOnLane,
881 : const double /* travelledDistanceVehicleOnLane */,
882 : const double /* meanLengthOnLane */) {
883 32455 : updateMove(TIME2STEPS(timeOnLane), travelledDistanceFrontOnLane);
884 32455 : }
885 :
886 :
887 : bool
888 83003 : MSDevice_Taxi::notifyEnter(SUMOTrafficObject& /*veh*/, MSMoveReminder::Notification /*reason*/, const MSLane* /* enteredLane */) {
889 83003 : if (isEmpty() && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
890 51364 : myIdleAlgorithm->idle(this);
891 : }
892 83003 : return true; // keep the device
893 : }
894 :
895 :
896 : void
897 2693 : MSDevice_Taxi::customerEntered(const MSTransportable* t) {
898 2693 : myState |= OCCUPIED;
899 2693 : if (!hasFuturePickup()) {
900 2228 : myState &= ~PICKUP;
901 : }
902 12311 : for (const Reservation* res : myCurrentReservations) {
903 17130 : for (const MSTransportable* cand : res->persons) {
904 10186 : if (cand == t) {
905 2674 : const_cast<Reservation*>(res)->state = Reservation::ONBOARD;
906 2674 : break;
907 : }
908 : }
909 : }
910 2693 : }
911 :
912 :
913 : void
914 2693 : MSDevice_Taxi::customerArrived(const MSTransportable* person) {
915 2693 : myCustomersServed++;
916 : myCustomers.erase(person);
917 2693 : if (myHolder.getPersonNumber() == 0 && myHolder.getContainerNumber() == 0) {
918 1836 : myState &= ~OCCUPIED;
919 1836 : if (myHolder.getStops().size() > 1 && (myState & PICKUP) == 0) {
920 0 : WRITE_WARNINGF(TL("All customers left vehicle '%' at time=% but there are % remaining stops"),
921 : myHolder.getID(), time2string(SIMSTEP), myHolder.getStops().size() - 1);
922 0 : while (myHolder.getStops().size() > 1) {
923 0 : myHolder.abortNextStop(1);
924 : }
925 : }
926 : }
927 2693 : if (isEmpty()) {
928 : // cleanup
929 3612 : for (const Reservation* res : myCurrentReservations) {
930 1812 : myDispatcher->fulfilledReservation(res);
931 : }
932 : myCurrentReservations.clear();
933 1800 : checkTaskSwap();
934 1800 : if (isEmpty()) {
935 1796 : if (MSGlobals::gUseMesoSim && MSNet::getInstance()->getCurrentTimeStep() < myServiceEnd) {
936 580 : myIdleAlgorithm->idle(this);
937 : }
938 : }
939 : } else {
940 : // check whether a single reservation has been fulfilled
941 5315 : for (auto resIt = myCurrentReservations.begin(); resIt != myCurrentReservations.end();) {
942 : bool fulfilled = true;
943 5619 : for (const MSTransportable* t : (*resIt)->persons) {
944 : if (myCustomers.count(t) != 0) {
945 : fulfilled = false;
946 : break;
947 : }
948 : }
949 4422 : if (fulfilled) {
950 645 : myDispatcher->fulfilledReservation(*resIt);
951 : resIt = myCurrentReservations.erase(resIt);
952 : } else {
953 : ++resIt;
954 : }
955 : }
956 : }
957 2693 : }
958 :
959 :
960 : void
961 1800 : MSDevice_Taxi::checkTaskSwap() {
962 3600 : const std::string swapGroup = myHolder.getStringParam("device.taxi.swapGroup", false, "");
963 1800 : if (swapGroup != "") {
964 12 : SUMOAbstractRouter<MSEdge, SUMOVehicle>& router = myDispatcher->getRouter();
965 12 : const double stopTime = myHolder.isStopped() ? MAX2(0.0, STEPS2TIME(myHolder.getNextStop().duration)) : 0;
966 : double maxSaving = 0;
967 : MSDevice_Taxi* bestSwap = nullptr;
968 36 : for (MSDevice_Taxi* taxi : myFleet) {
969 48 : if (taxi->getHolder().hasDeparted() && taxi->getState() == PICKUP
970 60 : && taxi->getHolder().getStringParam("device.taxi.swapGroup", false, "") == swapGroup) {
971 : SUMOVehicle& veh = taxi->getHolder();
972 4 : const MSStop& stop = veh.getNextStop();
973 4 : ConstMSEdgeVector toPickup(veh.getCurrentRouteEdge(), stop.edge + 1);
974 4 : const double cost = router.recomputeCostsPos(toPickup, &veh, veh.getPositionOnLane(), stop.pars.endPos, SIMSTEP);
975 : ConstMSEdgeVector toPickup2;
976 4 : router.compute(myHolder.getEdge(), myHolder.getPositionOnLane(), *stop.edge, stop.pars.endPos, &myHolder, SIMSTEP, toPickup2, true);
977 4 : if (!toPickup2.empty()) {
978 4 : const double cost2 = router.recomputeCostsPos(toPickup2, &myHolder, myHolder.getPositionOnLane(), stop.pars.endPos, SIMSTEP);
979 4 : const double saving = cost - cost2 - stopTime;
980 : //std::cout << SIMTIME << " taxi=" << getID() << " other=" << veh.getID() << " cost=" << cost << " cost2=" << cost2 << " stopTime=" << stopTime << " saving=" << saving << " route1=" << toString(toPickup) << " route2=" << toString(toPickup2) << "\n";
981 4 : if (saving > maxSaving) {
982 : maxSaving = saving;
983 : bestSwap = taxi;
984 : }
985 : }
986 4 : }
987 : }
988 12 : if (maxSaving > SWAP_THRESHOLD) {
989 : #ifdef DEBUG_DISPATCH
990 : if (DEBUG_COND) {
991 : std::cout << SIMTIME << " taxi=" << myHolder.getID() << " swapWith=" << bestSwap->getHolder().getID() << " saving=" << maxSaving << " lastDispatch=";
992 : for (const Reservation* res : bestSwap->myLastDispatch) {
993 : std::cout << toString(res->persons) << "; ";
994 : }
995 : std::cout << "\n";
996 : }
997 : #endif
998 4 : dispatchShared(bestSwap->myLastDispatch);
999 : bestSwap->myCurrentReservations.clear();
1000 : bestSwap->myCustomers.clear();
1001 4 : bestSwap->myState = EMPTY;
1002 12 : while (bestSwap->getHolder().hasStops()) {
1003 8 : bestSwap->getHolder().abortNextStop();
1004 : }
1005 8 : for (const Reservation* res : myCurrentReservations) {
1006 4 : myDispatcher->swappedRunning(res, this);
1007 : }
1008 : }
1009 : }
1010 1800 : }
1011 :
1012 :
1013 : bool
1014 2693 : MSDevice_Taxi::hasFuturePickup() {
1015 7788 : for (const auto& stop : myHolder.getStops()) {
1016 5560 : if (stop.reached) {
1017 2683 : continue;
1018 : }
1019 2877 : if (stop.pars.permitted.size() > 0) {
1020 : return true;
1021 : }
1022 : }
1023 : return false;
1024 : }
1025 :
1026 : void
1027 1084 : MSDevice_Taxi::generateOutput(OutputDevice* tripinfoOut) const {
1028 1084 : if (tripinfoOut != nullptr) {
1029 1060 : tripinfoOut->openTag("taxi");
1030 1060 : tripinfoOut->writeAttr("customers", toString(myCustomersServed));
1031 1060 : tripinfoOut->writeAttr("occupiedDistance", toString(myOccupiedDistance));
1032 1060 : tripinfoOut->writeAttr("occupiedTime", time2string(myOccupiedTime));
1033 2120 : tripinfoOut->closeTag();
1034 : }
1035 1084 : }
1036 :
1037 : std::string
1038 1724 : MSDevice_Taxi::getParameter(const std::string& key) const {
1039 1724 : if (key == "customers") {
1040 0 : return toString(myCustomersServed);
1041 1724 : } else if (key == "occupiedDistance") {
1042 0 : return toString(myOccupiedDistance);
1043 1724 : } else if (key == "occupiedTime") {
1044 0 : return toString(STEPS2TIME(myOccupiedTime));
1045 1724 : } else if (key == "state") {
1046 1646 : return toString(myState);
1047 78 : } else if (key == "currentCustomers") {
1048 14 : return joinNamedToStringSorting(myCustomers, " ");
1049 64 : } else if (key == "pickUpDuration") {
1050 64 : return myHolder.getStringParam("device.taxi.pickUpDuration", false, "0");
1051 32 : } else if (key == "dropOffDuration") {
1052 64 : return myHolder.getStringParam("device.taxi.dropOffDuration", false, "60");
1053 : }
1054 0 : throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
1055 : }
1056 :
1057 :
1058 : void
1059 20 : MSDevice_Taxi::setParameter(const std::string& key, const std::string& value) {
1060 : double doubleValue;
1061 : try {
1062 20 : doubleValue = StringUtils::toDouble(value);
1063 0 : } catch (NumberFormatException&) {
1064 0 : throw InvalidArgument("Setting parameter '" + key + "' requires a number for device of type '" + deviceName() + "'");
1065 0 : }
1066 20 : if (key == "pickUpDuration" || key == "dropOffDuration") {
1067 : // store as generic vehicle parameters
1068 20 : ((SUMOVehicleParameter&)myHolder.getParameter()).setParameter("device.taxi." + key, value);
1069 : } else {
1070 : UNUSED_PARAMETER(doubleValue);
1071 0 : throw InvalidArgument("Setting parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
1072 : }
1073 20 : }
1074 :
1075 :
1076 : void
1077 31 : MSDevice_Taxi::saveState(OutputDevice& out) const {
1078 31 : out.openTag(SUMO_TAG_DEVICE);
1079 31 : out.writeAttr(SUMO_ATTR_ID, getID());
1080 31 : std::ostringstream internals;
1081 31 : internals << myState
1082 31 : << " " << myCustomersServed
1083 62 : << " " << myOccupiedDistance
1084 31 : << " " << myOccupiedTime;
1085 31 : out.writeAttr(SUMO_ATTR_STATE, internals.str());
1086 31 : if (myCustomers.size() > 0) {
1087 20 : out.writeAttr(SUMO_ATTR_CUSTOMERS, joinNamedToStringSorting(myCustomers, " "));
1088 : }
1089 31 : if (myCurrentReservations.size() > 0) {
1090 20 : out.writeAttr(SUMO_ATTR_RESERVATIONS, joinNamedToStringSorting(myCurrentReservations, " "));
1091 : }
1092 31 : out.closeTag();
1093 31 : }
1094 :
1095 :
1096 : void
1097 31 : MSDevice_Taxi::loadState(const SUMOSAXAttributes& attrs) {
1098 31 : bool ok = true;
1099 31 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
1100 31 : bis >> myState;
1101 31 : bis >> myCustomersServed;
1102 31 : bis >> myOccupiedDistance;
1103 31 : bis >> myOccupiedTime;
1104 31 : if (attrs.hasAttribute(SUMO_ATTR_CUSTOMERS)) {
1105 48 : for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_CUSTOMERS, getID().c_str(), ok)) {
1106 28 : myStateLoadedCustomers[id] = this;
1107 20 : }
1108 : }
1109 31 : if (attrs.hasAttribute(SUMO_ATTR_RESERVATIONS)) {
1110 40 : for (const std::string& id : attrs.get<std::vector<std::string> >(SUMO_ATTR_RESERVATIONS, getID().c_str(), ok)) {
1111 20 : myStateLoadedReservations[id] = this;
1112 20 : }
1113 : }
1114 31 : }
1115 :
1116 :
1117 : void
1118 439 : MSDevice_Taxi::finalizeLoadState() {
1119 439 : if (myStateLoadedCustomers.size() > 0) {
1120 14 : MSNet* net = MSNet::getInstance();
1121 14 : MSTransportableControl* pc = net->hasPersons() ? &net->getPersonControl() : nullptr;
1122 14 : MSTransportableControl* cc = net->hasContainers() ? &net->getContainerControl() : nullptr;
1123 42 : for (auto item : myStateLoadedCustomers) {
1124 28 : MSTransportable* t = nullptr;
1125 28 : if (pc != nullptr) {
1126 28 : t = pc->get(item.first);
1127 : }
1128 28 : if (t == nullptr && cc != nullptr) {
1129 0 : t = cc->get(item.first);
1130 : }
1131 28 : if (t == nullptr) {
1132 0 : WRITE_ERRORF("Could not find taxi customer '%'. Ensure state contains transportables", item.first);
1133 : } else {
1134 28 : item.second->myCustomers.insert(t);
1135 : }
1136 : }
1137 : }
1138 439 : if (myStateLoadedReservations.size() > 0) {
1139 : assert(myDispatcher != nullptr);
1140 : std::map<std::string, const Reservation*> resLookup;
1141 62 : for (const Reservation* res : myDispatcher->getReservations()) {
1142 48 : resLookup[res->getID()] = res;
1143 14 : }
1144 42 : for (const Reservation* res : myDispatcher->getRunningReservations()) {
1145 28 : resLookup[res->getID()] = res;
1146 14 : }
1147 34 : for (auto item : myStateLoadedReservations) {
1148 : auto it = resLookup.find(item.first);
1149 20 : if (it == resLookup.end()) {
1150 0 : WRITE_ERRORF("Could not find taxi reservation '%'.", item.first);
1151 : } else {
1152 20 : item.second->myCurrentReservations.insert(it->second);
1153 : }
1154 : }
1155 : }
1156 : myStateLoadedCustomers.clear();
1157 : myStateLoadedReservations.clear();
1158 439 : }
1159 :
1160 :
1161 : bool
1162 64078 : MSDevice_Taxi::compatibleLine(const std::string& taxiLine, const std::string& rideLine) {
1163 74540 : return ((taxiLine == rideLine && StringUtils::startsWith(rideLine, "taxi") && StringUtils::startsWith(taxiLine, "taxi"))
1164 117694 : || (taxiLine == TAXI_SERVICE && StringUtils::startsWith(rideLine, "taxi:"))
1165 194411 : || (rideLine == TAXI_SERVICE && StringUtils::startsWith(taxiLine, "taxi:")));
1166 : }
1167 :
1168 : bool
1169 4667 : MSDevice_Taxi::compatibleLine(const Reservation* res) {
1170 4667 : return compatibleLine(myHolder.getParameter().line, res->line);
1171 : }
1172 :
1173 :
1174 : /****************************************************************************/
|