Skip to content

Commit

Permalink
Convert the headless server from CmdArgs to structopt. #745
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Oct 26, 2021
1 parent cf3d00f commit 0464ab4
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 71 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion abstutil/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub use utils::*;
mod cli;
mod clone;
mod collections;
mod logger;
pub mod logger;
mod process;
mod serde;
pub mod time;
Expand Down
1 change: 1 addition & 0 deletions headless/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ rand_xorshift = "0.3.0"
serde = "1.0.123"
serde_json = "1.0.61"
sim = { path = "../sim" }
structopt = "0.3.23"
tokio = { version = "1.1.1", features = ["full"] }
url = "2.2.0"
59 changes: 36 additions & 23 deletions headless/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// This runs a simulation without any graphics and serves a very basic API to control things. See
// https://a-b-street.github.io/docs/tech/dev/api.html for documentation. To run this:
//
// > cd headless; cargo run -- --port=1234
// > curl http://localhost:1234/sim/get-time
// 00:00:00.0
// > curl http://localhost:1234/sim/goto-time?t=01:01:00
// it's now 01:01:00.0
// > curl http://localhost:1234/data/get-road-thruput
// ... huge JSON blob
//! This runs a simulation without any graphics and serves a very basic API to control things. See
//! https://a-b-street.github.io/docs/tech/dev/api.html for documentation. To run this:
//!
//! > cd headless; cargo run -- --port=1234
//! > curl http://localhost:1234/sim/get-time
//! 00:00:00.0
//! > curl http://localhost:1234/sim/goto-time?t=01:01:00
//! it's now 01:01:00.0
//! > curl http://localhost:1234/data/get-road-thruput
//! ... huge JSON blob

#[macro_use]
extern crate anyhow;
Expand All @@ -23,9 +23,10 @@ use hyper::{Body, Request, Response, Server, StatusCode};
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use serde::{Deserialize, Serialize};
use structopt::StructOpt;

use abstio::MapName;
use abstutil::{serialize_btreemap, CmdArgs, Timer};
use abstutil::{serialize_btreemap, Timer};
use geom::{Distance, Duration, LonLat, Time};
use map_model::{
CompressedMovementID, ControlTrafficSignal, EditCmd, EditIntersection, IntersectionID, Map,
Expand All @@ -50,28 +51,40 @@ lazy_static::lazy_static! {
});
}

#[derive(StructOpt)]
#[structopt(
name = "headless",
about = "Simulate traffic with a JSON API, not a GUI"
)]
struct Args {
/// What port to run the JSON API on.
#[structopt(long)]
port: u16,
/// An arbitrary number to seed the random number generator. This is input to the deterministic
/// simulation, so different values affect results.
// TODO default_value can only handle strings, so copying SimFlags::RNG_SEED
#[structopt(long, default_value = "42")]
rng_seed: u64,
#[structopt(flatten)]
opts: SimOptions,
}

