Skip to content

Commit

Permalink
fixes for 'passenger waiting' event
Browse files Browse the repository at this point in the history
  • Loading branch information
sebhoerl committed Nov 10, 2023
1 parent 3155f24 commit 0e0f0b4
Show file tree
Hide file tree
Showing 20 changed files with 113 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@
* @author Michal Maciejewski (michalm)
* @author Sebastian Hörl, IRT SystemX (sebhoerl)
*
* This class is based on DefaultPassengerEngine, but is able to handle prebooked requests in combination with
* PrebookingStopActivity. This means that persons may depart (PersonDepartureEvent) after the vehicle has arrived.
* This class is based on DefaultPassengerEngine, but is able to handle
* prebooked requests in combination with PrebookingStopActivity. This
* means that persons may depart (PersonDepartureEvent) after the
* vehicle has arrived.
*/
public final class PrebookingPassengerEngine implements PassengerEngine, PassengerRequestRejectedEventHandler {

Expand All @@ -60,13 +62,14 @@ public final class PrebookingPassengerEngine implements PassengerEngine, Passeng

private final PrebookingManager prebookingManager;

//accessed in doSimStep() and handleDeparture() (no need to sync)
// accessed in doSimStep() and handleDeparture() (no need to sync)
private final Map<Id<Request>, MobsimPassengerAgent> activePassengers = new HashMap<>();

// prebooking: holds vehicle stop activities for requests that have not arrived at departure point yet

// prebooking: holds vehicle stop activities for requests that have not arrived
// at departure point yet
private final Map<Id<Request>, PassengerPickupActivity> waitingForPassenger = new HashMap<>();

//accessed in doSimStep() and handleEvent() (potential data races)
// accessed in doSimStep() and handleEvent() (potential data races)
private final Queue<PassengerRequestRejectedEvent> rejectedRequestsEvents = new ConcurrentLinkedQueue<>();

// prebooking: pass PrebookingManager
Expand Down Expand Up @@ -96,23 +99,24 @@ public void onPrepareSim() {
}

@Override
public void doSimStep(double time) {
// prebooking: converted the initial while into an iterator to selectively clear the
public void doSimStep(double time) {
// prebooking: converted the initial while into an iterator to selectively clear
// the
// list. If prebooked requests are rejected (by the optimizer, through an
// event) after submission, but before departure, the PassengerEngine does not
// know this agent yet. Hence, we wait with setting the state to abort until the
// agent has arrived here (if ever). An alternative approach would be to save
// the ID to a list and then set the state to abort once the agent shows up (may
// be less memory consumption if we only need to save the IDs)

Iterator<PassengerRequestRejectedEvent> iterator = rejectedRequestsEvents.iterator();
while (iterator.hasNext()) {
PassengerRequestRejectedEvent event = iterator.next();
MobsimPassengerAgent passenger = activePassengers.remove(event.getRequestId());

if (passenger != null) {
//not much else can be done for immediate requests
//set the passenger agent to abort - the event will be thrown by the QSim
// not much else can be done for immediate requests
// set the passenger agent to abort - the event will be thrown by the QSim
passenger.setStateToAbort(mobsimTimer.getTimeOfDay());
internalInterface.arrangeNextAgentState(passenger);
iterator.remove();
Expand All @@ -130,43 +134,47 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id<Link> fromLinkI
return false;
}

MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent;
MobsimPassengerAgent passenger = (MobsimPassengerAgent) agent;
internalInterface.registerAdditionalAgentOnLink(passenger);

Id<Link> toLinkId = passenger.getDestinationLinkId();

// prebooking: try to find a prebooked requests that is associated to this leg
Leg leg = (Leg)((PlanAgent)passenger).getCurrentPlanElement();
Leg leg = (Leg) ((PlanAgent) passenger).getCurrentPlanElement();
PassengerRequest request = prebookingManager.consumePrebookedRequest(agent, leg);

if (request == null) { // prebooking: immediate request, default behavior
request = requestCreator.createRequest(internalPassengerHandling.createRequestId(),
passenger.getId(), leg.getRoute(), getLink(fromLinkId), getLink(toLinkId), now, now);
request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), passenger.getId(),
leg.getRoute(), getLink(fromLinkId), getLink(toLinkId), now, now);

// must come before validate* (to come before rejection event)
eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId()));

validateAndSubmitRequest(passenger, request, now);
} else { // prebooking: found a prebooked request for this customer departure
eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId()));

