2626import org .matsim .core .gbl .MatsimRandom ;
2727import org .matsim .core .mobsim .dsim .DistributedDepartureHandler ;
2828import org .matsim .core .mobsim .framework .MobsimAgent ;
29+ import org .matsim .core .mobsim .framework .MobsimDriverAgent ;
2930import org .matsim .core .mobsim .framework .PlanAgent ;
3031import org .matsim .core .mobsim .qsim .InternalInterface ;
3132import org .matsim .core .mobsim .qsim .TeleportationEngine ;
3233import org .matsim .core .mobsim .qsim .interfaces .DepartureHandler ;
3334import org .matsim .core .mobsim .qsim .interfaces .MobsimEngine ;
3435import org .matsim .core .mobsim .qsim .pt .TransitDriverAgent ;
3536import org .matsim .core .mobsim .qsim .qnetsimengine .NetworkModeDepartureHandler ;
37+ import org .matsim .core .mobsim .qsim .qnetsimengine .QLinkI ;
38+ import org .matsim .core .mobsim .qsim .qnetsimengine .QNetsimEngineI ;
39+ import org .matsim .core .mobsim .qsim .qnetsimengine .QVehicle ;
3640import org .matsim .core .population .routes .NetworkRoute ;
3741import org .matsim .core .router .util .TravelTime ;
3842import org .matsim .vehicles .Vehicle ;
3943import org .matsim .vehicles .VehicleType ;
4044
45+ import java .util .PriorityQueue ;
4146import java .util .Random ;
4247
4348/**
@@ -77,12 +82,15 @@ public class FISS implements NetworkModeDepartureHandler, DistributedDepartureHa
7782 private final MatsimServices matsimServices ;
7883 private final QSimConfigGroup qsimConfig ;
7984 private final Scenario scenario ;
85+ private final QNetsimEngineI qNetsimEngine ;
8086
87+ private final PriorityQueue <VehicleArrivalEntry > vehicleArrivals = new PriorityQueue <>();
8188
8289 @ Inject
8390 FISS (MatsimServices matsimServices , Scenario scenario , FISSConfigGroup fissConfigGroup , Network network , TeleportationEngine teleport ,
8491 @ Named (TransportMode .car ) TravelTime travelTime ,
85- @ Named ("base-network-mode-departure-handler" ) NetworkModeDepartureHandler networkModeDepartureHandler ) {
92+ @ Named ("base-network-mode-departure-handler" ) NetworkModeDepartureHandler networkModeDepartureHandler ,
93+ QNetsimEngineI qNetsimEngine ) {
8694 this .delegate = networkModeDepartureHandler ;
8795 this .fissConfigGroup = fissConfigGroup ;
8896 this .network = network ;
@@ -92,6 +100,7 @@ public class FISS implements NetworkModeDepartureHandler, DistributedDepartureHa
92100 this .scenario = scenario ;
93101 this .qsimConfig = scenario .getConfig ().qsim ();
94102 this .random = MatsimRandom .getLocalInstance ();
103+ this .qNetsimEngine = qNetsimEngine ;
95104 }
96105
97106 @ Override
@@ -109,53 +118,56 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id<Link> linkId) {
109118 }
110119
111120 // This updates the travel time. Teleportation departure is handled further down.
121+ double newTravelTime = 0. ;
122+ Id <Link > destinationLinkId = agent .getDestinationLinkId ();
112123 if (agent instanceof PlanAgent planAgent ) {
113124 Leg currentLeg = (Leg ) planAgent .getCurrentPlanElement ();
114125 NetworkRoute networkRoute = (NetworkRoute ) currentLeg .getRoute ();
115126 Person person = planAgent .getCurrentPlan ().getPerson ();
116127 Vehicle vehicle = this .scenario .getVehicles ().getVehicles ().get (networkRoute .getVehicleId ());
117128
118- double newTravelTime = calcQSimTravelTime (networkRoute , now , person , vehicle );
129+ newTravelTime = calcQSimTravelTime (networkRoute , now , person , vehicle );
119130 LOG .debug ("New travelTime: {}, was {}" , newTravelTime , networkRoute .getTravelTime ().orElseGet (() -> Double .NaN ));
120131
121132 networkRoute .setTravelTime (newTravelTime );
122133 }
123134
124- // !!!! The following is not a teleportation-pull (as it is in NetworkModeDpHandler...), but a teleportation-push. yyyy Are we sure
125- // that these are the same? E.g. if the vehicle needs to be made available for the next driver? kai, feb'25
126-
127- // // remove vehicle of teleported agent from parking spot
128- // QVehicle removedVehicle = null;
129- // if (agent instanceof MobsimDriverAgent driverAgent) {
130- // Id<Vehicle> vehicleId = driverAgent.getPlannedVehicleId();
131- // QLinkI qLinkI = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(linkId);
132- // removedVehicle = qLinkI.removeParkedVehicle(vehicleId);
133- // if (removedVehicle == null) {
134- // throw new RuntimeException(
135- // "Could not remove parked vehicle with id " + vehicleId + " on the link id "
136- // + qNetsimEngine.getVehicles().get(vehicleId).getCurrentLinkId()
137- // + ". Maybe it is currently used by someone else?"
138- // + " (In which case ignoring this exception would lead to duplication of this vehicle.) "
139- // + "Maybe was never placed onto a link?");
140- // }
141- // // yyyy need to check how this is handled elsewhere. I think that with some config settings we wait until the vehicle becomes available--???
142- // }
135+ // Remove vehicle from departure link and schedule delayed arrival at
136+ // destination.
137+ if (agent instanceof MobsimDriverAgent driverAgent ) {
138+ Id <Vehicle > vehicleId = driverAgent .getPlannedVehicleId ();
139+ QLinkI qLinkI = (QLinkI ) this .qNetsimEngine .getNetsimNetwork ().getNetsimLink (linkId );
140+ QVehicle removedVehicle = qLinkI .removeParkedVehicle (vehicleId );
141+ if (removedVehicle == null ) {
142+ // Vehicle not yet available on this link — may still be in transit from another
143+ // teleported agent's trip. Fall back to standard departure handling: with
144+ // VehicleBehavior.wait the agent will wait until the vehicle arrives via
145+ // doSimStep.
146+ LOG .info (
147+ "Vehicle {} not found on link {} for agent {} at time {}. Falling back to standard departure (agent will wait for vehicle)." ,
148+ vehicleId , linkId , driverAgent .getId (), now );
149+ return delegate .handleDeparture (now , agent , linkId );
150+ }
151+ double arrivalTime = now + newTravelTime ;
152+ vehicleArrivals .add (new VehicleArrivalEntry (arrivalTime , removedVehicle , destinationLinkId ));
153+ }
154+
143155 boolean result = teleport .handleDeparture (now , agent , linkId );
144156 Gbl .assertIf (result ); // otherwise we are now confused
145157
146- // teleport vehicle right after agent
147- // if (removedVehicle != null) {
148- // Id<Link> destinationLinkId = agent.getDestinationLinkId();
149- // QLinkI qLinkDest = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(destinationLinkId);
150- // qLinkDest.addParkedVehicle(removedVehicle);
151- // }
152-
153158 return result ;
154159
155160 }
156161
157162 @ Override
158163 public void doSimStep (double time ) {
164+ // Deliver vehicles that have reached their arrival time.
165+ while (!vehicleArrivals .isEmpty () && vehicleArrivals .peek ().arrivalTime <= time ) {
166+ VehicleArrivalEntry entry = vehicleArrivals .poll ();
167+ QLinkI qLinkDest = (QLinkI ) this .qNetsimEngine .getNetsimNetwork ().getNetsimLink (entry .destinationLinkId );
168+ qLinkDest .addParkedVehicle (entry .vehicle );
169+ qLinkDest .makeVehicleAvailableToNextDriver (entry .vehicle );
170+ }
159171 teleport .doSimStep (time );
160172 }
161173
@@ -248,4 +260,12 @@ private boolean switchOffFISS() {
248260 public void setInternalInterface (InternalInterface internalInterface ) {
249261 teleport .setInternalInterface (internalInterface );
250262 }
263+
264+ private record VehicleArrivalEntry (double arrivalTime , QVehicle vehicle ,
265+ Id <Link > destinationLinkId ) implements Comparable <VehicleArrivalEntry > {
266+ @ Override
267+ public int compareTo (VehicleArrivalEntry o ) {
268+ return Double .compare (this .arrivalTime , o .arrivalTime );
269+ }
270+ }
251271}
0 commit comments