1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2001-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file MSInsertionControl.cpp
11 /// @author Christian Roessel
12 /// @author Daniel Krajzewicz
13 /// @author Axel Wegener
14 /// @author Michael Behrisch
15 /// @author Jakob Erdmann
16 /// @date Mon, 12 Mar 2001
17 /// @version $Id$
18 ///
19 // Inserts vehicles into the network when their departure time is reached
20 /****************************************************************************/
21
22
23 // ===========================================================================
24 // included modules
25 // ===========================================================================
26 #include <config.h>
27
28 #include <iostream>
29 #include <algorithm>
30 #include <cassert>
31 #include <iterator>
32 #include <utils/router/IntermodalRouter.h>
33 #include <microsim/devices/MSDevice_Routing.h>
34 #include <microsim/devices/MSRoutingEngine.h>
35 #include "MSGlobals.h"
36 #include "MSVehicle.h"
37 #include "MSVehicleControl.h"
38 #include "MSLane.h"
39 #include "MSEdge.h"
40 #include "MSNet.h"
41 #include "MSRouteHandler.h"
42 #include "MSInsertionControl.h"
43
44
45 // ===========================================================================
46 // member method definitions
47 // ===========================================================================
MSInsertionControl(MSVehicleControl & vc,SUMOTime maxDepartDelay,bool eagerInsertionCheck,int maxVehicleNumber,SUMOTime randomDepartOffset)48 MSInsertionControl::MSInsertionControl(MSVehicleControl& vc,
49 SUMOTime maxDepartDelay,
50 bool eagerInsertionCheck,
51 int maxVehicleNumber,
52 SUMOTime randomDepartOffset) :
53 myVehicleControl(vc),
54 myMaxDepartDelay(maxDepartDelay),
55 myEagerInsertionCheck(eagerInsertionCheck),
56 myMaxVehicleNumber(maxVehicleNumber),
57 myPendingEmitsUpdateTime(SUMOTime_MIN) {
58 myMaxRandomDepartOffset = randomDepartOffset;
59 RandHelper::initRandGlobal(&myFlowRNG);
60 }
61
62
~MSInsertionControl()63 MSInsertionControl::~MSInsertionControl() {
64 for (std::vector<Flow>::iterator i = myFlows.begin(); i != myFlows.end(); ++i) {
65 delete (i->pars);
66 }
67 }
68
69
70 void
add(SUMOVehicle * veh)71 MSInsertionControl::add(SUMOVehicle* veh) {
72 myAllVeh.add(veh);
73 }
74
75
76 bool
addFlow(SUMOVehicleParameter * const pars,int index)77 MSInsertionControl::addFlow(SUMOVehicleParameter* const pars, int index) {
78 const bool loadingFromState = index >= 0;
79 if (myFlowIDs.count(pars->id) > 0) {
80 if (loadingFromState) {
81 // flows loaded from simulation state must be unique
82 return false;
83 }
84 // set actual parameters for a state-loaded flow (for which only index is known)
85 for (Flow& flow : myFlows) {
86 // if the flow was loaded from state this is recognizable by having
87 // neither repetitionNumber nor repetitionProbability
88 if (flow.pars->id == pars->id && flow.pars->repetitionNumber == -1 && flow.pars->repetitionProbability == -1) {
89 if (flow.pars->wasSet(VEHPARS_FORCE_REROUTE)) {
90 pars->parametersSet |= VEHPARS_FORCE_REROUTE;
91 }
92 delete flow.pars;
93 flow.pars = pars;
94 return true;
95 }
96 }
97 return false;
98 } else {
99 Flow flow;
100 flow.pars = pars;
101 flow.index = loadingFromState ? index : 0;
102 myFlows.push_back(flow);
103 myFlowIDs.insert(pars->id);
104 return true;
105 }
106 }
107
108
109 int
emitVehicles(SUMOTime time)110 MSInsertionControl::emitVehicles(SUMOTime time) {
111 // check whether any vehicles shall be emitted within this time step
112 const bool havePreChecked = MSRoutingEngine::isEnabled();
113 if (myPendingEmits.empty() || (havePreChecked && myEmitCandidates.empty())) {
114 return 0;
115 }
116 int numEmitted = 0;
117 // we use buffering for the refused emits to save time
118 // for this, we have two lists; one contains previously refused emits, the second
119 // will be used to append those vehicles that will not be able to depart in this
120 // time step
121 MSVehicleContainer::VehicleVector refusedEmits;
122
123 // go through the list of previously refused vehicles, first
124 MSVehicleContainer::VehicleVector::const_iterator veh;
125 for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
126 if (havePreChecked && (myEmitCandidates.count(*veh) == 0)) {
127 refusedEmits.push_back(*veh);
128 } else {
129 numEmitted += tryInsert(time, *veh, refusedEmits);
130 }
131 }
132 myEmitCandidates.clear();
133 myPendingEmits = refusedEmits;
134 return numEmitted;
135 }
136
137
138 int
tryInsert(SUMOTime time,SUMOVehicle * veh,MSVehicleContainer::VehicleVector & refusedEmits)139 MSInsertionControl::tryInsert(SUMOTime time, SUMOVehicle* veh,
140 MSVehicleContainer::VehicleVector& refusedEmits) {
141 assert(veh->getParameter().depart < time + DELTA_T);
142 const MSEdge& edge = *veh->getEdge();
143 if (veh->isOnRoad()) {
144 return 1;
145 }
146 if ((myMaxVehicleNumber < 0 || (int)MSNet::getInstance()->getVehicleControl().getRunningVehicleNo() < myMaxVehicleNumber)
147 && edge.insertVehicle(*veh, time, false, myEagerInsertionCheck)) {
148 // Successful insertion
149 return 1;
150 }
151 if (myMaxDepartDelay >= 0 && time - veh->getParameter().depart > myMaxDepartDelay) {
152 // remove vehicles waiting too long for departure
153 myVehicleControl.deleteVehicle(veh, true);
154 } else if (edge.isVaporizing()) {
155 // remove vehicles if the edge shall be empty
156 myVehicleControl.deleteVehicle(veh, true);
157 } else if (myAbortedEmits.count(veh) > 0) {
158 // remove vehicles which shall not be inserted for some reason
159 myAbortedEmits.erase(veh);
160 myVehicleControl.deleteVehicle(veh, true);
161 } else {
162 // let the vehicle wait one step, we'll retry then
163 refusedEmits.push_back(veh);
164 }
165 edge.setLastFailedInsertionTime(time);
166 return 0;
167 }
168
169
170 void
checkCandidates(SUMOTime time,const bool preCheck)171 MSInsertionControl::checkCandidates(SUMOTime time, const bool preCheck) {
172 while (myAllVeh.anyWaitingBefore(time + DELTA_T)) {
173 const MSVehicleContainer::VehicleVector& top = myAllVeh.top();
174 copy(top.begin(), top.end(), back_inserter(myPendingEmits));
175 myAllVeh.pop();
176 }
177 if (preCheck) {
178 MSVehicleContainer::VehicleVector::const_iterator veh;
179 for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
180 SUMOVehicle* const v = *veh;
181 const MSEdge* const edge = v->getEdge();
182 if (edge->insertVehicle(*v, time, true, myEagerInsertionCheck)) {
183 myEmitCandidates.insert(v);
184 } else {
185 MSDevice_Routing* dev = static_cast<MSDevice_Routing*>(v->getDevice(typeid(MSDevice_Routing)));
186 if (dev != nullptr) {
187 dev->skipRouting(time);
188 }
189 }
190 }
191 }
192 }
193
194
195 void
determineCandidates(SUMOTime time)196 MSInsertionControl::determineCandidates(SUMOTime time) {
197 MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
198 for (std::vector<Flow>::iterator i = myFlows.begin(); i != myFlows.end();) {
199 SUMOVehicleParameter* pars = i->pars;
200 bool tryEmitByProb = pars->repetitionProbability > 0;
201 while ((pars->repetitionProbability < 0
202 && pars->repetitionsDone < pars->repetitionNumber
203 && pars->depart + pars->repetitionsDone * pars->repetitionOffset < time + DELTA_T)
204 || (tryEmitByProb
205 && pars->depart < time + DELTA_T
206 && pars->repetitionEnd > time
207 // only call rand if all other conditions are met
208 && RandHelper::rand(&myFlowRNG) < (pars->repetitionProbability * TS))
209 ) {
210 tryEmitByProb = false; // only emit one per step
211 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
212 newPars->id = pars->id + "." + toString(i->index);
213 newPars->depart = pars->repetitionProbability > 0 ? time : (SUMOTime)(pars->depart + pars->repetitionsDone * pars->repetitionOffset) + computeRandomDepartOffset();
214 pars->repetitionsDone++;
215 // try to build the vehicle
216 if (vehControl.getVehicle(newPars->id) == nullptr) {
217 const MSRoute* route = MSRoute::dictionary(pars->routeid);
218 MSVehicleType* vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
219 SUMOVehicle* vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes);
220 int quota = vehControl.getQuota();
221 if (quota > 0) {
222 vehControl.addVehicle(newPars->id, vehicle);
223 add(vehicle);
224 i->index++;
225 while (--quota > 0) {
226 SUMOVehicleParameter* quotaPars = new SUMOVehicleParameter(*pars);
227 quotaPars->id = pars->id + "." + toString(i->index);
228 quotaPars->depart = pars->repetitionProbability > 0 ? time :
229 (SUMOTime)(pars->depart + pars->repetitionsDone * pars->repetitionOffset) + computeRandomDepartOffset();
230 SUMOVehicle* vehicle = vehControl.buildVehicle(quotaPars, route, vtype, !MSGlobals::gCheckRoutes);
231 vehControl.addVehicle(quotaPars->id, vehicle);
232 add(vehicle);
233 i->index++;
234 }
235 } else {
236 vehControl.deleteVehicle(vehicle, true);
237 }
238 } else {
239 // strange: another vehicle with the same id already exists
240 if (MSGlobals::gStateLoaded) {
241 vehControl.discountStateLoaded();
242 break;
243 }
244 throw ProcessError("Another vehicle with the id '" + newPars->id + "' exists.");
245 }
246 }
247 if (pars->repetitionsDone == pars->repetitionNumber || (pars->repetitionProbability > 0 && pars->repetitionEnd <= time)) {
248 i = myFlows.erase(i);
249 MSRoute::checkDist(pars->routeid);
250 delete pars;
251 } else {
252 ++i;
253 }
254 }
255 checkCandidates(time, MSRoutingEngine::isEnabled());
256 }
257
258
259 int
getWaitingVehicleNo() const260 MSInsertionControl::getWaitingVehicleNo() const {
261 return (int)myPendingEmits.size();
262 }
263
264
265 int
getPendingFlowCount() const266 MSInsertionControl::getPendingFlowCount() const {
267 return (int)myFlows.size();
268 }
269
270
271 void
descheduleDeparture(const SUMOVehicle * veh)272 MSInsertionControl::descheduleDeparture(const SUMOVehicle* veh) {
273 myAbortedEmits.insert(veh);
274 }
275
276
277 void
alreadyDeparted(SUMOVehicle * veh)278 MSInsertionControl::alreadyDeparted(SUMOVehicle* veh) {
279 myPendingEmits.erase(std::remove(myPendingEmits.begin(), myPendingEmits.end(), veh), myPendingEmits.end());
280 myAllVeh.remove(veh);
281 }
282
283
284 void
clearPendingVehicles(const std::string & route)285 MSInsertionControl::clearPendingVehicles(const std::string& route) {
286 //clear out the refused vehicle list, deleting the vehicles entirely
287 MSVehicleContainer::VehicleVector::iterator veh;
288 for (veh = myPendingEmits.begin(); veh != myPendingEmits.end();) {
289 if ((*veh)->getRoute().getID() == route || route == "") {
290 myVehicleControl.deleteVehicle(*veh, true);
291 veh = myPendingEmits.erase(veh);
292 } else {
293 ++veh;
294 }
295 }
296 }
297
298
299 int
getPendingEmits(const MSLane * lane)300 MSInsertionControl::getPendingEmits(const MSLane* lane) {
301 if (MSNet::getInstance()->getCurrentTimeStep() > myPendingEmitsUpdateTime) {
302 // updated pending emits (only once per time step)
303 myPendingEmitsForLane.clear();
304 for (MSVehicleContainer::VehicleVector::const_iterator veh = myPendingEmits.begin(); veh != myPendingEmits.end(); ++veh) {
305 const MSLane* lane = (*veh)->getLane();
306 if (lane != nullptr) {
307 myPendingEmitsForLane[lane]++;
308 } else {
309 // no (tentative) departLane was set, increase count for all
310 // lanes of the depart edge
311 const MSEdge* edge = (*veh)->getEdge();
312 const std::vector<MSLane*>& lanes = edge->getLanes();
313 for (std::vector<MSLane*>::const_iterator i = lanes.begin(); i != lanes.end(); ++i) {
314 myPendingEmitsForLane[*i]++;
315 }
316 }
317 }
318 myPendingEmitsUpdateTime = MSNet::getInstance()->getCurrentTimeStep();
319 }
320 return myPendingEmitsForLane[lane];
321 }
322
323
324 void
adaptIntermodalRouter(MSNet::MSIntermodalRouter & router) const325 MSInsertionControl::adaptIntermodalRouter(MSNet::MSIntermodalRouter& router) const {
326 // fill the public transport router with pre-parsed public transport lines
327 for (const Flow& f : myFlows) {
328 if (f.pars->line != "") {
329 const MSRoute* const route = MSRoute::dictionary(f.pars->routeid);
330 router.getNetwork()->addSchedule(*f.pars, route == nullptr ? nullptr : &route->getStops());
331 }
332 }
333 }
334
335
336 void
saveState(OutputDevice & out)337 MSInsertionControl::saveState(OutputDevice& out) {
338 // save flow states
339 for (const Flow& flow : myFlows) {
340 out.openTag(SUMO_TAG_FLOWSTATE);
341 out.writeAttr(SUMO_ATTR_ID, flow.pars->id);
342 out.writeAttr(SUMO_ATTR_INDEX, flow.index);
343 if (flow.pars->wasSet(VEHPARS_FORCE_REROUTE)) {
344 out.writeAttr(SUMO_ATTR_REROUTE, true);
345 }
346 out.closeTag();
347 }
348 }
349
350
351 SUMOTime
computeRandomDepartOffset() const352 MSInsertionControl::computeRandomDepartOffset() const {
353 if (myMaxRandomDepartOffset > 0) {
354 // round to the closest usable simulation step
355 return DELTA_T * int((RandHelper::rand((int)myMaxRandomDepartOffset, MSRouteHandler::getParsingRNG()) + 0.5 * DELTA_T) / DELTA_T);
356 } else {
357 return 0;
358 }
359 }
360
361
362
363 /****************************************************************************/
364