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