#[tokio::main]
async fn main() {
let mut args = CmdArgs::new();
let mut timer = Timer::new("setup headless");
let rng_seed = args
.optional_parse("--rng_seed", |s| s.parse())
.unwrap_or(SimFlags::RNG_SEED);
let opts = SimOptions::from_args(&mut args, rng_seed);
let port = args.required("--port").parse::<u16>().unwrap();
args.done();
abstutil::logger::setup();
let args = Args::from_args();

{
let mut load = LOAD.write().unwrap();
load.rng_seed = rng_seed;
load.opts = opts;
load.rng_seed = args.rng_seed;
load.opts = args.opts;

let (map, sim) = load.setup(&mut timer);
let (map, sim) = load.setup(&mut Timer::new("setup headless"));
*MAP.write().unwrap() = map;
*SIM.write().unwrap() = sim;
}

let addr = std::net::SocketAddr::from(([127, 0, 0, 1], port));
let addr = std::net::SocketAddr::from(([127, 0, 0, 1], args.port));
info!("Listening on http://{}", addr);
let serve_future = Server::bind(&addr).serve(hyper::service::make_service_fn(|_| async {
Ok::<_, hyper::Error>(hyper::service::service_fn(serve_req))
Expand Down
1 change: 1 addition & 0 deletions sim/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ rand = "0.8.3"
rand_distr = "0.4.0"
rand_xorshift = "0.3.0"
serde = "1.0.123"
structopt = "0.3.23"

[[bin]]
name = "run_scenario"
Expand Down
4 changes: 2 additions & 2 deletions sim/src/mechanics/driving.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ impl DrivingSimState {
cars: FixedMap::new(),
queues: HashMap::new(),
events: Vec::new(),
recalc_lanechanging: opts.recalc_lanechanging,
handle_uber_turns: opts.handle_uber_turns,
recalc_lanechanging: !opts.dont_recalc_lanechanging,
handle_uber_turns: !opts.dont_handle_uber_turns,
waiting_to_spawn: BTreeMap::new(),

time_to_unpark_onstreet: Duration::seconds(10.0),
Expand Down
6 changes: 3 additions & 3 deletions sim/src/mechanics/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ impl IntersectionSimState {
let mut sim = IntersectionSimState {
state: BTreeMap::new(),
use_freeform_policy_everywhere: opts.use_freeform_policy_everywhere,
dont_block_the_box: opts.dont_block_the_box,
break_turn_conflict_cycles: opts.break_turn_conflict_cycles,
handle_uber_turns: opts.handle_uber_turns,
dont_block_the_box: !opts.allow_block_the_box,
break_turn_conflict_cycles: !opts.dont_break_turn_conflict_cycles,
handle_uber_turns: !opts.dont_handle_uber_turns,
disable_turn_conflicts: opts.disable_turn_conflicts,
blocked_by: BTreeSet::new(),
events: Vec::new(),
Expand Down
102 changes: 64 additions & 38 deletions sim/src/sim/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use instant::Instant;
use rand::SeedableRng;
use rand_xorshift::XorShiftRng;
use serde::{Deserialize, Serialize};
use structopt::StructOpt;

use abstio::{CityName, MapName};
use abstutil::{prettyprint_usize, serialized_size_bytes, CmdArgs, Timer};
Expand Down Expand Up @@ -74,62 +75,85 @@ pub(crate) struct Ctx<'a> {
}

/// Options controlling the traffic simulation.
#[derive(Clone)]
#[derive(Clone, StructOpt)]
pub struct SimOptions {
/// Used to distinguish savestates for running the same scenario.
#[structopt(long, default_value = "unnamed")]
pub run_name: String,
/// Ignore all stop signs and traffic signals, instead using a "freeform" policy to control
/// access to intersections. If a requested turn doesn't conflict with an already accepted one,
/// immediately accept it. FIFO ordering, no balancing between different movements.
#[structopt(long)]
pub use_freeform_policy_everywhere: bool,
/// Prevent a vehicle from starting a turn if their target lane is already full, since this may
/// mean they'll get stuck blocking the intersection.
pub dont_block_the_box: bool,
/// As a vehicle follows a route, opportunistically make small changes to use a different lane,
/// based on some score of "least-loaded" lane.
pub recalc_lanechanging: bool,
/// If a cycle of vehicles depending on each other to turn is detected, temporarily allow
/// "blocking the box" to try to break gridlock.
pub break_turn_conflict_cycles: bool,
/// Enable experimental handling for "uber-turns", sequences of turns through complex
/// Allow a vehicle to start a turn, even if their target lane is already full. This may mean
/// they'll get stuck blocking the intersection.
#[structopt(long)]
pub allow_block_the_box: bool,
/// Normally as a vehicle follows a route, it opportunistically make small changes to use a different lane,
/// based on some score of "least-loaded" lane. Disable this default behavior.
#[structopt(long)]
pub dont_recalc_lanechanging: bool,
/// Normally if a cycle of vehicles depending on each other to turn is detected, temporarily allow
/// "blocking the box" to try to break gridlock. Disable this default behavior.
#[structopt(long)]
pub dont_break_turn_conflict_cycles: bool,
/// Disable experimental handling for "uber-turns", sequences of turns through complex
/// intersections with short roads. "Locks" the entire movement before starting, and ignores
/// red lights after starting.
pub handle_uber_turns: bool,
/// Enable an experimental SEIR pandemic model.
#[structopt(long)]
pub dont_handle_uber_turns: bool,
/// Enable an experimental SEIR pandemic model. This requires an RNG seed, which can be the
/// same or different from the one used for the rest of the simulation.
#[structopt(long, parse(try_from_str = parse_rng))]
pub enable_pandemic_model: Option<XorShiftRng>,
/// When a warning is encountered during simulation, specifies how to respond.
#[structopt(long, parse(try_from_str = parse_alert_handler), default_value = "print")]
pub alerts: AlertHandler,
/// Ignore parking data in the map and instead treat every building as if it has unlimited
/// capacity for vehicles.
///
/// Some maps always have this hardcoded on -- see the code for the list.
#[structopt(long)]
pub infinite_parking: bool,
/// Allow all agents to immediately proceed into an intersection, even if they'd hit another
/// agent. Obviously this destroys realism of the simulation, but can be used to debug
/// gridlock. Also implies freeform_policy, so vehicles ignore traffic signals.
#[structopt(long)]
pub disable_turn_conflicts: bool,
/// Don't collect any analytics. Only useful for benchmarking and debugging gridlock more
/// quickly.
#[structopt(long)]
pub skip_analytics: bool,
}

impl Default for SimOptions {
fn default() -> SimOptions {
SimOptions::new("tmp")
impl SimOptions {
pub fn new(run_name: &str) -> SimOptions {
SimOptions {
run_name: run_name.to_string(),
use_freeform_policy_everywhere: false,
allow_block_the_box: false,
dont_recalc_lanechanging: false,
dont_break_turn_conflict_cycles: false,
dont_handle_uber_turns: false,
enable_pandemic_model: None,
alerts: AlertHandler::Print,
infinite_parking: false,
disable_turn_conflicts: false,
skip_analytics: false,
}
}
}

impl SimOptions {
// TODO Remove
pub fn from_args(args: &mut CmdArgs, rng_seed: u64) -> SimOptions {
SimOptions {
run_name: args
.optional("--run_name")
.unwrap_or_else(|| "unnamed".to_string()),
use_freeform_policy_everywhere: args.enabled("--freeform_policy"),
dont_block_the_box: !args.enabled("--disable_block_the_box"),
recalc_lanechanging: !args.enabled("--disable_recalc_lc"),
break_turn_conflict_cycles: !args.enabled("--disable_break_turn_conflict_cycles"),
handle_uber_turns: !args.enabled("--disable_handle_uber_turns"),
allow_block_the_box: args.enabled("--disable_block_the_box"),
dont_recalc_lanechanging: args.enabled("--disable_recalc_lc"),
dont_break_turn_conflict_cycles: args.enabled("--disable_break_turn_conflict_cycles"),
dont_handle_uber_turns: args.enabled("--disable_handle_uber_turns"),
enable_pandemic_model: if args.enabled("--pandemic") {
Some(XorShiftRng::seed_from_u64(rng_seed))
} else {
Expand All @@ -151,6 +175,17 @@ impl SimOptions {
}
}

impl Default for SimOptions {
fn default() -> SimOptions {
SimOptions::new("tmp")
}
}

fn parse_rng(x: &str) -> Result<XorShiftRng> {
let seed: u64 = x.parse()?;
Ok(XorShiftRng::seed_from_u64(seed))
}

#[derive(Clone)]
pub enum AlertHandler {
/// Just print the alert to STDOUT
Expand All @@ -167,21 +202,12 @@ impl Default for AlertHandler {
}
}

impl SimOptions {
pub fn new(run_name: &str) -> SimOptions {
SimOptions {
run_name: run_name.to_string(),
use_freeform_policy_everywhere: false,
dont_block_the_box: true,
recalc_lanechanging: true,
break_turn_conflict_cycles: true,
handle_uber_turns: true,
enable_pandemic_model: None,
alerts: AlertHandler::Print,
infinite_parking: false,
disable_turn_conflicts: false,
skip_analytics: false,
}
fn parse_alert_handler(x: &str) -> Result<AlertHandler> {
match x {
"print" => Ok(AlertHandler::Print),
"block" => Ok(AlertHandler::Block),
"silence" => Ok(AlertHandler::Silence),
_ => bail!("Bad --alerts={}. Must be print|block|silence", x),
}
}

Expand All @@ -204,7 +230,7 @@ impl Sim {

// Hack around simulation bugs to get a Tehran map running.
if map.get_name() == &MapName::new("ir", "tehran", "parliament") {
opts.dont_block_the_box = false;
opts.allow_block_the_box = true;
}

Sim {
Expand Down
6 changes: 2 additions & 4 deletions widgetry_demo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ use widgetry::{
};

pub fn main() {
// Use this to initialize logging.
abstutil::CmdArgs::new().done();
abstutil::logger::setup();

let settings = Settings::new("widgetry demo");
run(settings);
Expand Down Expand Up @@ -642,8 +641,7 @@ use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen(js_name = "run")]
pub fn run_wasm(root_dom_id: String, assets_base_url: String, assets_are_gzipped: bool) {
// Use this to initialize logging.
abstutil::CmdArgs::new().done();
abstutil::logger::setup();
let settings = Settings::new("widgetry demo")
.root_dom_element_id(root_dom_id)
.assets_base_url(assets_base_url)
Expand Down

0 comments on commit 0464ab4

Please sign in to comment.