mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-22 14:59:37 +02:00
Add a new async exercise: elevator simulation. (#557)
This commit is contained in:
parent
d8e442b9cb
commit
6c97e1a7be
src
SUMMARY.md
exercises/day-4
@ -226,6 +226,10 @@
|
||||
- [With Java](android/interoperability/java.md)
|
||||
- [Exercises](exercises/day-4/android.md)
|
||||
|
||||
# Async (temp until issue181 is closed)
|
||||
|
||||
- [Exercises](exercises/day-4/elevator.md)
|
||||
|
||||
# Final Words
|
||||
|
||||
- [Thanks!](thanks.md)
|
||||
|
1
src/exercises/day-4/async-elevator.md
Normal file
1
src/exercises/day-4/async-elevator.md
Normal file
@ -0,0 +1 @@
|
||||
# Exercises
|
91
src/exercises/day-4/elevator.md
Normal file
91
src/exercises/day-4/elevator.md
Normal file
@ -0,0 +1,91 @@
|
||||
# Elevator Operation
|
||||
|
||||
Elevators seem simple. You press a button, doors open, you wait, and you're at
|
||||
the floor you requested. But implementing an elevator controller is surprisingly
|
||||
difficult! This exercise involves building a simple elevator control that
|
||||
operates in a simple simulator.
|
||||
|
||||
The overall design of this elevator uses the actor pattern: you will implement a
|
||||
controller task that communicates with other components of the elevator system
|
||||
by sending and receiving messages.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Download the [exercise template](../../comprehensive-rust-exercises.zip) and look in the `elevator`
|
||||
directory for the following files.
|
||||
|
||||
`src/main.rs`:
|
||||
|
||||
<!-- File src/main.rs -->
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include elevator/src/main.rs}}
|
||||
```
|
||||
|
||||
`src/building.rs`:
|
||||
|
||||
<!-- File src/building.rs -->
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include elevator/src/building.rs}}
|
||||
```
|
||||
|
||||
`src/driver.rs`:
|
||||
|
||||
<!-- File src/driver.rs -->
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include elevator/src/driver.rs}}
|
||||
```
|
||||
|
||||
`src/controller.rs`:
|
||||
|
||||
<!-- File src/controller.rs -->
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include elevator/src/controller.rs}}
|
||||
```
|
||||
|
||||
`Cargo.toml` (you shouldn't need to change this):
|
||||
|
||||
<!-- File Cargo.toml -->
|
||||
|
||||
```toml
|
||||
{{#include elevator/Cargo.toml}}
|
||||
```
|
||||
|
||||
Use `cargo run` to run the elevator simulation.
|
||||
|
||||
## Exercises
|
||||
|
||||
Begin by implementing a controller that can transport the passengers provided by
|
||||
the simple driver. There is only one elevator, and passengers always go from
|
||||
floor 0 to floor 2, one-by-one.
|
||||
|
||||
Once you have this done, make the problem more complex. Suggested tasks:
|
||||
|
||||
* Make the driver more complex, with passengers arriving at random floors with
|
||||
random destinations at random times.
|
||||
|
||||
* Create a building with more than one elevator, and adjust the controller to
|
||||
handle this efficiently.
|
||||
|
||||
* Add additional events and metadata to analyze your controller's efficiency.
|
||||
What is the distribution of wait time for passengers? Is the result fair?
|
||||
|
||||
* Modify the building to support a maximum passenger capacity for each
|
||||
elevator, and modify the controller to take this information into account.
|
||||
|
||||
* Update the driver to simulate business traffic, with lots of passengers going
|
||||
up from the ground floor at the same time, and those passengers returning to
|
||||
the ground floor some time later. Can your controller adjust to these
|
||||
circumstances?
|
||||
|
||||
* Modify the building to support "destination dispatch", where passengers
|
||||
signal their destination floor in the elevator lobby, before boarding the
|
||||
elevator.
|
||||
|
||||
* If you are taking the course with other students, trade controllers or
|
||||
drivers with another student to see how robust your design is.
|
||||
|
||||
* Build a textual or graphical display of the elevators as they run.
|
318
src/exercises/day-4/elevator/Cargo.lock
generated
Normal file
318
src/exercises/day-4/elevator/Cargo.lock
generated
Normal file
@ -0,0 +1,318 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "elevator"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
9
src/exercises/day-4/elevator/Cargo.toml
Normal file
9
src/exercises/day-4/elevator/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[workspace]
|
||||
|
||||
[package]
|
||||
name = "elevator"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.26.0", features = ["full"] }
|
234
src/exercises/day-4/elevator/src/building.rs
Normal file
234
src/exercises/day-4/elevator/src/building.rs
Normal file
@ -0,0 +1,234 @@
|
||||
//! The building simulates floors and elevators.
|
||||
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
use tokio::task;
|
||||
use tokio::time;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
}
|
||||
|
||||
/// A passenger is a person with a destination floor in mind.
|
||||
#[derive(Debug)]
|
||||
struct Passenger {
|
||||
destination: FloorId,
|
||||
}
|
||||
|
||||
/// FloorId identifies a floor. These are zero-based integers.
|
||||
pub type FloorId = usize;
|
||||
|
||||
/// Floor represents the current status of a floor in the building.
|
||||
#[derive(Default, Debug)]
|
||||
struct Floor {
|
||||
passengers: Vec<Passenger>,
|
||||
}
|
||||
|
||||
/// ElevatorId identifies an elevator in the building. These are zero-based integers.
|
||||
pub type ElevatorId = usize;
|
||||
|
||||
/// Elevator represents the current status of an elevator in the building.
|
||||
#[derive(Default, Debug)]
|
||||
struct Elevator {
|
||||
/// Floor the elevator is currently on. In the simulation the elevator
|
||||
/// transports instantaneously from one floor to the next in a single
|
||||
/// simulation tick.
|
||||
position: FloorId,
|
||||
/// Destination floor for the elevator, if any. This can change at any time.
|
||||
destination: Option<FloorId>,
|
||||
/// Passengers currently on the elevator.
|
||||
passengers: Vec<Passenger>,
|
||||
/// True if the elevator is stopped with the doors open. The elevator
|
||||
/// will not move with the doors open, but they will close at the next
|
||||
/// tick of the simulation.
|
||||
doors_open: bool,
|
||||
}
|
||||
|
||||
/// A BuildingEvent is an event that occurs in the building.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BuildingEvent {
|
||||
/// A passenger has pressed a floor button in the elevator.
|
||||
FloorButtonPressed(ElevatorId, FloorId),
|
||||
/// A passenger on the given floor has pressed the call button.
|
||||
CallButtonPressed(FloorId, Direction),
|
||||
/// The elevator has arrived at the given floor. If this is the
|
||||
/// elevator's destination, then it will stop open its doors.
|
||||
AtFloor(ElevatorId, FloorId),
|
||||
/// A passenger has been delivered to their desired floor.
|
||||
PassengerDelivered(FloorId),
|
||||
}
|
||||
|
||||
/// A BuildingCommand tells the building what to do.
|
||||
#[derive(Debug)]
|
||||
pub enum BuildingCommand {
|
||||
/// Set the elevator's destination. The elevator will close its doors
|
||||
/// if necessary and then begin moving toward this floor.
|
||||
GoToFloor(ElevatorId, FloorId),
|
||||
}
|
||||
|
||||
/// A DriverCommand is a message from the driver to change the state of
|
||||
/// the building.
|
||||
#[derive(Debug)]
|
||||
pub enum DriverCommand {
|
||||
/// A passenger has arrived and is waiting for an elevator. The passenger will automatically
|
||||
/// press the relevant call button, board the elevator when it arrives, press their floor
|
||||
/// button, and depart when the doors open on their destination floor.
|
||||
PassengerArrived { at: FloorId, destination: FloorId },
|
||||
|
||||
/// Halt all activity in the building and end the building task.
|
||||
Halt,
|
||||
}
|
||||
|
||||
/// Building manages the current status of the building.
|
||||
#[derive(Debug)]
|
||||
pub struct Building {
|
||||
floors: Vec<Floor>,
|
||||
elevators: Vec<Elevator>,
|
||||
}
|
||||
|
||||
impl Building {
|
||||
pub fn new(num_floors: usize, num_elevators: usize) -> Self {
|
||||
let mut floors = vec![];
|
||||
for _ in 0..num_floors {
|
||||
floors.push(Floor::default());
|
||||
}
|
||||
let mut elevators = vec![];
|
||||
for _ in 0..num_elevators {
|
||||
elevators.push(Elevator::default());
|
||||
}
|
||||
Self { floors, elevators }
|
||||
}
|
||||
|
||||
/// Start the building. The resulting channels are used to communicate
|
||||
/// with the building
|
||||
pub fn start(
|
||||
self,
|
||||
) -> (
|
||||
task::JoinHandle<()>,
|
||||
broadcast::Receiver<BuildingEvent>,
|
||||
mpsc::Sender<BuildingCommand>,
|
||||
mpsc::Sender<DriverCommand>,
|
||||
) {
|
||||
let (events_tx, events_rx) = broadcast::channel(10);
|
||||
let (building_cmd_tx, building_cmd_rx) = mpsc::channel(10);
|
||||
let (driver_cmd_tx, driver_cmd_rx) = mpsc::channel(10);
|
||||
let task = tokio::spawn(self.run(events_tx, building_cmd_rx, driver_cmd_rx));
|
||||
(task, events_rx, building_cmd_tx, driver_cmd_tx)
|
||||
}
|
||||
|
||||
async fn run(
|
||||
mut self,
|
||||
events_tx: broadcast::Sender<BuildingEvent>,
|
||||
mut building_cmd_rx: mpsc::Receiver<BuildingCommand>,
|
||||
mut driver_cmd_rx: mpsc::Receiver<DriverCommand>,
|
||||
) {
|
||||
let mut ticker = time::interval(time::Duration::from_millis(100));
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(BuildingCommand::GoToFloor(el, fl)) = building_cmd_rx.recv() => {
|
||||
self.elevators[el].destination = Some(fl);
|
||||
}
|
||||
Some(cmd) = driver_cmd_rx.recv() => {
|
||||
match cmd {
|
||||
DriverCommand::PassengerArrived{at, destination} => {
|
||||
self.new_passenger(&events_tx, at, destination).await;
|
||||
}
|
||||
DriverCommand::Halt => return,
|
||||
}
|
||||
}
|
||||
_ = ticker.tick() => self.move_elevators(&events_tx).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Move the elevators toward their destinations.
|
||||
async fn move_elevators(&mut self, events_tx: &broadcast::Sender<BuildingEvent>) {
|
||||
for el in 0..self.elevators.len() {
|
||||
let elevator = &mut self.elevators[el];
|
||||
|
||||
// If the elevator's doors are open, close them and wait for the next tick.
|
||||
if elevator.doors_open {
|
||||
elevator.doors_open = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the elevator has somewhere to go, move toward it.
|
||||
if let Some(dest) = elevator.destination {
|
||||
if dest > elevator.position {
|
||||
elevator.position += 1;
|
||||
}
|
||||
if dest < elevator.position {
|
||||
elevator.position -= 1;
|
||||
}
|
||||
events_tx
|
||||
.send(BuildingEvent::AtFloor(el, elevator.position))
|
||||
.unwrap();
|
||||
|
||||
// If the elevator has reached its destination, open
|
||||
// the doors and let passengers get on and off.
|
||||
if elevator.position == dest {
|
||||
elevator.destination = None;
|
||||
elevator.doors_open = true;
|
||||
self.exchange_passengers(&events_tx, el).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a new passenger arriving at the given floor.
|
||||
async fn new_passenger(
|
||||
&mut self,
|
||||
events_tx: &broadcast::Sender<BuildingEvent>,
|
||||
at: FloorId,
|
||||
destination: FloorId,
|
||||
) {
|
||||
println!("Passenger arrived at {} going to {}", at, destination);
|
||||
if at == destination {
|
||||
events_tx
|
||||
.send(BuildingEvent::PassengerDelivered(destination))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
self.floors[at].passengers.push(Passenger { destination });
|
||||
let dir = if at < destination {
|
||||
Direction::Up
|
||||
} else {
|
||||
Direction::Down
|
||||
};
|
||||
events_tx
|
||||
.send(BuildingEvent::CallButtonPressed(at, dir))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// The doors for the given elevator are open, so take on and discharge passengers.
|
||||
async fn exchange_passengers(
|
||||
&mut self,
|
||||
events_tx: &broadcast::Sender<BuildingEvent>,
|
||||
el: ElevatorId,
|
||||
) {
|
||||
let elevator = &mut self.elevators[el];
|
||||
let fl = elevator.position;
|
||||
|
||||
// Handle passengers leaving the elevator at their floor.
|
||||
let (this_floor, other_floors): (Vec<Passenger>, Vec<Passenger>) = elevator
|
||||
.passengers
|
||||
.drain(..)
|
||||
.partition(|px| px.destination == fl);
|
||||
for px in this_floor {
|
||||
events_tx
|
||||
.send(BuildingEvent::PassengerDelivered(px.destination))
|
||||
.unwrap();
|
||||
}
|
||||
elevator.passengers = other_floors;
|
||||
|
||||
// Handle passengers entering the elevator.
|
||||
for px in self.floors[fl].passengers.drain(..) {
|
||||
events_tx
|
||||
.send(BuildingEvent::FloorButtonPressed(el, px.destination))
|
||||
.unwrap();
|
||||
elevator.passengers.push(px);
|
||||
}
|
||||
}
|
||||
}
|
28
src/exercises/day-4/elevator/src/controller.rs
Normal file
28
src/exercises/day-4/elevator/src/controller.rs
Normal file
@ -0,0 +1,28 @@
|
||||
//! The controller directs the elevators to operate so that passengers
|
||||
//! get to their destinations.
|
||||
|
||||
use crate::building::{BuildingCommand, BuildingEvent};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
pub async fn controller(
|
||||
mut events_rx: broadcast::Receiver<BuildingEvent>,
|
||||
building_cmd_tx: mpsc::Sender<BuildingCommand>,
|
||||
) {
|
||||
while let Ok(evt) = events_rx.recv().await {
|
||||
match evt {
|
||||
BuildingEvent::CallButtonPressed(at, _) => {
|
||||
building_cmd_tx
|
||||
.send(BuildingCommand::GoToFloor(0, at))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
BuildingEvent::FloorButtonPressed(_, destination) => {
|
||||
building_cmd_tx
|
||||
.send(BuildingCommand::GoToFloor(0, destination))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
35
src/exercises/day-4/elevator/src/driver.rs
Normal file
35
src/exercises/day-4/elevator/src/driver.rs
Normal file
@ -0,0 +1,35 @@
|
||||
//! The driver controls when and where passengers arrive.
|
||||
|
||||
use crate::building::{Building, BuildingEvent, DriverCommand};
|
||||
use tokio::sync::{broadcast, mpsc};
|
||||
|
||||
/// Create a new building to be driven by this driver.
|
||||
pub fn make_building() -> Building {
|
||||
Building::new(3, 1)
|
||||
}
|
||||
|
||||
/// Simulate people arriving at the ground floor and going to the first floor, one by one.
|
||||
pub async fn driver(
|
||||
mut events_rx: broadcast::Receiver<BuildingEvent>,
|
||||
driver_cmd_tx: mpsc::Sender<DriverCommand>,
|
||||
) {
|
||||
for _ in 0..3 {
|
||||
// A passenger has arrived..
|
||||
driver_cmd_tx
|
||||
.send(DriverCommand::PassengerArrived {
|
||||
at: 0,
|
||||
destination: 2,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Wait until they are delivered..
|
||||
while let Ok(evt) = events_rx.recv().await {
|
||||
if let BuildingEvent::PassengerDelivered(_) = evt {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
driver_cmd_tx.send(DriverCommand::Halt).await.unwrap();
|
||||
}
|
23
src/exercises/day-4/elevator/src/main.rs
Normal file
23
src/exercises/day-4/elevator/src/main.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use building::BuildingEvent;
|
||||
use tokio::sync::broadcast;
|
||||
|
||||
mod building;
|
||||
mod controller;
|
||||
mod driver;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let building = driver::make_building();
|
||||
let (building_task, events_rx, building_cmd_tx, driver_cmd_tx) = building.start();
|
||||
|
||||
tokio::spawn(print_events(events_rx.resubscribe()));
|
||||
tokio::spawn(driver::driver(events_rx.resubscribe(), driver_cmd_tx));
|
||||
tokio::spawn(controller::controller(events_rx, building_cmd_tx));
|
||||
building_task.await.unwrap();
|
||||
}
|
||||
|
||||
async fn print_events(mut events_rx: broadcast::Receiver<BuildingEvent>) {
|
||||
while let Ok(evt) = events_rx.recv().await {
|
||||
println!("BuildingEvent::{:?}", evt);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user