mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
Get an end-to-end sample experiment working through the API and Python client
This commit is contained in:
parent
5c337e2e8e
commit
02569b1642
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1306,6 +1306,7 @@ dependencies = [
|
||||
"hyper 0.13.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"map_model 0.1.0",
|
||||
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sim 0.1.0",
|
||||
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -10,6 +10,7 @@ geom = { path = "../geom" }
|
||||
hyper = "0.13.7"
|
||||
lazy_static = "1.4.0"
|
||||
map_model = { path = "../map_model" }
|
||||
serde = "1.0.110"
|
||||
sim = { path = "../sim" }
|
||||
tokio = { version = "0.2", features = ["full"] }
|
||||
url = "2.1.1"
|
||||
|
@ -1,24 +1,58 @@
|
||||
#!/usr/bin/python3
|
||||
# This example will see how changing one traffic signal affects trip times.
|
||||
# Before running this script, start the API server:
|
||||
#
|
||||
# > cargo run --release --bin headless -- --port=1234 data/system/scenarios/montlake/weekday.bin
|
||||
|
||||
import json
|
||||
# You may need to install https://requests.readthedocs.io
|
||||
import requests
|
||||
|
||||
api = 'http://localhost:1234'
|
||||
|
||||
print('Did you just start the simulation? Time is currently', requests.get(api + '/get-time').text)
|
||||
print('Is intersection #42 a traffic signal?', requests.get(api + '/get-traffic-signal', params={'id': 42}).text)
|
||||
def main():
|
||||
api = 'http://localhost:1234'
|
||||
|
||||
# Get the current configuration of one traffic signal
|
||||
ts = requests.get(api + '/get-traffic-signal', params={'id': 67}).json()
|
||||
print('Offset of signal #67 is {} seconds'.format(ts['offset']))
|
||||
print('Phases of signal #67:')
|
||||
for phase in ts['phases']:
|
||||
print('')
|
||||
print(json.dumps(phase, indent=2))
|
||||
# Make sure to start the simulation from the beginning
|
||||
print('Did you just start the simulation? Time is currently', requests.get(api + '/sim/get-time').text)
|
||||
print('Reset the simulation:', requests.get(api + '/sim/reset').text)
|
||||
print()
|
||||
|
||||
# Double the duration of the first phase
|
||||
print()
|
||||
print()
|
||||
ts['phases'][0]['phase_type']['Fixed'] *= 2
|
||||
print('Update the signal config', requests.post(api + '/set-traffic-signal', json=ts).text)
|
||||
# Run 12 hours to get a baseline
|
||||
print('Simulating 12 hours before any edits')
|
||||
print(requests.get(api + '/sim/goto-time', params={'t': '12:00:00'}).text)
|
||||
baseline_trips = process_trips(requests.get(api + '/data/get-finished-trips').json()['trips'])
|
||||
print('Baseline: {} finished trips, total of {} seconds'.format(len(baseline_trips), sum(baseline_trips.values())))
|
||||
print()
|
||||
|
||||
# Modify one traffic signal, doubling the duration of its second phase
|
||||
ts = requests.get(api + '/traffic-signals/get', params={'id': 67}).json()
|
||||
ts['phases'][1]['phase_type']['Fixed'] *= 2
|
||||
# Reset the simulation before applying the edit, since reset also clears edits.
|
||||
print('Reset the simulation:', requests.get(api + '/sim/reset').text)
|
||||
print('Update a traffic signal:', requests.post(api + '/traffic-signals/set', json=ts).text)
|
||||
print()
|
||||
|
||||
# Repeat the experiment
|
||||
print('Simulating 12 hours after the edits')
|
||||
print(requests.get(api + '/sim/goto-time', params={'t': '12:00:00'}).text)
|
||||
experimental_trips = process_trips(requests.get(api + '/data/get-finished-trips').json()['trips'])
|
||||
print('Experiment: {} finished trips, total of {} seconds'.format(len(experimental_trips), sum(experimental_trips.values())))
|
||||
print()
|
||||
|
||||
# Compare -- did this help or not?
|
||||
print('{} more trips finished after the edits (higher is better)'.format(len(experimental_trips) - len(baseline_trips)))
|
||||
print('Experiment was {} seconds faster, over all trips'.format(sum(baseline_trips.values()) - sum(experimental_trips.values())))
|
||||
|
||||
|
||||
# Return a map from trip ID to the duration (in seconds) of the trip. Filter
|
||||
# out aborted (failed) trips.
|
||||
def process_trips(trips):
|
||||
results = {}
|
||||
for (_, trip, mode, duration) in trips:
|
||||
if mode is not None:
|
||||
results[trip] = duration
|
||||
return results
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -10,10 +10,11 @@
|
||||
// ... huge JSON blob
|
||||
|
||||
use abstutil::{CmdArgs, Timer};
|
||||
use geom::Time;
|
||||
use geom::{Duration, Time};
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use map_model::{ControlTrafficSignal, IntersectionID, Map};
|
||||
use sim::{AlertHandler, Sim, SimFlags, SimOptions};
|
||||
use serde::Serialize;
|
||||
use sim::{AlertHandler, Sim, SimFlags, SimOptions, TripID, TripMode};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::sync::RwLock;
|
||||
@ -21,6 +22,8 @@ use std::sync::RwLock;
|
||||
lazy_static::lazy_static! {
|
||||
static ref MAP: RwLock<Map> = RwLock::new(Map::blank());
|
||||
static ref SIM: RwLock<Sim> = RwLock::new(Sim::new(&Map::blank(), SimOptions::new("tmp"), &mut Timer::throwaway()));
|
||||
// TODO Readonly?
|
||||
static ref FLAGS: RwLock<SimFlags> = RwLock::new(SimFlags::for_test("tmp"));
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -32,10 +35,10 @@ async fn main() {
|
||||
|
||||
// Less spam
|
||||
sim_flags.opts.alerts = AlertHandler::Silence;
|
||||
let mut timer = Timer::new("setup headless");
|
||||
let (map, sim, _) = sim_flags.load(&mut timer);
|
||||
let (map, sim, _) = sim_flags.load(&mut Timer::new("setup headless"));
|
||||
*MAP.write().unwrap() = map;
|
||||
*SIM.write().unwrap() = sim;
|
||||
*FLAGS.write().unwrap() = sim_flags;
|
||||
|
||||
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port));
|
||||
println!("Listening on http://{}", addr);
|
||||
@ -81,19 +84,26 @@ fn handle_command(
|
||||
map: &mut Map,
|
||||
) -> Result<String, Box<dyn Error>> {
|
||||
match path {
|
||||
"/get-time" => Ok(sim.time().to_string()),
|
||||
"/goto-time" => {
|
||||
// Controlling the simulation
|
||||
"/sim/reset" => {
|
||||
let (new_map, new_sim, _) = FLAGS.read().unwrap().load(&mut Timer::new("reset sim"));
|
||||
*map = new_map;
|
||||
*sim = new_sim;
|
||||
Ok(format!("sim reloaded"))
|
||||
}
|
||||
"/sim/get-time" => Ok(sim.time().to_string()),
|
||||
"/sim/goto-time" => {
|
||||
let t = Time::parse(¶ms["t"])?;
|
||||
if t <= sim.time() {
|
||||
Err(format!("{} is in the past", t).into())
|
||||
Err(format!("{} is in the past. call /sim/reset first?", t).into())
|
||||
} else {
|
||||
let dt = t - sim.time();
|
||||
sim.timed_step(map, dt, &mut None, &mut Timer::throwaway());
|
||||
sim.timed_step(map, dt, &mut None, &mut Timer::new("goto-time"));
|
||||
Ok(format!("it's now {}", t))
|
||||
}
|
||||
}
|
||||
"/get-delays" => Ok(abstutil::to_json(&sim.get_analytics().intersection_delays)),
|
||||
"/get-traffic-signal" => {
|
||||
// Traffic signals
|
||||
"/traffic-signals/get" => {
|
||||
let i = IntersectionID(params["id"].parse::<usize>()?);
|
||||
if let Some(ts) = map.maybe_get_traffic_signal(i) {
|
||||
Ok(abstutil::to_json(ts))
|
||||
@ -101,11 +111,25 @@ fn handle_command(
|
||||
Err(format!("{} isn't a traffic signal", i).into())
|
||||
}
|
||||
}
|
||||
"/set-traffic-signal" => {
|
||||
"/traffic-signals/set" => {
|
||||
let ts: ControlTrafficSignal = abstutil::from_json(body)?;
|
||||
let id = ts.id;
|
||||
map.incremental_edit_traffic_signal(ts);
|
||||
Ok(format!("cool, got ts updates"))
|
||||
Ok(format!("{} has been updated", id))
|
||||
}
|
||||
// Querying data
|
||||
"/data/get-finished-trips" => Ok(abstutil::to_json(&FinishedTrips {
|
||||
trips: sim.get_analytics().finished_trips.clone(),
|
||||
})),
|
||||
_ => Err("Unknown command".into()),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO I think specifying the API with protobufs or similar will be a better idea.
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct FinishedTrips {
|
||||
// TODO Hack: No TripMode means aborted
|
||||
// Finish time, ID, mode (or None as aborted), trip duration
|
||||
pub trips: Vec<(Time, TripID, Option<TripMode>, Duration)>,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user