diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingActionCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingActionCreator.java index e0638f562c1..f30976ad8d0 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingActionCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingActionCreator.java @@ -7,6 +7,8 @@ import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.passenger.PassengerEngine; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; import org.matsim.contrib.dynagent.DynAction; @@ -24,13 +26,13 @@ */ public class PrebookingActionCreator implements VrpAgentLogic.DynActionCreator { private final VrpAgentLogic.DynActionCreator delegate; - private final PrebookingPassengerEngine passengerEngine; + private final PassengerHandler passengerHandler; private final PassengerStopDurationProvider stopDurationProvider; - public PrebookingActionCreator(PrebookingPassengerEngine passengerEngine, VrpAgentLogic.DynActionCreator delegate, + public PrebookingActionCreator(PassengerHandler passengerHandler, VrpAgentLogic.DynActionCreator delegate, PassengerStopDurationProvider stopDurationProvider) { this.delegate = delegate; - this.passengerEngine = passengerEngine; + this.passengerHandler = passengerHandler; this.stopDurationProvider = stopDurationProvider; } @@ -40,7 +42,7 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now if (getBaseTypeOrElseThrow(task).equals(DrtTaskBaseType.STOP)) { DrtStopTask stopTask = (DrtStopTask) task; - return new PrebookingStopActivity(passengerEngine, dynAgent, stopTask, stopTask.getDropoffRequests(), + return new PrebookingStopActivity(passengerHandler, dynAgent, stopTask, stopTask.getDropoffRequests(), stopTask.getPickupRequests(), DrtActionCreator.DRT_STOP_NAME, () -> stopTask.getEndTime(), stopDurationProvider, vehicle); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingManager.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingManager.java index 95dbdd04911..4d1a2399cc5 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingManager.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingManager.java @@ -6,6 +6,8 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Nullable; + import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.IdMap; import org.matsim.api.core.v01.network.Link; @@ -15,6 +17,7 @@ import org.matsim.contrib.drt.extension.prebooking.events.PassengerRequestBookedEvent; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; import org.matsim.contrib.dvrp.passenger.PassengerRequest; import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; @@ -46,7 +49,7 @@ * * @author Sebastian Hörl (sebhoerl), IRT SystemX */ -public class PrebookingManager implements MobsimEngine { +public class PrebookingManager implements MobsimEngine, AdvanceRequestProvider { private final String mode; private final Network network; @@ -156,7 +159,9 @@ private record QueueItem(MobsimAgent person, Leg leg, double earliestDepartureTi // Interface with PassengerEngine - PassengerRequest retrievePrebookedRequest(MobsimAgent agent, Leg leg) { + @Override + @Nullable + public PassengerRequest retrieveRequest(MobsimAgent agent, Leg leg) { Verify.verify(leg.getMode().equals(mode), "Invalid mode for this prebooking manager"); Id requestId = getRequestId(leg); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeModule.java index 6fcdc161876..76f2110b73f 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeModule.java @@ -46,7 +46,7 @@ public void install() { })); // install QSim bindings / overrides - installOverridingQSimModule(new PrebookingModeQSimModule(getMode(), drtConfig, isElectric)); + installOverridingQSimModule(new PrebookingModeQSimModule(getMode(), isElectric)); // analysis install(new PrebookingModeAnalysisModule(getMode())); diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeQSimModule.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeQSimModule.java index 859ab385c75..e995791bc77 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeQSimModule.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingModeQSimModule.java @@ -3,10 +3,10 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.extension.edrt.EDrtActionCreator; import org.matsim.contrib.drt.extension.prebooking.electric.ElectricPrebookingActionCreator; -import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; import org.matsim.contrib.dvrp.passenger.PassengerEngine; import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; @@ -20,13 +20,11 @@ class PrebookingModeQSimModule extends AbstractDvrpModeQSimModule { private final boolean isElectric; - private final DrtConfigGroup drtConfig; - PrebookingModeQSimModule(String mode, DrtConfigGroup drtConfig, boolean isElectric) { + PrebookingModeQSimModule(String mode, boolean isElectric) { super(mode); this.isElectric = isElectric; - this.drtConfig = drtConfig; } @Override @@ -34,13 +32,12 @@ protected void configureQSim() { // bind the custom PrebookingActionCreator if (!isElectric) { bindModal(PrebookingActionCreator.class).toProvider(modalProvider(getter -> { - PrebookingPassengerEngine passengerEngine = (PrebookingPassengerEngine) getter - .getModal(PassengerHandler.class); + PassengerHandler passengerHandler = (PassengerEngine) getter.getModal(PassengerHandler.class); DrtActionCreator delegate = getter.getModal(DrtActionCreator.class); PassengerStopDurationProvider stopDurationProvider = getter .getModal(PassengerStopDurationProvider.class); - return new PrebookingActionCreator(passengerEngine, delegate, stopDurationProvider); + return new PrebookingActionCreator(passengerHandler, delegate, stopDurationProvider); })).in(Singleton.class); bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(PrebookingActionCreator.class)); } else { @@ -48,35 +45,18 @@ protected void configureQSim() { // delegate to edrt instead of drt EDrtActionCreator delegate = getter.getModal(EDrtActionCreator.class); - PrebookingPassengerEngine passengerEngine = (PrebookingPassengerEngine) getter - .getModal(PassengerHandler.class); + PassengerHandler passengerHandler = getter.getModal(PassengerHandler.class); PassengerStopDurationProvider stopDurationProvider = getter .getModal(PassengerStopDurationProvider.class); MobsimTimer timer = getter.get(MobsimTimer.class); PrebookingManager prebookingManager = getter.getModal(PrebookingManager.class); - return new ElectricPrebookingActionCreator(passengerEngine, delegate, stopDurationProvider, timer, + return new ElectricPrebookingActionCreator(passengerHandler, delegate, stopDurationProvider, timer, prebookingManager); })).in(Singleton.class); bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(ElectricPrebookingActionCreator.class)); } - // override the PassengerEngine - bindModal(PrebookingPassengerEngine.class).toProvider(modalProvider(getter -> { - EventsManager eventsManager = getter.get(EventsManager.class); - MobsimTimer mobsimTimer = getter.get(MobsimTimer.class); - - PassengerRequestCreator requestCreator = getter.getModal(PassengerRequestCreator.class); - VrpOptimizer optimizer = getter.getModal(VrpOptimizer.class); - Network network = getter.getModal(Network.class); - PassengerRequestValidator requestValidator = getter.getModal(PassengerRequestValidator.class); - PrebookingManager prebookingManager = getter.getModal(PrebookingManager.class); - - return new PrebookingPassengerEngine(getMode(), eventsManager, mobsimTimer, requestCreator, optimizer, - network, requestValidator, prebookingManager); - })).in(Singleton.class); - bindModal(PassengerEngine.class).to(modalKey(PrebookingPassengerEngine.class)); - // bind the prebooking manager bindModal(PrebookingManager.class).toProvider(modalProvider(getter -> { Network network = getter.getModal(Network.class); @@ -89,5 +69,6 @@ protected void configureQSim() { eventsManager); })).in(Singleton.class); addModalQSimComponentBinding().to(modalKey(PrebookingManager.class)); + bindModal(AdvanceRequestProvider.class).to(modalKey(PrebookingManager.class)); } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingPassengerEngine.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingPassengerEngine.java deleted file mode 100644 index 495c7cb64a4..00000000000 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingPassengerEngine.java +++ /dev/null @@ -1,253 +0,0 @@ -package org.matsim.contrib.drt.extension.prebooking.dvrp; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.contrib.dvrp.optimizer.Request; -import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; -import org.matsim.contrib.dvrp.passenger.InternalPassengerHandling; -import org.matsim.contrib.dvrp.passenger.PassengerEngine; -import org.matsim.contrib.dvrp.passenger.PassengerPickupActivity; -import org.matsim.contrib.dvrp.passenger.PassengerRequest; -import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; -import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; -import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; -import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; -import org.matsim.contrib.dvrp.passenger.PassengerWaitingEvent; -import org.matsim.contrib.dvrp.run.DvrpModes; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.mobsim.framework.MobsimAgent; -import org.matsim.core.mobsim.framework.MobsimDriverAgent; -import org.matsim.core.mobsim.framework.MobsimPassengerAgent; -import org.matsim.core.mobsim.framework.MobsimTimer; -import org.matsim.core.mobsim.framework.PlanAgent; -import org.matsim.core.mobsim.qsim.InternalInterface; -import org.matsim.core.modal.ModalProviders; - -import com.google.common.base.Preconditions; - -import jakarta.inject.Inject; -import jakarta.inject.Provider; - -/** - * @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. - */ -public final class PrebookingPassengerEngine implements PassengerEngine, PassengerRequestRejectedEventHandler { - - private final String mode; - private final MobsimTimer mobsimTimer; - private final EventsManager eventsManager; - - private final PassengerRequestCreator requestCreator; - private final VrpOptimizer optimizer; - private final Network network; - private final PassengerRequestValidator requestValidator; - - private final InternalPassengerHandling internalPassengerHandling; - - private InternalInterface internalInterface; - - private final PrebookingManager prebookingManager; - - // accessed in doSimStep() and handleDeparture() (no need to sync) - private final Map, MobsimPassengerAgent> activePassengers = new HashMap<>(); - - // prebooking: holds vehicle stop activities for requests that have not arrived - // at departure point yet - private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); - - // accessed in doSimStep() and handleEvent() (potential data races) - private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); - - // prebooking: pass PrebookingManager - PrebookingPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, - PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, - PassengerRequestValidator requestValidator, PrebookingManager prebookingManager) { - this.mode = mode; - this.mobsimTimer = mobsimTimer; - this.requestCreator = requestCreator; - this.optimizer = optimizer; - this.network = network; - this.requestValidator = requestValidator; - this.eventsManager = eventsManager; - this.prebookingManager = prebookingManager; - - internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); - } - - @Override - public void setInternalInterface(InternalInterface internalInterface) { - this.internalInterface = internalInterface; - internalPassengerHandling.setInternalInterface(internalInterface); - } - - @Override - public void onPrepareSim() { - } - - @Override - 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 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 - passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); - internalInterface.arrangeNextAgentState(passenger); - iterator.remove(); - } - } - } - - @Override - public void afterSim() { - } - - @Override - public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { - if (!agent.getMode().equals(mode)) { - return false; - } - - MobsimPassengerAgent passenger = (MobsimPassengerAgent) agent; - internalInterface.registerAdditionalAgentOnLink(passenger); - - Id toLinkId = passenger.getDestinationLinkId(); - - // prebooking: try to find a prebooked requests that is associated to this leg - Leg leg = (Leg) ((PlanAgent) passenger).getCurrentPlanElement(); - PassengerRequest request = prebookingManager.retrievePrebookedRequest(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); - - // 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); - } - } - - 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: - // - be called from outside DepartureHandlers - // - interfere with VrpOptimizer.nextTask() - // - impact VrpAgentLogic.computeNextAction() - synchronized (optimizer) { - // optimizer can also reject request if cannot handle it - // (async operation, notification comes via the events channel) - optimizer.requestSubmitted(request); - } - } - } - - private Link getLink(Id linkId) { - return Preconditions.checkNotNull(network.getLinks().get(linkId), - "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", - linkId, mode); - } - - @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, - Id requestId, double now) { - if (!activePassengers.containsKey(requestId)) { - // 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 - * stopDuration timing of the vehicles right. - */ - public boolean queryPassengerReady(PassengerPickupActivity pickupActivity, Id requestId) { - // Copy & paste, see above, may make sense to restructure things - if (!activePassengers.containsKey(requestId)) { - waitingForPassenger.put(requestId, pickupActivity); - return false; - } - - return true; - } - - @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { - internalPassengerHandling.dropOffPassenger(driver, activePassengers.remove(requestId), requestId, now); - } - - @Override - public void handleEvent(PassengerRequestRejectedEvent event) { - if (event.getMode().equals(mode)) { - rejectedRequestsEvents.add(event); - } - } - - public static Provider createProvider(String mode) { - return new ModalProviders.AbstractProvider<>(mode, DvrpModes::mode) { - @Inject - private EventsManager eventsManager; - - @Inject - private MobsimTimer mobsimTimer; - - @Override - public PrebookingPassengerEngine get() { - return new PrebookingPassengerEngine(getMode(), eventsManager, mobsimTimer, - getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), - getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), - getModalInstance(PrebookingManager.class)); - } - }; - } -} diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingStopActivity.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingStopActivity.java index f2e992f5968..98f75b7a8c5 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingStopActivity.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/dvrp/PrebookingStopActivity.java @@ -12,6 +12,7 @@ import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.passenger.PassengerPickupActivity; import org.matsim.contrib.dvrp.schedule.StayTask; import org.matsim.contrib.dynagent.DynAgent; @@ -37,18 +38,18 @@ public class PrebookingStopActivity extends FirstLastSimStepDynActivity implemen private final IdMap leaveTimes = new IdMap<>(Request.class); private final Set> enteredRequests = new HashSet<>(); - private final PrebookingPassengerEngine passengerEngine; + private final PassengerHandler passengerHandler; private final PassengerStopDurationProvider stopDurationProvider; private final Supplier endTime; - public PrebookingStopActivity(PrebookingPassengerEngine passengerEngine, DynAgent driver, StayTask task, + public PrebookingStopActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask task, Map, ? extends AcceptedDrtRequest> dropoffRequests, Map, ? extends AcceptedDrtRequest> pickupRequests, String activityType, Supplier endTime, PassengerStopDurationProvider stopDurationProvider, DvrpVehicle vehicle) { super(activityType); - this.passengerEngine = passengerEngine; + this.passengerHandler = passengerHandler; this.driver = driver; this.dropoffRequests = dropoffRequests; this.pickupRequests = pickupRequests; @@ -84,7 +85,7 @@ private void processDropoffRequests(double now) { var entry = iterator.next(); if (entry.getValue() <= now) { // Request should leave now - passengerEngine.dropOffPassenger(driver, entry.getKey(), now); + passengerHandler.dropOffPassenger(driver, entry.getKey(), now); iterator.remove(); } } @@ -96,7 +97,7 @@ private boolean updatePickupRequests(double now) { // this is a new request that has been added after the activity has been created // or that had not arrived yet - if (passengerEngine.queryPassengerReady(this, request.getId())) { + if (passengerHandler.notifyWaitForPassenger(this, this.driver, request.getId())) { // agent starts to enter queuePickup(request, now); } @@ -110,7 +111,7 @@ private boolean updatePickupRequests(double now) { if (entry.getValue() <= now) { // let agent enter now - Verify.verify(passengerEngine.tryPickUpPassenger(this, driver, entry.getKey(), now)); + Verify.verify(passengerHandler.tryPickUpPassenger(this, driver, entry.getKey(), now)); enteredRequests.add(entry.getKey()); iterator.remove(); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/electric/ElectricPrebookingActionCreator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/electric/ElectricPrebookingActionCreator.java index aa71047ae36..a5a751b6853 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/electric/ElectricPrebookingActionCreator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/prebooking/electric/ElectricPrebookingActionCreator.java @@ -1,13 +1,13 @@ package org.matsim.contrib.drt.extension.prebooking.electric; import org.matsim.contrib.drt.extension.prebooking.dvrp.PrebookingManager; -import org.matsim.contrib.drt.extension.prebooking.dvrp.PrebookingPassengerEngine; import org.matsim.contrib.drt.extension.prebooking.dvrp.PrebookingStopActivity; import org.matsim.contrib.drt.schedule.DrtStopTask; import org.matsim.contrib.drt.schedule.DrtTaskBaseType; import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.schedule.Task; import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; import org.matsim.contrib.dynagent.DynAction; @@ -24,15 +24,15 @@ */ public class ElectricPrebookingActionCreator implements VrpAgentLogic.DynActionCreator { private final VrpAgentLogic.DynActionCreator delegate; - private final PrebookingPassengerEngine passengerEngine; + private final PassengerHandler passengerHandler; private final PassengerStopDurationProvider stopDurationProvider; private final MobsimTimer timer; - public ElectricPrebookingActionCreator(PrebookingPassengerEngine passengerEngine, + public ElectricPrebookingActionCreator(PassengerHandler passengerHandler, VrpAgentLogic.DynActionCreator delegate, PassengerStopDurationProvider stopDurationProvider, MobsimTimer timer, PrebookingManager prebookingManager) { this.delegate = delegate; - this.passengerEngine = passengerEngine; + this.passengerHandler = passengerHandler; this.stopDurationProvider = stopDurationProvider; this.timer = timer; } @@ -47,7 +47,7 @@ public DynAction createAction(DynAgent dynAgent, DvrpVehicle vehicle, double now // added for electric stopTask.initTaskTracker(new OfflineETaskTracker((EvDvrpVehicle) vehicle, timer)); - return new PrebookingStopActivity(passengerEngine, dynAgent, stopTask, stopTask.getDropoffRequests(), + return new PrebookingStopActivity(passengerHandler, dynAgent, stopTask, stopTask.getDropoffRequests(), stopTask.getPickupRequests(), DrtActionCreator.DRT_STOP_NAME, () -> stopTask.getEndTime(), stopDurationProvider, vehicle); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java index ecaaf5b4065..288c4baefa7 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeQSimModule.java @@ -25,6 +25,7 @@ import org.matsim.contrib.drt.optimizer.DrtModeOptimizerQSimModule; import org.matsim.contrib.drt.passenger.DrtRequestCreator; import org.matsim.contrib.drt.speedup.DrtSpeedUp; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; import org.matsim.contrib.dvrp.passenger.DefaultPassengerRequestValidator; import org.matsim.contrib.dvrp.passenger.PassengerEngineQSimModule; import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; @@ -79,5 +80,7 @@ public DrtRequestCreator get() { return new DrtRequestCreator(getMode(), events); } }).asEagerSingleton(); + + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java index 1bbe061f8d7..ab5916c967e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiModule.java @@ -25,6 +25,7 @@ import org.matsim.api.core.v01.TransportMode; import org.matsim.contrib.dvrp.fleet.FleetModule; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; import org.matsim.contrib.dvrp.passenger.DefaultPassengerRequestValidator; import org.matsim.contrib.dvrp.passenger.PassengerEngineQSimModule; import org.matsim.contrib.dvrp.passenger.PassengerEngineQSimModule.PassengerEngineType; @@ -87,6 +88,9 @@ protected void configureQSim() { // converts scheduled tasks into simulated actions (legs and activities) bindModal(VrpAgentLogic.DynActionCreator.class).to(OneTaxiActionCreator.class).in(Singleton.class); + + // no advance request + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); } }); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AdvanceRequestProvider.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AdvanceRequestProvider.java new file mode 100644 index 00000000000..56426adb184 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AdvanceRequestProvider.java @@ -0,0 +1,13 @@ +package org.matsim.contrib.dvrp.passenger; + +import javax.annotation.Nullable; + +import org.matsim.api.core.v01.population.Leg; +import org.matsim.core.mobsim.framework.MobsimAgent; + +public interface AdvanceRequestProvider { + static public AdvanceRequestProvider NONE = (MobsimAgent agent, Leg leg) -> null; + + @Nullable + PassengerRequest retrieveRequest(MobsimAgent agent, Leg leg); +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java index 17959aa453a..428d2cdd91f 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java @@ -20,13 +20,11 @@ package org.matsim.contrib.dvrp.passenger; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import jakarta.inject.Inject; -import jakarta.inject.Provider; - import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -45,7 +43,9 @@ import org.matsim.core.modal.ModalProviders; import com.google.common.base.Preconditions; -import com.google.common.base.Verify; + +import jakarta.inject.Inject; +import jakarta.inject.Provider; /** * @author Michal Maciejewski (michalm) @@ -62,18 +62,22 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR private final PassengerRequestValidator requestValidator; private final InternalPassengerHandling internalPassengerHandling; + private final AdvanceRequestProvider advanceRequestProvider; private InternalInterface internalInterface; //accessed in doSimStep() and handleDeparture() (no need to sync) private final Map, MobsimPassengerAgent> activePassengers = new HashMap<>(); + + // holds vehicle stop activities for requests that have not arrived at departure point yet + private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); //accessed in doSimStep() and handleEvent() (potential data races) private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); DefaultPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, - PassengerRequestValidator requestValidator) { + PassengerRequestValidator requestValidator, AdvanceRequestProvider advanceRequestProvider) { this.mode = mode; this.mobsimTimer = mobsimTimer; this.requestCreator = requestCreator; @@ -81,6 +85,7 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR this.network = network; this.requestValidator = requestValidator; this.eventsManager = eventsManager; + this.advanceRequestProvider = advanceRequestProvider; internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); } @@ -97,12 +102,24 @@ public void onPrepareSim() { @Override public void doSimStep(double time) { - while (!rejectedRequestsEvents.isEmpty()) { - MobsimPassengerAgent passenger = activePassengers.remove(rejectedRequestsEvents.poll().getRequestId()); - //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); + // 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). + + Iterator 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 + passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); + internalInterface.arrangeNextAgentState(passenger); + iterator.remove(); + } } } @@ -120,20 +137,36 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI internalInterface.registerAdditionalAgentOnLink(passenger); Id toLinkId = passenger.getDestinationLinkId(); - - 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())); - - validateAndSubmitRequest(passenger, request, now); + // try to find a prebooked requests that is associated to this leg + Leg leg = (Leg) ((PlanAgent) passenger).getCurrentPlanElement(); + PassengerRequest request = advanceRequestProvider.retrieveRequest(agent, leg); + + if (request == null) { // immediate request + Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); + request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), passenger.getId(), + route, getLink(fromLinkId), getLink(toLinkId), now, now); + + // must come before validateAndSubmitRequest (to come before rejection event) + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId())); + activePassengers.put(request.getId(), passenger); + + validateAndSubmitRequest(passenger, request, now); + } else { // advance request + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId())); + activePassengers.put(request.getId(), passenger); + + PassengerPickupActivity pickupActivity = waitingForPassenger.remove(request.getId()); + if (pickupActivity != null) { + // the vehicle is already waiting for the request, notify it + pickupActivity.notifyPassengerIsReadyForDeparture(passenger, now); + } + } 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: // - be called from outside DepartureHandlers @@ -153,13 +186,39 @@ private Link getLink(Id linkId) { linkId, mode); } + /** + * There are two ways of interacting with the PassengerEngine: + * + * - (1) The stop activity tries to pick up a passenger and receives whether the + * pickup succeeded or not (see tryPickUpPassenger). In the classic + * implementation, the vehicle only calls tryPickUpPassenger at the time when it + * actually wants to pick up the person (at the end of the activity). It may + * happen that the person is not present yet. In that case, the pickup request + * is saved and notifyPassengerReady is called on the stop activity upen + * departure of the agent. + * + * - (2) If pickup and dropoff times are handled more flexibly by the stop + * activity, it might want to detect whether an agent is ready to be picked up, + * then start an "interaction time" and only after perform the actual pickup. + * For that purpose, we have queryPickUpPassenger, which indicates whether the + * agent is already there, and, if not, makes sure that the stop activity is + * notified once the agent arrives for departure. + */ + @Override + public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + if (!activePassengers.containsKey(requestId)) { + waitingForPassenger.put(requestId, pickupActivity); + return false; + } + + return true; + } + @Override public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { - boolean pickedUp = internalPassengerHandling.tryPickUpPassenger(driver, activePassengers.get(requestId), + return internalPassengerHandling.tryPickUpPassenger(driver, activePassengers.get(requestId), requestId, now); - Verify.verify(pickedUp, "Not possible without prebooking"); - return pickedUp; } @Override @@ -186,7 +245,8 @@ public static Provider createProvider(String mode) { public DefaultPassengerEngine get() { return new DefaultPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), - getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class)); + getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), + getModalInstance(AdvanceRequestProvider.class)); } }; } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java index d4eb8654cff..354bb788a6f 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java @@ -300,4 +300,25 @@ public static Provider createProvider(String mode) { } }; } + + /* + * This method has been retrofitted with the new prebooking implementation in + * Nov 2023. Not sure if this is the right way to do it, this class doesn't seem + * to be tested anywhere. /sebhoerl + */ + @Override + public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + Id linkId = driver.getCurrentLinkId(); + RequestEntry requestEntry = activeRequests.get(requestId); + MobsimPassengerAgent passenger = requestEntry.passenger; + + if (passenger.getCurrentLinkId() != linkId + || passenger.getState() != MobsimAgent.State.LEG + || !passenger.getMode().equals(mode)) { + awaitingPickups.put(requestId, pickupActivity); + return false;// wait for the passenger + } + + return true; // passenger present? + } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java index 1a9e313731c..408f187db77 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java @@ -30,6 +30,8 @@ * This looks quite general. But as of now is a dvrp thing. kai, apr'23 */ public interface PassengerHandler { + boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId); + boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now); diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java index 8153bbe5929..14374da7168 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java @@ -185,6 +185,12 @@ private Link getLink(Id linkId) { linkId, mode); } + @Override + public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + Id requestId) { + throw new UnsupportedOperationException("No notifying when teleporting"); + } + @Override public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java index 0ffbfbbfddb..6636119ccd3 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java @@ -178,6 +178,7 @@ protected void configureQSim() { bindModal(PassengerRequestCreator.class).to(OneTaxiRequest.OneTaxiRequestCreator.class) .asEagerSingleton(); bindModal(PassengerRequestValidator.class).toInstance(requestValidator); + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); //supply addQSimComponentBinding(DynActivityEngine.COMPONENT_NAME).to(DynActivityEngine.class);