MATSIM
AbstractQLink.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * *
4  * *********************************************************************** *
5  * *
6  * copyright : (C) 2010 by the members listed in the COPYING, *
7  * LICENSE and WARRANTY file. *
8  * email : info at matsim dot org *
9  * *
10  * *********************************************************************** *
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * See also COPYING, LICENSE and WARRANTY file *
17  * *
18  * *********************************************************************** */
19 
20 package org.matsim.core.mobsim.qsim.qnetsimengine;
21 
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedHashMap;
28 import java.util.LinkedHashSet;
29 import java.util.LinkedList;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Queue;
33 import java.util.Set;
34 import java.util.concurrent.ConcurrentHashMap;
35 
36 import org.apache.logging.log4j.LogManager;
37 import org.apache.logging.log4j.Logger;
38 import org.matsim.api.core.v01.Id;
47 import org.matsim.core.gbl.Gbl;
58 import org.matsim.vehicles.Vehicle;
59 
67 abstract class AbstractQLink implements QLinkI {
68  // yy The way forward might be to separate the "service link/network" from the "movement link/network". The
69  // service link would do things like park agents, accept additional agents on link, accept passengers or drivers waiting for
70  // cars, etc. Both the thread-based parallelization and the visualization then would have to treat those service links
71  // separately, which feels fairly messy. Maybe better leave things as they are. kai, mar'16
72 
74  continue_driving, rehandle, accepted
75 
76  }
77 
78  private static final Logger log = LogManager.getLogger(AbstractQLink.class);
79 
80  private final Link link;
81 
82 // private final QNetwork qnetwork ;
83  private NetElementActivationRegistry netElementActivationRegistry;
84  // (NOTE: via the qnetwork you reach the QNetsimEngine. That is the "global" thing. In contrast, via the netElementActivator,
85  // you reach the QNetsimEngineRunner. That is the thread that runs the QLink. Kai, mar'16
86 
87  // joint implementation for Customizable
88  private final Map<String, Object> customAttributes = new HashMap<>();
89 
90 // private final Map<Id<Vehicle>, QVehicle> parkedVehicles = new LinkedHashMap<>(10);
91  private final Map<Id<Vehicle>, QVehicle> parkedVehicles = new ConcurrentHashMap<>(10);
92 
93 // private final Map<Id<Person>, MobsimAgent> additionalAgentsOnLink = new LinkedHashMap<>();
94  private final Map<Id<Person>, MobsimAgent> additionalAgentsOnLink = new ConcurrentHashMap<>( ) ;
95 
96  private final Map<Id<Vehicle>, Queue<MobsimDriverAgent>> driversWaitingForCars = new LinkedHashMap<>();
97 
98  private final Map<Id<Person>, MobsimDriverAgent> driversWaitingForPassengers = new LinkedHashMap<>();
99 
100  private final Map<Id<Vehicle>, Set<MobsimAgent>> passengersWaitingForCars = new LinkedHashMap<>();
101 
107  private final Queue<QVehicle> waitingList = new LinkedList<>();
108 
109  private boolean active = false;
110 
111  private TransitQLink transitQLink;
112 
113  private final QNodeI toQNode ;
114 
115  private final NetsimEngineContext context;
116 
117  private final NetsimInternalInterface netsimEngine;
118  private final LinkSpeedCalculator linkSpeedCalculator;
119  private final VehicleHandler vehicleHandler;
120 
121  AbstractQLink(Link link, QNodeI toNode, NetsimEngineContext context, NetsimInternalInterface netsimEngine2, LinkSpeedCalculator linkSpeedCalculator, VehicleHandler vehicleHandler) {
122  this.link = link ;
123  this.toQNode = toNode ;
124  this.context = context;
125  this.netsimEngine = netsimEngine2;
126  this.linkSpeedCalculator = linkSpeedCalculator;
127  this.vehicleHandler = vehicleHandler;
128  }
129 
130  @Override
131  public QNodeI getToNode() {
132  return toQNode ;
133  }
134 
142  private void activateLink() {
143  if (!this.active) {
144  netElementActivationRegistry.registerLinkAsActive(this);
145  this.active = true;
146  }
147  // This is a bit involved since we do not want to ask the registry in every time step if the link is already active.
148  }
149  private static int wrnCnt = 0 ;
150 
151  public final void addParkedVehicle(MobsimVehicle vehicle, boolean isInitial) {
152  QVehicle qveh = (QVehicle) vehicle; // cast ok: when it gets here, it needs to be a qvehicle to work.
153 
154  if ( this.parkedVehicles.put(qveh.getId(), qveh) != null ) {
155  if ( wrnCnt < 1 ) {
156  wrnCnt++ ;
157  log.warn( "existing vehicle on link was just overwritten by other vehicle with same ID. Not clear what this means. Continuing anyways ...") ;
158  log.warn( Gbl.ONLYONCE ) ;
159  }
160  }
161  qveh.setCurrentLink(this.link);
162 
163  if (isInitial) {
164  vehicleHandler.handleInitialVehicleArrival(qveh, link);
165  }
166  }
167 
168  @Override
169  public final void addParkedVehicle(MobsimVehicle vehicle) {
170  addParkedVehicle(vehicle, true);
171  }
172 
173  /* package */ final boolean letVehicleArrive(QVehicle qveh) {
174  if (vehicleHandler.handleVehicleArrival(qveh, this.getLink())) {
175  addParkedVehicle(qveh, false);
176  double now = context.getSimTimer().getTimeOfDay();
177  context.getEventsManager().processEvent(new VehicleLeavesTrafficEvent(now , qveh.getDriver().getId(),
178  this.link.getId(), qveh.getId(), qveh.getDriver().getMode(), 1.0 ) ) ;
179 
180  this.netsimEngine.letVehicleArrive(qveh);
181  makeVehicleAvailableToNextDriver(qveh);
182  return true;
183  }
184 
185  return false;
186  }
187 
188  @Override
189  public final QVehicle removeParkedVehicle(Id<Vehicle> vehicleId) {
190  return this.parkedVehicles.remove(vehicleId);
191  }
192 
193  @Override
194  public QVehicle getParkedVehicle(Id<Vehicle> vehicleId) {
195  return this.parkedVehicles.get(vehicleId);
196  }
197 
198  private final void addDepartingVehicle(MobsimVehicle mvehicle) {
199  QVehicle vehicle = (QVehicle) mvehicle;
200  this.waitingList.add(vehicle);
201  vehicle.setCurrentLink(this.getLink());
202  this.activateLink();
203  vehicleHandler.handleVehicleDeparture(vehicle, link);
204  }
205 
206  @Override
207  public void registerAdditionalAgentOnLink(MobsimAgent planAgent) {
208  this.additionalAgentsOnLink.put(planAgent.getId(), planAgent);
209  }
210 
211  @Override
212  public MobsimAgent unregisterAdditionalAgentOnLink(Id<Person> mobsimAgentId) {
213  return this.additionalAgentsOnLink.remove(mobsimAgentId);
214  }
215 
216  @Override
217  public Collection<MobsimAgent> getAdditionalAgentsOnLink() {
218  return Collections.unmodifiableCollection( this.additionalAgentsOnLink.values());
219  }
220 
221  @Override
222  public void clearVehicles() {
223  double now = context.getSimTimer().getTimeOfDay();
224 
225  /*
226  * Some agents might be present in multiple lists/maps.
227  * Ensure that only one stuck event per agent is created.
228  */
229  Set<Id<Person>> stuckAgents = new HashSet<>();
230 
231  for (QVehicle veh : this.parkedVehicles.values()) {
232  if (veh.getDriver() != null) {
233  // skip transit driver which perform an activity while their vehicle is parked
234  if (veh.getDriver().getState() != State.LEG) continue;
235 
236  if (stuckAgents.contains(veh.getDriver().getId())) continue;
237  else stuckAgents.add(veh.getDriver().getId());
238 
239  context.getEventsManager().processEvent(
240  new VehicleAbortsEvent(now, veh.getId(), veh.getCurrentLink().getId()));
241 
242  context.getEventsManager().processEvent(
243  new PersonStuckEvent(now, veh.getDriver().getId(), veh.getCurrentLink().getId(), veh.getDriver().getMode()));
244  context.getAgentCounter().incLost();
245  context.getAgentCounter().decLiving();
246  }
247 
248  for (PassengerAgent passenger : veh.getPassengers()) {
249  if (stuckAgents.contains(passenger.getId())) continue;
250  else stuckAgents.add(passenger.getId());
251 
252  MobsimAgent mobsimAgent = (MobsimAgent) passenger;
253 
254  context.getEventsManager().processEvent(
255  new PersonStuckEvent(now, mobsimAgent.getId(), veh.getCurrentLink().getId(), mobsimAgent.getMode()));
256  context.getAgentCounter().incLost();
257  context.getAgentCounter().decLiving();
258  }
259  }
260  this.parkedVehicles.clear();
261  for (MobsimAgent driver : driversWaitingForPassengers.values()) {
262  if (stuckAgents.contains(driver.getId())) continue;
263  else stuckAgents.add(driver.getId());
264 
265  context.getEventsManager().processEvent(
266  new PersonStuckEvent(now, driver.getId(), driver.getCurrentLinkId(), driver.getMode()));
267  context.getAgentCounter().incLost();
268  context.getAgentCounter().decLiving();
269  }
270  driversWaitingForPassengers.clear();
271 
272 
273  for (Queue<MobsimDriverAgent> queue : driversWaitingForCars.values()) {
274  for (MobsimAgent driver : queue) {
275  if (stuckAgents.contains(driver.getId())) continue;
276  stuckAgents.add(driver.getId());
277 
278  context.getEventsManager().processEvent(
279  new PersonStuckEvent(now, driver.getId(), driver.getCurrentLinkId(), driver.getMode()));
280  context.getAgentCounter().incLost();
281  context.getAgentCounter().decLiving();
282  }
283  }
284  driversWaitingForCars.clear();
285  for (Set<MobsimAgent> passengers : passengersWaitingForCars.values()) {
286  for (MobsimAgent passenger : passengers) {
287  if (stuckAgents.contains(passenger.getId())) continue;
288  else stuckAgents.add(passenger.getId());
289 
290  context.getEventsManager().processEvent(
291  new PersonStuckEvent(now, passenger.getId(), passenger.getCurrentLinkId(), passenger.getMode()));
292  this.context.getAgentCounter().incLost();
293  this.context.getAgentCounter().decLiving();
294  }
295  }
296  this.passengersWaitingForCars.clear();
297 
298  for (QVehicle veh : this.waitingList) {
299  if (stuckAgents.contains(veh.getDriver().getId())) continue;
300  else stuckAgents.add(veh.getDriver().getId());
301 
302  this.context.getEventsManager().processEvent(
303  new VehicleAbortsEvent(now, veh.getId(), veh.getCurrentLink().getId()));
304 
305  this.context.getEventsManager().processEvent(
306  new PersonStuckEvent(now, veh.getDriver().getId(), veh.getCurrentLink().getId(), veh.getDriver().getMode()));
307  this.context.getAgentCounter().incLost();
308  this.context.getAgentCounter().decLiving();
309  }
310  this.waitingList.clear();
311  }
312 
313  void makeVehicleAvailableToNextDriver(QVehicle veh) {
314 
315  // this would (presumably) be the place where the "nature" of a vehicle could be changed (in the sense of PAVE), e.g. to
316  // a freight vehicle, or to an autonomous vehicle that can be sent around the block or home. However, this would
317  // necessitate some agent-like logic also for the vehicle: vehicle behavior given by driver as long as driver in
318  // vehicle; vehicle behavior given by something else afterwards. Parking search is structurally somewhat similar; how
319  // was that implemented? kai, oct'18
320 
321  /*
322  * Insert waiting passengers into vehicle.
323  */
324  Id<Vehicle> vehicleId = veh.getId();
325  Set<MobsimAgent> passengers = this.passengersWaitingForCars.get(vehicleId);
326  if (passengers != null) {
327  // Copy set of passengers since otherwise we would modify it concurrently.
328  List<MobsimAgent> passengersToHandle = new ArrayList<>(passengers);
329  for (MobsimAgent passenger : passengersToHandle) {
330  this.unregisterPassengerAgentWaitingForCar(passenger, vehicleId);
331  this.insertPassengerIntoVehicle(passenger, vehicleId);
332  }
333  }
334 
335  /*
336  * If the next driver is already waiting for the vehicle, check whether
337  * all passengers are also there. If not, the driver is not inserted
338  * into the vehicle and the vehicle does not depart.
339  */
340  final Queue<MobsimDriverAgent> driversWaitingForCar = driversWaitingForCars.get(veh.getId());
341  final boolean thereIsDriverWaiting = driversWaitingForCar != null && !driversWaitingForCar.isEmpty();
342  if ( thereIsDriverWaiting ) {
343  MobsimDriverAgent driverWaitingForPassengers =
344  driversWaitingForPassengers.get( driversWaitingForCar.element().getId());
345  if (driverWaitingForPassengers != null) return;
346  }
347 
348  /*
349  * If there is a driver waiting for its vehicle, and this car is not currently already leaving again with the
350  * same vehicle, put the new driver into the vehicle and let it depart.
351  */
352  if (thereIsDriverWaiting && veh.getDriver() == null) {
353  // set agent as driver and then let the vehicle depart
354  veh.setDriver(driversWaitingForCar.remove());
355  if (driversWaitingForCar.isEmpty()) {
356  final Queue<MobsimDriverAgent> r = driversWaitingForCars.remove(veh.getId());
357  assert r == driversWaitingForCar;
358  }
359  removeParkedVehicle( veh.getId() );
360  this.letVehicleDepart(veh);
361  }
362  }
363 
364  @Override
365  public final void letVehicleDepart(QVehicle vehicle) {
366  double now = context.getSimTimer().getTimeOfDay();
367 
368  MobsimDriverAgent driver = vehicle.getDriver();
369  if (driver == null) throw new RuntimeException("Vehicle cannot depart without a driver!");
370 
371  EventsManager eventsManager = context.getEventsManager();
372  eventsManager.processEvent(new PersonEntersVehicleEvent(now, driver.getId(), vehicle.getId()));
373  this.addDepartingVehicle(vehicle);
374  }
375 
376  /*
377  * If the vehicle is parked at the current link, insert the passenger,
378  * create an enter event and return true. Otherwise add the agent to
379  * the waiting list and return false.
380  */
381  @Override
382  public final boolean insertPassengerIntoVehicle(MobsimAgent passenger, Id<Vehicle> vehicleId) {
383  double now = context.getSimTimer().getTimeOfDay();
384 
385  QVehicle vehicle = this.getParkedVehicle(vehicleId);
386 
387  // if the vehicle is not parked at the link, mark the agent as passenger waiting for vehicle
388  if (vehicle == null) {
389  registerPassengerAgentWaitingForCar(passenger, vehicleId);
390  return false;
391  } else {
392  boolean added = vehicle.addPassenger((PassengerAgent) passenger);
393  if (!added) {
394  log.warn("Passenger " + passenger.getId().toString() +
395  " could not be inserted into vehicle " + vehicleId.toString() +
396  " since there is no free seat available!");
397  return false;
398  }
399 
400  ((PassengerAgent) passenger).setVehicle(vehicle);
401  EventsManager eventsManager = context.getEventsManager();
402  eventsManager.processEvent(new PersonEntersVehicleEvent(now, passenger.getId(), vehicle.getId()));
403  // TODO: allow setting passenger's currentLinkId to null
404 
405  return true;
406  }
407  }
408 
409  @Override
410  public QVehicle getVehicle(Id<Vehicle> vehicleId) {
411  // yyyy ?? does same as getParkedVehicle(...) ?? kai, mar'16
412 
413  QVehicle ret = this.parkedVehicles.get(vehicleId);
414  return ret;
415  }
416 
417  @Override
418  public final Collection<MobsimVehicle> getAllVehicles() {
419  Collection<MobsimVehicle> vehicles = this.getAllNonParkedVehicles();
420  vehicles.addAll(this.parkedVehicles.values());
421  return vehicles;
422  }
423 
424  @Override
425  public final Map<String, Object> getCustomAttributes() {
426  return customAttributes;
427  }
428 
429  /*package*/ void setNetElementActivationRegistry(NetElementActivationRegistry qSimEngineRunner) {
430  this.netElementActivationRegistry = qSimEngineRunner;
431  }
432 
433  @Override
434  public void registerDriverAgentWaitingForCar(final MobsimDriverAgent agent) {
435  final Id<Vehicle> vehicleId = agent.getPlannedVehicleId() ;
436  Queue<MobsimDriverAgent> queue = driversWaitingForCars.get( vehicleId );
437 
438  if ( queue == null ) {
439  queue = new LinkedList<>();
440  driversWaitingForCars.put( vehicleId , queue );
441  }
442 
443  queue.add( agent );
444  }
445 
446  @Override
447  public void registerDriverAgentWaitingForPassengers(MobsimDriverAgent agent) {
448  driversWaitingForPassengers.put(agent.getId(), agent);
449  }
450 
451  @Override
452  public MobsimAgent unregisterDriverAgentWaitingForPassengers(Id<Person> agentId) {
453  return driversWaitingForPassengers.remove(agentId);
454  }
455 
456  @Override
457  public void registerPassengerAgentWaitingForCar(MobsimAgent agent, Id<Vehicle> vehicleId) {
458  Set<MobsimAgent> passengers = passengersWaitingForCars.get(vehicleId);
459  if (passengers == null) {
460  passengers = new LinkedHashSet<>();
461  passengersWaitingForCars.put(vehicleId, passengers);
462  }
463  passengers.add(agent);
464  }
465 
466  @Override
467  public MobsimAgent unregisterPassengerAgentWaitingForCar(MobsimAgent agent, Id<Vehicle> vehicleId) {
468  Set<MobsimAgent> passengers = passengersWaitingForCars.get(vehicleId);
469  if (passengers != null && passengers.remove(agent)) return agent;
470  else return null;
471  }
472 
473  @Override
474  public Set<MobsimAgent> getAgentsWaitingForCar(Id<Vehicle> vehicleId) {
475  Set<MobsimAgent> set = passengersWaitingForCars.get(vehicleId);
476  if (set != null) return Collections.unmodifiableSet(set);
477  else return null;
478  }
479 
480  @Override
481  public Link getLink() {
482  return link;
483  }
484 
485  boolean isActive() {
486  return active;
487  }
488 
489  void setActive(boolean active) {
490  this.active = active;
491  }
492 
493  Queue<QVehicle> getWaitingList() {
494  return waitingList;
495  }
496 
497  TransitQLink getTransitQLink() {
498  return transitQLink;
499  }
500 
501  void setTransitQLink(TransitQLink transitQLink) {
502  this.transitQLink = transitQLink;
503  }
504 
509  public final class QLinkInternalInterface {
510 
511  public QNodeI getToNodeQ() {
512  return AbstractQLink.this.toQNode ;
513  }
514  public Node getToNode() {
515  return AbstractQLink.this.link.getToNode() ;
516  }
517 
518  public double getFreespeed() {
519  // yyyy does it make sense to provide the method without time? kai, feb'18
520  return AbstractQLink.this.link.getFreespeed() ;
521  }
522 
523  public Id<Link> getId() {
524  return AbstractQLink.this.link.getId() ;
525  }
526 
528  // yy now would not be needed as an argument. kai, feb'18
529  // yy linkId would not be needed as an argument. kai, feb'18
530  return AbstractQLink.this.transitQLink.handleTransitStop(now, veh, driver, linkId) ;
531  }
532 
533  public void addParkedVehicle(QVehicle veh) {
534  AbstractQLink.this.addParkedVehicle(veh);
535  }
536 
537  public boolean letVehicleArrive(QVehicle veh) {
538  return AbstractQLink.this.letVehicleArrive(veh);
539  }
540 
542  AbstractQLink.this.makeVehicleAvailableToNextDriver(veh);
543  }
544 
545  public void activateLink() {
546  AbstractQLink.this.activateLink();
547  }
548 
549  public double getMaximumVelocityFromLinkSpeedCalculator(QVehicle veh, double now) {
550  final LinkSpeedCalculator linkSpeedCalculator = AbstractQLink.this.linkSpeedCalculator;
551  Gbl.assertNotNull(linkSpeedCalculator);
552  return linkSpeedCalculator.getMaximumVelocity(veh, AbstractQLink.this.link, now) ;
553  }
554 
556  veh.setCurrentLink( AbstractQLink.this.link );
557  }
558 
560  return AbstractQLink.this.getAcceptingQLane() ;
561  }
562 
563  public List<QLaneI> getOfferingQLanes() {
564  return AbstractQLink.this.getOfferingQLanes() ;
565  }
566 
567  public Node getFromNode() {
568  return AbstractQLink.this.link.getFromNode() ;
569  }
570 
571  public double getFreespeed(double now) {
572  return AbstractQLink.this.link.getFreespeed(now) ;
573  }
574 
575  public int getNumberOfLanesAsInt(double now) {
576  return NetworkUtils.getNumberOfLanesAsInt(now,AbstractQLink.this.link) ;
577  }
578 
579  public Link getLink() {
580  return AbstractQLink.this.link;
581  }
582  }
583  private final QLinkInternalInterface qLinkInternalInterface = new QLinkInternalInterface() ;
584  public final QLinkInternalInterface getInternalInterface() {
585  return qLinkInternalInterface ;
586  }
587 
588 }
static int getNumberOfLanesAsInt(final double time, final Link link)
static final String ONLYONCE
Definition: Gbl.java:42
static void assertNotNull(Object obj)
Definition: Gbl.java:212
boolean addPassenger(final PassengerAgent passenger)