activePassengers.put(request.getId(), passenger);

PassengerPickupActivity pickupActivity = waitingForPassenger.remove(request.getId());
if (pickupActivity != null) {
// prebooking: the vehicle is already waiting for the customer, notify it
pickupActivity.notifyPassengerIsReadyForDeparture(passenger, now);
}
}

eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId()));


return true;
}

private void validateAndSubmitRequest(MobsimPassengerAgent passenger, PassengerRequest request, double now) {
activePassengers.put(request.getId(), passenger);
if (internalPassengerHandling.validateRequest(request, requestValidator, now)) {
//need to synchronise to address cases where requestSubmitted() may:
// need to synchronise to address cases where requestSubmitted() may:
// - be called from outside DepartureHandlers
// - interfere with VrpOptimizer.nextTask()
// - impact VrpAgentLogic.computeNextAction()
synchronized (optimizer) {
//optimizer can also reject request if cannot handle it
// optimizer can also reject request if cannot handle it
// (async operation, notification comes via the events channel)
optimizer.requestSubmitted(request);
}
Expand All @@ -183,20 +191,21 @@ private Link getLink(Id<Link> linkId) {
public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver,
Id<Request> requestId, double now) {
if (!activePassengers.containsKey(requestId)) {
// prebooking: vehicle queries customer, which has not departed yet, note it down
// prebooking: vehicle queries customer, which has not departed yet, note it
// down
waitingForPassenger.put(requestId, pickupActivity);
return false;
}

boolean pickedUp = internalPassengerHandling.tryPickUpPassenger(driver, activePassengers.get(requestId),
requestId, now);

// prebooking: commented the following line, this is a valid situation now!
// Verify.verify(pickedUp, "Not possible without prebooking");

return pickedUp;
}

/*
* prebooking: new method that does not actually pick up a passenger but only
* announces that a vehicle would like to. This is necessary to get the
Expand All @@ -211,7 +220,7 @@ public boolean queryPassengerReady(PassengerPickupActivity pickupActivity, Id<Re

return true;
}

@Override
public void dropOffPassenger(MobsimDriverAgent driver, Id<Request> requestId, double now) {
internalPassengerHandling.dropOffPassenger(driver, activePassengers.remove(requestId), requestId, now);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package org.matsim.contrib.drt.extension.prebooking;

import static org.junit.Assert.assertEquals;

import java.net.URL;

import org.junit.Rule;
import org.junit.Test;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.IdMap;
import org.matsim.contrib.drt.extension.DrtWithExtensionsConfigGroup;
import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtVehicleDataEntryFactory.EDrtVehicleDataEntryFactoryProvider;
import org.matsim.contrib.drt.extension.edrt.run.EDrtControlerCreator;
import org.matsim.contrib.drt.extension.prebooking.logic.FixedSharePrebookingLogic;
import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent;
import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEventHandler;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.drt.run.DrtControlerCreator;
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
import org.matsim.contrib.dvrp.optimizer.Request;
import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler;
import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
import org.matsim.contrib.dvrp.run.DvrpConfigGroup;
import org.matsim.contrib.ev.EvConfigGroup;
Expand Down Expand Up @@ -57,7 +67,15 @@ public void testDrtWithPrebooking() {
controller.addOverridingModule(new PrebookingModule());
FixedSharePrebookingLogic.install("drt", 0.5, 4.0 * 3600.0, controller);

Tracker tracker = new Tracker();
tracker.install(controller);

controller.run();

assertEquals(157, tracker.immediateScheduled);
assertEquals(205, tracker.prebookedScheduled);
assertEquals(26, tracker.immediateRejected);
assertEquals(0, tracker.prebookedRejected);
}

@Test
Expand Down Expand Up @@ -101,6 +119,50 @@ public void install() {
controler.addOverridingModule(new ElectricPrebookingModule());
FixedSharePrebookingLogic.install("drt", 0.5, 4.0 * 3600.0, controler);

Tracker tracker = new Tracker();
tracker.install(controler);

controler.run();

assertEquals(74, tracker.immediateScheduled);
assertEquals(178, tracker.prebookedScheduled);
assertEquals(109, tracker.immediateRejected);
assertEquals(27, tracker.prebookedRejected);
}

static private class Tracker implements PassengerRequestRejectedEventHandler, PassengerRequestScheduledEventHandler {
int immediateScheduled = 0;
int prebookedScheduled = 0;
int immediateRejected = 0;
int prebookedRejected = 0;

@Override
public void handleEvent(PassengerRequestScheduledEvent event) {
if (event.getRequestId().toString().contains("prebooked")) {
prebookedScheduled++;
} else {
immediateScheduled++;
}
}

@Override
public void handleEvent(PassengerRequestRejectedEvent event) {
if (event.getRequestId().toString().contains("prebooked")) {
prebookedRejected++;
} else {
immediateRejected++;
}
}

void install(Controler controller) {
Tracker thisTracker = this;

controller.addOverridingModule(new AbstractModule() {
@Override
public void install() {
addEventHandlerBinding().toInstance(thisTracker);
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id<Link> fromLinkI
Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute();
PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(),
passenger.getId(), route, getLink(fromLinkId), getLink(toLinkId), now, now);
validateAndSubmitRequest(passenger, request, now);

eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId()));

validateAndSubmitRequest(passenger, request, now);

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,14 @@ private Link getLink(Id<Link> linkId) {
//TODO what if it was already rejected while prebooking??

PassengerRequest prebookedRequest = prebookedRequests.get(0);

eventsManager.processEvent(new PassengerWaitingEvent(now, mode, prebookedRequest.getId(), prebookedRequest.getPassengerId()));

PassengerPickupActivity awaitingPickup = awaitingPickups.remove(prebookedRequest.getId());
if (awaitingPickup != null) {
awaitingPickup.notifyPassengerIsReadyForDeparture(passenger, now);
}

eventsManager.processEvent(new PassengerWaitingEvent(now, mode, prebookedRequest.getId(), prebookedRequest.getPassengerId()));

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id<Link> fromLinkI
Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute();
PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(),
passenger.getId(), route, getLink(fromLinkId), getLink(toLinkId), now, now);

eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId()));

if (internalPassengerHandling.validateRequest(request, requestValidator, now)) {
Route teleportedRoute = adaptLegRouteForTeleportation(passenger, request, now);
Expand All @@ -157,8 +159,6 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id<Link> fromLinkI
internalInterface.arrangeNextAgentState(passenger);
}

eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId()));

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void test_valid_served() {
fixture.assertPassengerEvents(
new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY),
new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE),
new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID),
new PassengerRequestScheduledEvent(departureTime, MODE, requestId, fixture.PERSON_ID, VEHICLE_ID, 0,
scheduledDropoffTime),
new PersonEntersVehicleEvent(pickupStartTime, fixture.PERSON_ID, Id.createVehicleId(VEHICLE_ID)),
Expand All @@ -121,6 +122,7 @@ public void test_invalid_rejected() {
var requestId = Id.create("taxi_0", Request.class);
fixture.assertPassengerEvents(new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY),
new PersonDepartureEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE),
new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID),
new PassengerRequestRejectedEvent(0, MODE, requestId, fixture.PERSON_ID, "invalid"),
new PersonStuckEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE));
}
Expand All @@ -136,6 +138,7 @@ public void test_valid_rejected() {
var requestId = Id.create("taxi_0", Request.class);
fixture.assertPassengerEvents(new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY),
new PersonDepartureEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE),
new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID),
new PassengerRequestRejectedEvent(0, MODE, requestId, fixture.PERSON_ID, "rejecting_all_requests"),
new PersonStuckEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public void test_valid_teleported() {
fixture.assertPassengerEvents(
new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY),
new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE),
new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID),
new PassengerRequestScheduledEvent(departureTime, MODE, requestId, fixture.PERSON_ID, null, departureTime,
arrivalTime), new PassengerPickedUpEvent(departureTime, MODE, requestId, fixture.PERSON_ID, null),
new PassengerDroppedOffEvent(arrivalTime, MODE, requestId, fixture.PERSON_ID, null),
Expand All @@ -93,6 +94,7 @@ public void test_invalid_rejected() {
fixture.assertPassengerEvents(
new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY),
new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE),
new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID),
new PassengerRequestRejectedEvent(departureTime, MODE, requestId, fixture.PERSON_ID, "invalid"),
new PersonStuckEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE));
}
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 0e0f0b4

Please sign in to comment.