Line data Source code
1 : /****************************************************************************/
2 : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 : // Copyright (C) 2001-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_Transportable.cpp
15 : /// @author Daniel Krajzewicz
16 : /// @author Jakob Erdmann
17 : /// @author Michael Behrisch
18 : /// @author Laura Bieker
19 : /// @author Melanie Weber
20 : /// @author Andreas Kendziorra
21 : /// @date Fri, 30.01.2009
22 : ///
23 : // A device which is used to keep track of persons and containers riding with a vehicle
24 : /****************************************************************************/
25 : #include <config.h>
26 :
27 : #include <utils/xml/SUMOSAXAttributes.h>
28 : #include <microsim/output/MSStopOut.h>
29 : #include <microsim/MSNet.h>
30 : #include <microsim/MSEdge.h>
31 : #include <microsim/MSStop.h>
32 : #include <microsim/MSStoppingPlace.h>
33 : #include <microsim/transportables/MSPerson.h>
34 : #include <microsim/transportables/MSTransportableControl.h>
35 : #include <microsim/transportables/MSStageDriving.h>
36 : #include "MSDevice_Transportable.h"
37 : #include "MSDevice_Taxi.h"
38 :
39 :
40 : // ===========================================================================
41 : // method definitions
42 : // ===========================================================================
43 : // ---------------------------------------------------------------------------
44 : // static initialisation methods
45 : // ---------------------------------------------------------------------------
46 : MSDevice_Transportable*
47 4317724 : MSDevice_Transportable::buildVehicleDevices(SUMOVehicle& v, std::vector<MSVehicleDevice*>& into, const bool isContainer) {
48 8630431 : MSDevice_Transportable* device = new MSDevice_Transportable(v, isContainer ? "container_" + v.getID() : "person_" + v.getID(), isContainer);
49 5017 : into.push_back(device);
50 5017 : return device;
51 : }
52 :
53 :
54 : // ---------------------------------------------------------------------------
55 : // MSDevice_Transportable-methods
56 : // ---------------------------------------------------------------------------
57 4317724 : MSDevice_Transportable::MSDevice_Transportable(SUMOVehicle& holder, const std::string& id, const bool isContainer) :
58 : MSVehicleDevice(holder, id),
59 4317724 : myAmContainer(isContainer),
60 : myTransportables(),
61 4317724 : myStopped(holder.isStopped()),
62 4317724 : myOriginalType(&holder.getVehicleType()),
63 8635448 : myLoadedType(nullptr) {
64 4317724 : const std::string key = "device." + deviceName() + ".loadedType";
65 12948155 : const std::string loadedTypeID = holder.getStringParam(key);
66 4317724 : if (loadedTypeID != "") {
67 4312719 : myLoadedType = MSNet::getInstance()->getVehicleControl().getVType(loadedTypeID, getEquipmentRNG());
68 4312719 : if (myLoadedType == nullptr) {
69 12938121 : throw InvalidArgument(TLF("Vehicle type '%' in parameter '%' of vehicle '%' is not known.", loadedTypeID, key, holder.getID()));
70 : }
71 : }
72 8630431 : }
73 :
74 :
75 10034 : MSDevice_Transportable::~MSDevice_Transportable() {
76 : // flush any unfortunate riders still remaining
77 5027 : for (auto it = myTransportables.begin(); it != myTransportables.end();) {
78 10 : MSTransportable* transportable = *it;
79 40 : WRITE_WARNING((myAmContainer ? "Removing container '" : "Removing person '") + transportable->getID() +
80 : "' at removal of vehicle '" + myHolder.getID() + "'");
81 10 : MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(transportable->getCurrentStage());
82 10 : if (stage != nullptr) {
83 10 : stage->setVehicle(nullptr);
84 : }
85 10 : if (myAmContainer) {
86 0 : MSNet::getInstance()->getContainerControl().erase(transportable);
87 : } else {
88 10 : MSNet::getInstance()->getPersonControl().erase(transportable);
89 : }
90 10 : it = myTransportables.erase(it);
91 : }
92 10034 : }
93 :
94 : void
95 24739 : MSDevice_Transportable::notifyMoveInternal(const SUMOTrafficObject& veh,
96 : const double /* frontOnLane */,
97 : const double /* timeOnLane */,
98 : const double /* meanSpeedFrontOnLane */,
99 : const double /* meanSpeedVehicleOnLane */,
100 : const double travelledDistanceFrontOnLane,
101 : const double /* travelledDistanceVehicleOnLane */,
102 : const double /* meanLengthOnLane */) {
103 24739 : notifyMove(const_cast<SUMOTrafficObject&>(veh), -1, travelledDistanceFrontOnLane, veh.getEdge()->getVehicleMaxSpeed(&veh));
104 24739 : }
105 :
106 : bool
107 44 : MSDevice_Transportable::anyLeavingAtStop(const MSStop& stop) const {
108 54 : for (const MSTransportable* t : myTransportables) {
109 44 : MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(t->getCurrentStage());
110 44 : if (stage->canLeaveVehicle(t, myHolder, stop)) {
111 : return true;
112 : }
113 : }
114 : return false;
115 : }
116 :
117 :
118 : void
119 12 : MSDevice_Transportable::transferAtSplitOrJoin(MSBaseVehicle* otherVeh) {
120 12 : const MSStop& stop = myHolder.getNextStop();
121 30 : for (auto it = myTransportables.begin(); it != myTransportables.end();) {
122 18 : MSTransportable* t = *it;
123 18 : if (t->getNumRemainingStages() > 1) {
124 12 : MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(t->getCurrentStage());
125 12 : if (stage->canLeaveVehicle(t, myHolder, stop)) {
126 12 : MSStageDriving* const stage2 = dynamic_cast<MSStageDriving*>(t->getNextStage(1));
127 12 : if (stage2 && stage2->isWaitingFor(otherVeh)) {
128 12 : auto locker = myHolder.getScopeLock();
129 12 : it = myTransportables.erase(it);
130 : // proceeding registers t as waiting on edge
131 12 : t->proceed(MSNet::getInstance(), SIMSTEP);
132 12 : MSTransportableControl& tc = (t->isPerson() ?
133 3 : MSNet::getInstance()->getPersonControl() :
134 9 : MSNet::getInstance()->getContainerControl());
135 12 : tc.abortWaitingForVehicle(t);
136 12 : t->getEdge()->removeTransportable(t);
137 12 : otherVeh->addTransportable(t);
138 12 : stage2->setVehicle(otherVeh);
139 : continue;
140 : }
141 : }
142 : }
143 : it++;
144 : }
145 12 : }
146 :
147 :
148 : bool
149 84471 : MSDevice_Transportable::willTransferAtJoin(const MSTransportable* t, const MSBaseVehicle* joinVeh) {
150 84471 : if (joinVeh && t->getNumRemainingStages() > 1) {
151 6 : MSStageDriving* const stage2 = dynamic_cast<MSStageDriving*>(t->getNextStage(1));
152 6 : return stage2->isWaitingFor(joinVeh);
153 : }
154 : return false;
155 : }
156 :
157 :
158 : bool
159 1634043 : MSDevice_Transportable::notifyMove(SUMOTrafficObject& /*tObject*/, double /*oldPos*/, double newPos, double newSpeed) {
160 1634043 : SUMOVehicle& veh = myHolder;
161 1634043 : const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
162 1634043 : if (myStopped) {
163 943753 : if (!veh.isStopped()) {
164 7285 : const SUMOTime freeFlowTimeCorrection = MSGlobals::gUseMesoSim ? TIME2STEPS(newPos / newSpeed) : 0;
165 19826 : for (MSTransportable* const transportable : myTransportables) {
166 12541 : transportable->setDeparted(currentTime - freeFlowTimeCorrection);
167 : }
168 7285 : myStopped = false;
169 : }
170 : } else {
171 690290 : if (veh.isStopped()) {
172 80853 : myStopped = true;
173 80853 : MSStop& stop = veh.getNextStopMutable();
174 80853 : const MSVehicle* joinVeh = dynamic_cast<MSVehicle*>(MSNet::getInstance()->getVehicleControl().getVehicle(stop.pars.join));
175 80853 : const SUMOTime boardingDuration = veh.getVehicleType().getLoadingDuration(!myAmContainer);
176 : int numUnloaded = 0;
177 98300 : for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
178 92450 : MSTransportable* transportable = *i;
179 92450 : MSStageDriving* const stage = dynamic_cast<MSStageDriving*>(transportable->getCurrentStage());
180 92450 : if (stage->canLeaveVehicle(transportable, myHolder, stop) && !willTransferAtJoin(transportable, joinVeh)) {
181 84465 : SUMOTime& timeForNext = myAmContainer ? stop.timeToLoadNextContainer : stop.timeToBoardNextPerson;
182 84465 : MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
183 71322 : if (taxiDevice != nullptr && timeForNext == 0 && !MSGlobals::gUseMesoSim) {
184 : // taxi passengers must leave at the end of the stop duration
185 1154 : timeForNext = stop.pars.started + stop.pars.duration;
186 : }
187 84465 : if (timeForNext - DELTA_T > currentTime) {
188 : // try deboarding again in the next step
189 75003 : myStopped = false;
190 75003 : break;
191 : }
192 9462 : if (stage->getDestinationStop() != nullptr) {
193 4905 : stage->getDestinationStop()->addTransportable(transportable);
194 : }
195 :
196 : SUMOTime arrivalTime = currentTime;
197 9462 : if (MSGlobals::gUseMesoSim) {
198 : // no boarding / unboarding time in meso
199 1766 : arrivalTime += 1;
200 : } else {
201 7696 : const SUMOTime boardingTime = (SUMOTime)((double)boardingDuration * transportable->getVehicleType().getBoardingFactor());
202 7696 : if (timeForNext > currentTime - DELTA_T) {
203 6377 : timeForNext += boardingTime;
204 : } else {
205 1319 : timeForNext = currentTime + boardingTime;
206 : }
207 : }
208 : //ensure that vehicle stops long enough for deboarding
209 9462 : stop.duration = MAX2(stop.duration, timeForNext - currentTime);
210 :
211 9462 : veh.removeTransportableMass(transportable);
212 9462 : auto locker = myHolder.getScopeLock();
213 9462 : i = myTransportables.erase(i); // erase first in case proceed throws an exception
214 9462 : numUnloaded++;
215 9462 : if (taxiDevice != nullptr) {
216 2294 : taxiDevice->customerArrived(transportable);
217 : }
218 9462 : if (!transportable->proceed(MSNet::getInstance(), arrivalTime)) {
219 4960 : if (myAmContainer) {
220 369 : MSNet::getInstance()->getContainerControl().erase(transportable);
221 : } else {
222 4591 : MSNet::getInstance()->getPersonControl().erase(transportable);
223 : }
224 : }
225 9462 : if (MSStopOut::active()) {
226 : SUMOVehicle* vehicle = dynamic_cast<SUMOVehicle*>(&veh);
227 1900 : if (myAmContainer) {
228 118 : MSStopOut::getInstance()->unloadedContainers(vehicle, 1);
229 : } else {
230 1782 : MSStopOut::getInstance()->unloadedPersons(vehicle, 1);
231 : }
232 : }
233 : continue;
234 : }
235 : ++i;
236 : }
237 80853 : if (numUnloaded != 0) {
238 5814 : changeAttached();
239 : }
240 : }
241 : }
242 1634043 : return true;
243 : }
244 :
245 :
246 : bool
247 81941 : MSDevice_Transportable::notifyEnter(SUMOTrafficObject& veh, MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
248 81941 : if (reason == MSMoveReminder::NOTIFICATION_DEPARTED) {
249 2525 : const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
250 5490 : for (MSTransportable* const transportable : myTransportables) {
251 2965 : transportable->setDeparted(currentTime);
252 : }
253 : }
254 81941 : if (MSGlobals::gUseMesoSim) {
255 : // to trigger vehicle leaving
256 25226 : notifyMove(veh, -1., -1., -1.);
257 : }
258 81941 : return true;
259 : }
260 :
261 :
262 : bool
263 83128 : MSDevice_Transportable::notifyLeave(SUMOTrafficObject& veh, double /*lastPos*/,
264 : MSMoveReminder::Notification reason, const MSLane* /* enteredLane */) {
265 83128 : if (reason >= MSMoveReminder::NOTIFICATION_ARRIVED) {
266 4050 : auto locker = myHolder.getScopeLock();
267 8260 : for (std::vector<MSTransportable*>::iterator i = myTransportables.begin(); i != myTransportables.end();) {
268 4210 : MSTransportableControl& tc = myAmContainer ? MSNet::getInstance()->getContainerControl() : MSNet::getInstance()->getPersonControl();
269 4210 : MSTransportable* transportable = *i;
270 4210 : if (transportable->getDestination() != veh.getEdge()) {
271 182 : WRITE_WARNINGF("Teleporting % '%' from vehicle destination edge '%' to intended destination edge '%' time=%",
272 : myAmContainer ? "container" : "person", transportable->getID(), veh.getEdge()->getID(),
273 : transportable->getDestination()->getID(), time2string(SIMSTEP));
274 : tc.registerTeleportWrongDest();
275 : }
276 4210 : if (!transportable->proceed(MSNet::getInstance(), MSNet::getInstance()->getCurrentTimeStep(), true)) {
277 3987 : tc.erase(transportable);
278 : }
279 4210 : i = myTransportables.erase(i);
280 : }
281 : }
282 83128 : return true;
283 : }
284 :
285 :
286 : void
287 13900 : MSDevice_Transportable::addTransportable(MSTransportable* transportable) {
288 13900 : auto locker = myHolder.getScopeLock();
289 13900 : if (myTransportables.empty()) {
290 5738 : myOriginalType = &myHolder.getVehicleType();
291 : }
292 13900 : myTransportables.push_back(transportable);
293 13900 : if (MSStopOut::active()) {
294 2902 : if (myAmContainer) {
295 187 : MSStopOut::getInstance()->loadedContainers(&myHolder, 1);
296 : } else {
297 2715 : MSStopOut::getInstance()->loadedPersons(&myHolder, 1);
298 : }
299 : }
300 13900 : MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
301 : if (taxiDevice != nullptr) {
302 2309 : taxiDevice->customerEntered(transportable);
303 : }
304 13900 : changeAttached();
305 13900 : }
306 :
307 :
308 : void
309 206 : MSDevice_Transportable::removeTransportable(MSTransportable* transportable) {
310 206 : auto it = std::find(myTransportables.begin(), myTransportables.end(), transportable);
311 206 : if (it != myTransportables.end()) {
312 206 : auto locker = myHolder.getScopeLock();
313 206 : myTransportables.erase(it);
314 206 : if (MSStopOut::active() && myHolder.isStopped()) {
315 55 : if (myAmContainer) {
316 20 : MSStopOut::getInstance()->unloadedContainers(&myHolder, 1);
317 : } else {
318 35 : MSStopOut::getInstance()->unloadedPersons(&myHolder, 1);
319 : }
320 : }
321 206 : MSDevice_Taxi* taxiDevice = static_cast<MSDevice_Taxi*>(myHolder.getDevice(typeid(MSDevice_Taxi)));
322 : if (taxiDevice != nullptr) {
323 15 : taxiDevice->customerArrived(transportable);
324 : }
325 206 : changeAttached();
326 : }
327 206 : }
328 :
329 :
330 : void
331 19920 : MSDevice_Transportable::changeAttached() {
332 19920 : if (myLoadedType != nullptr) {
333 168 : int perAttached = myAmContainer ? myLoadedType->getContainerCapacity() : myLoadedType->getPersonCapacity();
334 168 : if (perAttached > 0) {
335 168 : MSBaseVehicle& veh = dynamic_cast<MSBaseVehicle&>(myHolder);
336 168 : SUMOVehicleClass oldVC = myHolder.getVClass();
337 168 : const double numAttached = ceil(myTransportables.size() / perAttached);
338 168 : if (numAttached > 0.) {
339 148 : MSVehicleType* stype = &veh.getSingularType();
340 148 : stype->setVClass(myLoadedType->getVehicleClass());
341 148 : stype->setGUIShape(myLoadedType->getGuiShape());
342 148 : stype->setLength(myOriginalType->getLength() + numAttached * myLoadedType->getLength());
343 148 : stype->setMass(myOriginalType->getMass() + numAttached * myLoadedType->getMass());
344 : SUMOVTypeParameter& sparam = const_cast<SUMOVTypeParameter&>(stype->getParameter());
345 148 : sparam.carriageLength = myLoadedType->getParameter().carriageLength;
346 148 : sparam.locomotiveLength = myLoadedType->getParameter().locomotiveLength;
347 148 : sparam.carriageGap = myLoadedType->getParameter().carriageGap;
348 : } else {
349 20 : myHolder.replaceVehicleType(myOriginalType);
350 : }
351 168 : if (oldVC != myHolder.getVClass()) {
352 24 : veh.reroute(SIMSTEP, "device." + deviceName() + ".loadedType", veh.getRouterTT());
353 : }
354 : }
355 : }
356 19920 : }
357 :
358 :
359 : void
360 9 : MSDevice_Transportable::saveState(OutputDevice& out) const {
361 9 : out.openTag(SUMO_TAG_DEVICE);
362 9 : out.writeAttr(SUMO_ATTR_ID, getID());
363 : std::vector<std::string> internals;
364 9 : internals.push_back(toString(myStopped));
365 9 : out.writeAttr(SUMO_ATTR_STATE, toString(internals));
366 9 : out.closeTag();
367 9 : }
368 :
369 :
370 : void
371 0 : MSDevice_Transportable::loadState(const SUMOSAXAttributes& attrs) {
372 0 : std::istringstream bis(attrs.getString(SUMO_ATTR_STATE));
373 0 : bis >> myStopped;
374 0 : }
375 :
376 :
377 : std::string
378 4 : MSDevice_Transportable::getParameter(const std::string& key) const {
379 4 : if (key == "IDList") {
380 : std::vector<std::string> ids;
381 8 : for (const MSTransportable* t : myTransportables) {
382 4 : ids.push_back(t->getID());
383 : }
384 8 : return toString(ids);
385 4 : }
386 0 : throw InvalidArgument("Parameter '" + key + "' is not supported for device of type '" + deviceName() + "'");
387 : }
388 :
389 :
390 : /****************************************************************************/
|