1
1
package org .matsim .contrib .drt .prebooking ;
2
2
3
- import java .util .HashSet ;
3
+ import java .util .LinkedList ;
4
4
import java .util .List ;
5
5
import java .util .Map ;
6
6
import java .util .PriorityQueue ;
7
7
import java .util .Queue ;
8
- import java .util .Set ;
9
8
import java .util .function .Supplier ;
10
9
11
10
import org .matsim .api .core .v01 .Id ;
12
11
import org .matsim .api .core .v01 .IdMap ;
12
+ import org .matsim .api .core .v01 .IdSet ;
13
13
import org .matsim .api .core .v01 .Identifiable ;
14
14
import org .matsim .api .core .v01 .population .Person ;
15
15
import org .matsim .contrib .drt .passenger .AcceptedDrtRequest ;
@@ -39,9 +39,13 @@ public class PrebookingStopActivity extends FirstLastSimStepDynActivity implemen
39
39
private final Map <Id <Request >, ? extends AcceptedDrtRequest > pickupRequests ;
40
40
private final Map <Id <Request >, ? extends AcceptedDrtRequest > dropoffRequests ;
41
41
42
- private final IdMap < Request , Double > enterTimes = new IdMap <>(Request . class );
42
+ private final Queue < QueuedRequest > enterTimes = new PriorityQueue <>();
43
43
private final Queue <QueuedRequest > leaveTimes = new PriorityQueue <>();
44
- private final Set <Id <Request >> enteredRequests = new HashSet <>();
44
+
45
+ private final IdSet <Request > enteredRequests = new IdSet <>(Request .class );
46
+
47
+ private final IdSet <Request > registeredPickups = new IdSet <>(Request .class );
48
+ private final IdMap <Request , AcceptedDrtRequest > expectedPickups = new IdMap <>(Request .class );
45
49
46
50
private final PrebookingManager prebookingManager ;
47
51
private final PassengerHandler passengerHandler ;
@@ -93,7 +97,6 @@ private void initDropoffRequests(double now) {
93
97
}
94
98
95
99
private boolean updateDropoffRequests (double now ) {
96
-
97
100
while (!leaveTimes .isEmpty () && leaveTimes .peek ().time <= now ) {
98
101
Id <Request > requestId = leaveTimes .poll ().id ;
99
102
passengerHandler .dropOffPassengers (driver , requestId , now );
@@ -105,60 +108,94 @@ private boolean updateDropoffRequests(double now) {
105
108
}
106
109
107
110
private record QueuedRequest (Id <Request > id , double time ) implements Comparable <QueuedRequest > {
108
-
109
111
@ Override
110
112
public int compareTo (QueuedRequest o ) {
111
113
return Double .compare (this .time , o .time );
112
114
}
113
115
}
114
116
117
+ private int cachedPickupRequestsHash = -1 ;
118
+
115
119
private boolean updatePickupRequests (double now , boolean isFirstStep ) {
116
- var pickupIterator = pickupRequests .values ().iterator ();
117
-
118
- while (pickupIterator .hasNext ()) {
119
- var request = pickupIterator .next ();
120
-
121
- if (!enteredRequests .contains (request .getId ()) && !enterTimes .containsKey (request .getId ())) {
122
- // this is a new request that has been added after the activity has been created
123
- // or that had not arrived yet
124
-
125
- if (passengerHandler .notifyWaitForPassengers (this , this .driver , request .getId ())) {
126
- // agent starts to enter
127
- queuePickup (request , now );
128
- } else if (now > request .getEarliestStartTime () && !isFirstStep ) {
129
- if (abandonVoter .abandonRequest (now , vehicle , request )) {
130
- prebookingManager .abandon (request .getId ());
131
- }
120
+ int pickupRequestsHash = pickupRequests .hashCode ();
121
+
122
+ // part 1: check if the pickup list has been updated dynamically
123
+
124
+ if (isFirstStep || pickupRequestsHash != cachedPickupRequestsHash ) {
125
+ cachedPickupRequestsHash = pickupRequestsHash ;
126
+
127
+ // added requests
128
+ for (AcceptedDrtRequest request : pickupRequests .values ()) {
129
+ if (!registeredPickups .contains (request .getId ())) {
130
+ // in the first step, this is a standard pickup request, later this is a request that has been added after the activity has been created
131
+ expectedPickups .put (request .getId (), request );
132
+ registeredPickups .add (request .getId ());
133
+ }
134
+ }
135
+
136
+ // removed requests (for instance via cancellation)
137
+ var expectedIterator = expectedPickups .iterator ();
138
+ while (expectedIterator .hasNext ()) {
139
+ if (!pickupRequests .containsKey (expectedIterator .next ().getId ())) {
140
+ // a request has been removed from the list of expected pickups
141
+ expectedIterator .remove ();
132
142
}
133
143
}
134
144
}
145
+
146
+ // part 2: handle the requests that we expect but which have not arrived yet
147
+
148
+ var expectedIterator = expectedPickups .values ().iterator ();
149
+ while (expectedIterator .hasNext ()) {
150
+ AcceptedDrtRequest request = expectedIterator .next ();
151
+
152
+ if (passengerHandler .notifyWaitForPassengers (this , this .driver , request .getId ())) {
153
+ // agent starts to enter
154
+ queuePickup (request , now );
155
+ expectedIterator .remove ();
156
+ } else if (now > request .getEarliestStartTime () && !isFirstStep ) {
157
+ if (abandonVoter .abandonRequest (now , vehicle , request )) {
158
+ // abandon the request, but not in the first time step for the sake of event timing
159
+ prebookingManager .abandon (request .getId ());
160
+ expectedIterator .remove ();
161
+ }
162
+ }
163
+ }
164
+
165
+ // part 3: handle the requests that are currently entering the vehicle
166
+
167
+ var enterIterator = enterTimes .iterator ();
135
168
136
- var enterIterator = enterTimes .entrySet ().iterator ();
169
+ // logic is as follows:
170
+ // - let people enter in the order at which they arrived + their interaction time
171
+ // - but in case there is no capacity (others still disembarking) they need to wait
137
172
138
173
while (enterIterator .hasNext ()) {
139
174
var entry = enterIterator .next ();
140
175
int availableCapacity = vehicle .getCapacity () - onboard ;
141
176
142
- if (entry .getValue () <= now ) {
143
- int requiredCapacity = pickupRequests .get (entry .getKey () ).getPassengerCount ();
177
+ if (entry .time <= now ) {
178
+ int requiredCapacity = pickupRequests .get (entry .id ).getPassengerCount ();
144
179
145
180
if (requiredCapacity <= availableCapacity ) {
146
181
// let agent enter now
147
- Verify .verify (passengerHandler .tryPickUpPassengers (this , driver , entry .getKey () , now ));
148
- enteredRequests .add (entry .getKey () );
182
+ Verify .verify (passengerHandler .tryPickUpPassengers (this , driver , entry .id , now ));
183
+ enteredRequests .add (entry .id );
149
184
onboard += requiredCapacity ;
150
185
enterIterator .remove ();
151
186
}
187
+ } else {
188
+ break ;
152
189
}
153
190
}
154
191
155
- return enterTimes .size () == 0 && pickupRequests .size () == enteredRequests .size ();
192
+ return expectedPickups .size () == 0 && pickupRequests .size () == enteredRequests .size ();
156
193
}
157
194
158
195
private void queuePickup (AcceptedDrtRequest request , double now ) {
159
196
prebookingManager .notifyPickup (now , request );
160
197
double enterTime = now + stopDurationProvider .calcPickupDuration (vehicle , request .getRequest ());
161
- enterTimes .put ( request .getId (), enterTime );
198
+ enterTimes .add ( new QueuedRequest ( request .getId (), enterTime ) );
162
199
}
163
200
164
201
@ Override
@@ -170,6 +207,7 @@ protected void simStep(double now) {
170
207
public void notifyPassengersAreReadyForDeparture (List <MobsimPassengerAgent > passengers , double now ) {
171
208
var request = getRequestForPassengers (passengers .stream ().map (Identifiable ::getId ).toList ());
172
209
queuePickup (request , now );
210
+ expectedPickups .remove (request .getId ());
173
211
}
174
212
175
213
private AcceptedDrtRequest getRequestForPassengers (List <Id <Person >> passengerIds ) {
0 commit comments