MATSIM
BasicPlanAgentImpl.java
Go to the documentation of this file.
1 
2 /* *********************************************************************** *
3  * project: org.matsim.*
4  * BasicPlanAgentImpl.java
5  * *
6  * *********************************************************************** *
7  * *
8  * copyright : (C) 2019 by the members listed in the COPYING, *
9  * LICENSE and WARRANTY file. *
10  * email : info at matsim dot org *
11  * *
12  * *********************************************************************** *
13  * *
14  * This program is free software; you can redistribute it and/or modify *
15  * it under the terms of the GNU General Public License as published by *
16  * the Free Software Foundation; either version 2 of the License, or *
17  * (at your option) any later version. *
18  * See also COPYING, LICENSE and WARRANTY file *
19  * *
20  * *********************************************************************** */
21 
22  package org.matsim.core.mobsim.qsim.agents;
23 
24 import java.util.List;
25 
26 import jakarta.validation.constraints.NotNull;
27 
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.matsim.api.core.v01.Id;
31 import org.matsim.api.core.v01.Scenario;
42 import org.matsim.core.gbl.Gbl;
48 import org.matsim.core.mobsim.qsim.QSim;
56 import org.matsim.vehicles.Vehicle;
57 
58 import com.google.common.base.MoreObjects;
59 import com.google.common.base.Preconditions;
60 
62 
63  private static final Logger log = LogManager.getLogger(BasicPlanAgentImpl.class);
64  private static int finalActHasDpTimeWrnCnt = 0;
65  private static int noRouteWrnCnt = 0;
66 
67  private int currentPlanElementIndex = 0;
68  private Plan plan;
69  private boolean firstTimeToGetModifiablePlan = true;
70  private final Scenario scenario;
71  private final EventsManager events;
72  private final MobsimTimer simTimer;
74  private double activityEndTime;
76  private Id<Link> currentLinkId = null;
78 
84  private int currentLinkIndex = 0;
85 
86  public BasicPlanAgentImpl(Plan plan2, Scenario scenario, EventsManager events, MobsimTimer simTimer, TimeInterpretation timeInterpretation) {
87 
88  this.plan = PopulationUtils.unmodifiablePlan(plan2) ;
89  // yy MZ suggests, and I agree, to always give the agent a full plan, and consume that plan as the agent goes. kai, nov'14
90 
91  this.scenario = scenario ;
92  this.events = events ;
93  this.simTimer = simTimer ;
94  this.timeInterpretation = timeInterpretation;
95 
96  List<PlanElement> planElements = this.plan.getPlanElements();
97  Preconditions.checkArgument(!planElements.isEmpty(), "Plan must consist of at least one activity");
98  Activity firstAct = (Activity) planElements.get(0);
99  //this.setCurrentLinkId( firstAct.getLinkId() ) ;
100  final Id<Link> linkId = PopulationUtils.computeLinkIdFromActivity(firstAct, scenario.getActivityFacilities(), scenario.getConfig() );
101  this.setCurrentLinkId( linkId );
102  this.setState(State.ACTIVITY) ;
104  }
105 
106  @Override
107  public final void endLegAndComputeNextState(final double now) {
108  this.getEvents().processEvent(new PersonArrivalEvent( now, this.getId(), this.getDestinationLinkId(), getCurrentLeg().getMode()));
109  if( (!(this.getCurrentLinkId() == null && this.getDestinationLinkId() == null))
110  && !this.getCurrentLinkId().equals(this.getDestinationLinkId())) {
111  log.error("The agent " + this.getPerson().getId() + " has destination link " + this.getDestinationLinkId()
112  + ", but arrived on link " + this.getCurrentLinkId() + ". Setting agent state to ABORT.");
113  this.setState(MobsimAgent.State.ABORT) ;
114  } else {
115  // note that when we are here we don't know if next is another leg, or an activity Therefore, we go to a general method:
116  advancePlan(now) ;
117  }
118 
119  this.currentLinkIndex = 0 ;
120  }
121 
122  @Override
123  public final void setStateToAbort(final double now) {
124  this.setState(MobsimAgent.State.ABORT) ;
125  }
126 
127  @Override
128  public final void notifyArrivalOnLinkByNonNetworkMode(final Id<Link> linkId) {
129  this.setCurrentLinkId( linkId ) ;
130  }
131 
132  private void advancePlan(double now) {
133  this.currentPlanElementIndex++ ;
134 
135  // check if plan has run dry:
136  if ( this.getCurrentPlanElementIndex() >= this.getCurrentPlan().getPlanElements().size() ) {
137  log.error("plan of agent with id = " + this.getId() + " has run empty. Setting agent state to ABORT (but continuing the mobsim).") ;
138  this.setState(MobsimAgent.State.ABORT) ;
139  return;
140  }
141 
142  PlanElement pe = this.getCurrentPlanElement() ;
143  if (pe instanceof Activity) {
144  Activity act = (Activity) pe;
145  initializeActivity(act, now);
146  } else if (pe instanceof Leg) {
147  Leg leg = (Leg) pe;
148  initializeLeg(leg);
149  } else {
150  throw new RuntimeException("Unknown PlanElement of type: " + pe.getClass().getName());
151  }
152  }
153 
154  private void initializeLeg(Leg leg) {
155  this.setState(MobsimAgent.State.LEG) ;
156  this.currentLinkIndex = 0 ;
157  if (leg.getRoute() == null) {
158  log.error("The agent " + this.getPerson().getId() + " has no route in its leg. Setting agent state to ABORT.");
159  if ( noRouteWrnCnt < 1 ) {
160  log.info( "(Route is needed inside Leg even if you want teleportation since Route carries the start/endLinkId info.)") ;
161  noRouteWrnCnt++ ;
162  }
163  this.setState(MobsimAgent.State.ABORT) ;
164 // throw new RuntimeException("no route in leg") ;
165  }
166  }
167 
168  private void initializeActivity(Activity act, double now) {
169  this.setState(MobsimAgent.State.ACTIVITY) ;
170  this.getEvents().processEvent( new ActivityStartEvent(now, this.getId(), this.getCurrentLinkId(), act.getFacilityId(), act.getType(),
171  act.getCoord() ) );
173  getModifiablePlan(); // this is necessary to make the plan modifiable, so that setting the start time (next line) is actually feasible. kai/mz, oct'16
174  ((Activity) getCurrentPlanElement()).setStartTime(now);
175  }
176 
181  private final void calculateAndSetDepartureTime(Activity act) {
182  double now = this.getSimTimer().getTimeOfDay() ;
183  double departure = Math.max(now, timeInterpretation.decideOnActivityEndTime(act, now).orElse(Double.POSITIVE_INFINITY));
184 
185  if ( this.getCurrentPlanElementIndex() == this.getCurrentPlan().getPlanElements().size()-1 ) {
186  if ( finalActHasDpTimeWrnCnt < 1 && departure!=Double.POSITIVE_INFINITY ) {
187  log.error( "last activity of person driver agent id " + this.getId() + " has end time < infty; setting it to infty") ;
188  log.error( Gbl.ONLYONCE ) ;
189  finalActHasDpTimeWrnCnt++ ;
190  }
191  departure = Double.POSITIVE_INFINITY ;
192  }
193 
194  this.activityEndTime = departure ;
195  }
196 
197  @Override
198  public final void endActivityAndComputeNextState(final double now) {
199  if ( ! ( this.getCurrentPlanElement() instanceof Activity ) ){
200  log.warn( "trying to end an activity but current plan element is not an activity; agentId=" + this.getId() );
201  }
202  Activity act = (Activity) this.getCurrentPlanElement() ;
203  this.getEvents().processEvent( new ActivityEndEvent(now, this.getPerson().getId(), this.currentLinkId, act.getFacilityId(), act.getType(), act.getCoord()));
204 
205  // note that when we are here we don't know if next is another leg, or an activity Therefore, we go to a general method:
206  advancePlan(now);
207  }
208  @Override
209  public final void resetCaches() {
210  if ( this.getCurrentPlanElement() instanceof Activity ) {
211  Activity act = (Activity) this.getCurrentPlanElement() ;
213  }
214  }
215 
216  // ============================================================================
217  // (nearly) pure getters and setters below here
218 
224  @Override
225  public final Plan getModifiablePlan() {
226  // yy MZ suggests, and I agree, to always give the agent a full plan, and consume that plan as the agent goes. kai, nov'14
227  if (firstTimeToGetModifiablePlan) {
228  firstTimeToGetModifiablePlan = false ;
230  PopulationUtils.copyFromTo(this.getCurrentPlan(), newPlan);
231  this.plan = newPlan;
232  }
233  return this.getCurrentPlan();
234  }
235  @Override
237  NetworkRoute route = (NetworkRoute) this.getCurrentLeg().getRoute(); // if casts fail: illegal state.
238  if (route.getVehicleId() != null) {
239  return route.getVehicleId();
240  }
242  return Id.create(this.getId(), Vehicle.class); // we still assume the vehicleId is the agentId if no vehicleId is given.
243  }
244  @Override
245  public final String getMode() {
246  if( this.getCurrentPlanElementIndex() >= this.getCurrentPlan().getPlanElements().size() ) {
247  // just having run out of plan elements it not an argument for not being able to answer the "mode?" question,
248  // thus we answer with "null". This will likely result in an "abort". kai, nov'14
249  return null ;
250  }
251  PlanElement currentPlanElement = this.getCurrentPlanElement();
252  if (!(currentPlanElement instanceof Leg)) {
253  return null;
254  }
255  return ((Leg) currentPlanElement).getMode() ;
256  }
257  @Override
259  return timeInterpretation.decideOnLegTravelTime((Leg)this.getCurrentPlanElement());
260  }
261 
262  @Override
263  public final Double getExpectedTravelDistance() {
264  PlanElement currentPlanElement = this.getCurrentPlanElement();
265  if (!(currentPlanElement instanceof Leg)) {
266  return null;
267  }
268  return ((Leg) currentPlanElement).getRoute().getDistance();
269  }
270 
271  @Override
273  return this.plan.getPlanElements().get(this.currentPlanElementIndex);
274  }
275  @Override
277  if ( this.currentPlanElementIndex < this.plan.getPlanElements().size()-1 ) {
278  return this.plan.getPlanElements().get( this.currentPlanElementIndex+1 ) ;
279  } else {
280  return null ;
281  }
282  }
283  public final Activity getNextActivity() {
284  for ( int idx = this.currentPlanElementIndex+1 ; idx < this.plan.getPlanElements().size(); idx++ ) {
285  if ( this.plan.getPlanElements().get(idx) instanceof Activity ) {
286  return (Activity) this.plan.getPlanElements().get(idx) ;
287  }
288  }
289  return null ;
290  }
291  public final Activity getPreviousActivity() {
292  for ( int idx = this.currentPlanElementIndex-1 ; idx>=0 ; idx-- ) {
293  if ( this.plan.getPlanElements().get(idx) instanceof Activity ) {
294  return (Activity) this.plan.getPlanElements().get(idx) ;
295  }
296  }
297  return null ;
298  }
299  @Override
301  if ( this.currentPlanElementIndex >=1 ) {
302  return this.plan.getPlanElements().get( this.currentPlanElementIndex-1 ) ;
303  } else {
304  return null ;
305  }
306  }
307 
308  /* default */ final int getCurrentPlanElementIndex() {
309  // Should this be made public?
310  // Pro: Many programmers urgently seem to need this: They do everything possible to get to this index, including copy/paste of the whole class.
311  // Con: I don't think that it is needed in many of those cases with a bit of thinking, and without it it really makes the code more flexible including
312  // the possibility to, say, "consume" the plan while it is executed.
313  // kai, may'15
315  }
316  @Override
317  public final Plan getCurrentPlan() {
318  return plan;
319  }
320  @Override
321  public final Id<Person> getId() {
322  return this.plan.getPerson().getId() ;
323  }
324  @Override
325  public final Person getPerson() {
326  return this.plan.getPerson() ;
327  }
328 
329  public final Scenario getScenario() {
330  return scenario;
331  }
332 
333  public final EventsManager getEvents() {
334  return events;
335  }
336 
337  final MobsimTimer getSimTimer() {
338  return simTimer;
339  }
340 
341  @Override
342  public final MobsimVehicle getVehicle() {
343  return vehicle;
344  }
345 
346  @Override
347  public final void setVehicle(MobsimVehicle vehicle) {
348  this.vehicle = vehicle;
349  }
350 
351  @Override
352  public final Id<Link> getCurrentLinkId() {
353  return this.currentLinkId;
354  }
355 
356  /* package */ final void setCurrentLinkId(@NotNull Id<Link> linkId ) {
357  this.currentLinkId = Preconditions.checkNotNull(linkId);
358  }
359 
360  @Override
362  return this.getCurrentLeg().getRoute().getEndLinkId() ;
363  }
364  @Override
365  public final double getActivityEndTime() {
366  return this.activityEndTime;
367  }
368  @Override
369  public final MobsimAgent.State getState() {
370  return state;
371  }
372 
373  final void setState(MobsimAgent.State state) {
374  this.state = state;
375  }
376 
377  final Leg getCurrentLeg() {
378  return (Leg) this.getCurrentPlanElement() ;
379  }
380  @Override
381  public final int getCurrentLinkIndex() {
382  // Other then getPlanElementIndex, it seems that this one here has to be accessible, since routes may contain loops, in which
383  // case an indexOf search fails. kai, nov'17
384  return currentLinkIndex;
385  }
386 
387  final void incCurrentLinkIndex() {
388  currentLinkIndex++ ;
389  }
390 
391  @Override
393  PlanElement pe = this.getCurrentPlanElement() ;
394  Activity activity ;
395  if ( pe instanceof Activity ) {
396  activity = (Activity) pe;
397  } else if ( pe instanceof Leg ) {
398  activity = this.getPreviousActivity() ;
399  } else {
400  throw new RuntimeException("unexpected type of PlanElement") ;
401  }
402  return FacilitiesUtils.toFacility( activity, scenario.getActivityFacilities() ) ;
403  }
404 
405  @Override
407  PlanElement pe = this.getCurrentPlanElement() ;
408  if ( pe instanceof Leg ) {
409  Activity activity = this.getNextActivity() ;
410  return FacilitiesUtils.toFacility( activity, scenario.getActivityFacilities() ) ;
411 
412  // the above assumes alternating acts/legs. I start having the suspicion that we should revoke our decision to give that up.
413  // If not, one will have to use TripUtils to find the preceeding activity ... but things get more difficult. Preferably, the
414  // factility should then sit in the leg (since there it is used for routing). kai, dec'15
415  } else if (pe instanceof Activity) {
416  return null;
417  }
418  throw new RuntimeException("unexpected type of PlanElement");
419  }
420 
421  @Override
422  public String toString() {
423  return MoreObjects.toStringHelper(this)
424  .add("plan", plan)
425  .add("vehicle", vehicle)
426  .add("state", state)
427  .add("currentPlanElementIndex", currentPlanElementIndex)
428  .add("currentLinkId", currentLinkId)
429  .toString();
430  }
431 }
static final String ONLYONCE
Definition: Gbl.java:42
static void assertIf(boolean flag)
Definition: Gbl.java:207
OptionalTime decideOnActivityEndTime(Activity activity, double startTime)
static Id< Link > computeLinkIdFromActivity(Activity act, ActivityFacilities facs, Config config)
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
static void copyFromTo(final Plan in, final Plan out)
List< PlanElement > getPlanElements()
QSimConfigGroup qsim()
Definition: Config.java:447
Id< ActivityFacility > getFacilityId()
ActivityFacilities getActivityFacilities()
final void notifyArrivalOnLinkByNonNetworkMode(final Id< Link > linkId)
static Facility toFacility(final Activity toWrap, ActivityFacilities activityFacilities)
BasicPlanAgentImpl(Plan plan2, Scenario scenario, EventsManager events, MobsimTimer simTimer, TimeInterpretation timeInterpretation)