Skip to content

Commit dfb3c3b

Browse files
committed
added explanations to taxi_sim example
1 parent e8cad49 commit dfb3c3b

File tree

1 file changed

+99
-12
lines changed

1 file changed

+99
-12
lines changed

control/simulation/taxi_sim.py

Lines changed: 99 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""
33
Taxi simulator
44
5-
Sample run with two cars, random seed = 4::
5+
Sample run with two cars, random seed = 4. This is a valid doctest.
66
77
>>> main(num_taxis=2, seed=10)
88
taxi: 0 Event(time=0, proc=0, action='leave garage')
@@ -23,6 +23,8 @@
2323
taxi: 1 Event(time=76, proc=1, action='going home')
2424
*** end of events ***
2525
26+
See explanation and longer sample run at the end of this module.
27+
2628
"""
2729

2830
import sys
@@ -43,18 +45,20 @@ def compute_delay(interval):
4345
"""Compute action delay using exponential distribution"""
4446
return int(random.expovariate(1/interval)) + 1
4547

46-
47-
def taxi_process(ident, trips, start_time=0):
48+
# BEGIN TAXI_PROCESS
49+
def taxi_process(ident, trips, start_time=0): # <1>
4850
"""Yield to simulator issuing event at each state change"""
49-
time = yield Event(start_time, ident, 'leave garage')
50-
for i in range(trips):
51-
prowling_ends = time + compute_delay(SEARCH_INTERVAL)
52-
time = yield Event(prowling_ends, ident, 'pick up passenger')
51+
time = yield Event(start_time, ident, 'leave garage') # <2>
52+
for i in range(trips): # <3>
53+
prowling_ends = time + compute_delay(SEARCH_INTERVAL) # <4>
54+
time = yield Event(prowling_ends, ident, 'pick up passenger') # <5>
5355

54-
trip_ends = time + compute_delay(TRIP_DURATION)
55-
time = yield Event(trip_ends, ident, 'drop off passenger')
56+
trip_ends = time + compute_delay(TRIP_DURATION) # <6>
57+
time = yield Event(trip_ends, ident, 'drop off passenger') # <7>
5658

57-
yield Event(trip_ends + 1, ident, 'going home')
59+
yield Event(time + 1, ident, 'going home') # <8>
60+
# <9>
61+
# END TAXI_PROCESS
5862

5963
# BEGIN TAXI_SIMULATOR
6064
class Simulator:
@@ -129,10 +133,93 @@ def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXIS,
129133

130134

131135
"""
132-
Sample run:
136+
Notes for the ``taxi_process`` coroutine::
137+
138+
<1> `taxi_process` will be called once per taxi, creating a generator
139+
object to represent its operations. `ident` is the number of the taxi
140+
(eg. 0, 1, 2 in the sample run); `trips` is the number of trips this
141+
taxi will make before going home; `start_time` is when the taxi
142+
leaves the garage.
143+
144+
<2> The first `Event` yielded is `'leave garage'`. This suspends the
145+
coroutine, and lets the simulation main loop proceed to the next
146+
scheduled event. When it's time to reactivate this process, the main
147+
loop will `send` the current simulation time, which is assigned to
148+
`time`.
149+
150+
<3> This block will be repeated once for each trip.
151+
152+
<4> The ending time of the search for a passenger is computed.
153+
154+
<5> An `Event` signaling passenger pick up is yielded. The coroutine
155+
pauses here. When the time comes to reactivate this coroutine,
156+
the main loop will again `send` the current time.
157+
158+
<6> The ending time of the trip is computed, taking into account the
159+
current `time`.
160+
161+
<7> An `Event` signaling passenger drop off is yielded. Coroutine
162+
suspended again, waiting for the main loop to send the time of when
163+
it's time to continue.
164+
165+
<8> The `for` loop ends after the given number of trips, and a final
166+
`'going home'` event is yielded, to happen 1 minute after the current
167+
time. The coroutine will suspend for the last time. When reactivated,
168+
it will be sent the time from the simulation main loop, but here I
169+
don't assign it to any variable because it will not be useful.
170+
171+
<9> When the coroutine falls off the end, the coroutine object raises
172+
`StopIteration`.
173+
174+
175+
Notes for the ``Simulator.run`` method::
176+
177+
<1> The simulation `end_time` is the only required argument for `run`.
178+
179+
<2> Use `sorted` to retrieve the `self.procs` items ordered by the
180+
integer key; we don't care about the key, so assign it to `_`.
181+
182+
<3> `next(proc)` primes each coroutine by advancing it to the first
183+
yield, so it's ready to be sent data. An `Event` is yielded.
184+
185+
<4> Add each event to the `self.events` `PriorityQueue`. The first
186+
event for each taxi is `'leave garage'`, as seen in the sample run
187+
(ex_taxi_process>>).
188+
189+
<5> Main loop of the simulation: run until the current `time` equals
190+
or exceeds the `end_time`.
191+
192+
<6> The main loop may also exit if there are no pending events in the
193+
queue.
194+
195+
<7> Get `Event` with the smallest `time` in the queue; this is the
196+
`current_event`.
197+
198+
<8> Display the `Event`, identifying the taxi and adding indentation
199+
according to the taxi id.
200+
201+
<9> Update the simulation time with the time of the `current_event`.
202+
203+
<10> Retrieve the coroutine for this taxi from the `self.procs`
204+
dictionary.
205+
206+
<11> Send the `time` to the coroutine. The coroutine will yield the
207+
`next_event` or raise `StopIteration` it's finished.
208+
209+
<12> If `StopIteration` was raised, delete the coroutine from the
210+
`self.procs` dictionary.
211+
212+
<13> Otherwise, put the `next_event` in the queue.
213+
214+
<14> If the loop exits because the simulation time passed, display the
215+
number of events pending (which may be zero by coincidence,
216+
sometimes).
217+
218+
219+
Sample run from the command line::
133220
134221
# BEGIN TAXI_SAMPLE_RUN
135-
$ $ clear; python3 taxi_sim.py -t 3 -s 19
222+
$ clear; python3 taxi_sim.py -t 3 -s 19
136223
taxi: 0 Event(time=0, proc=0, action='leave garage')
137224
taxi: 0 Event(time=5, proc=0, action='pick up passenger')
138225
taxi: 1 Event(time=10, proc=1, action='leave garage')

0 commit comments

Comments
 (0)