Add a /map/set-edits API call

This commit is contained in:
Dustin Carlino 2020-09-21 10:18:02 -07:00
parent a17ef9cc43
commit e1de43b75c
4 changed files with 37 additions and 17 deletions

View File

@ -24,12 +24,14 @@ For now, the API is JSON over HTTP. The exact format is unspecified, error codes
are missing, etc. A summary of the commands available so far:
- **/sim**
- **GET /sim/reset**: Reset all map edits and the simulation state. The trips
that will run don't change; they're determined by the scenario file you
initially pass to `headless`.
- **GET /sim/reset**: Reset all temporary map edits and the simulation state.
The trips that will run don't change; they're determined by the scenario
file you initially pass to `headless`. If you made live map edits using
things like `/traffic-signals/set`, they'll be reset. If you specified
`--edits` or used `/map/set-edits`, these will remain in effect.
- **POST /sim/load**: Switch the scenario being simulated. Takes a
[SimFlags](https://dabreegster.github.io/abstreet/rustdoc/sim/struct.SimFlags.html)
as a JSON POST body.
as a JSON POST body. Resets all map edits.
- **GET /sim/get-time**: Returns the current simulation time.
- **GET /sim/goto-time?t=06:30:00**: Simulate until 6:30 AM. If the time you
specify is before the current time, you have to call **/sim/reset** first.
@ -61,6 +63,10 @@ are missing, etc. A summary of the commands available so far:
this to a file in `data/player/edits/map_name/` and later use it in-game
normally. You can also later run the `headless` server with
`--edits=name_of_edits`.
- **POST /map/set-edits**: The POST body must be
[PermanentMapEdits](https://dabreegster.github.io/abstreet/rustdoc/map_model/struct.PermanentMapEdits.html)
in JSON format. This is the same as the files in `data/player/edits/`. The
edits will remain as you call `/sim/reset`, but get reset with `/sim/load`.
## Working with the map model

View File

@ -1,5 +1,5 @@
// This runs a simulation without any graphics and serves a very basic API to control things. The
// API is not documented yet. To run this:
// This runs a simulation without any graphics and serves a very basic API to control things. See
// https://dabreegster.github.io/abstreet/dev/api.html for documentation. To run this:
//
// > cd headless; cargo run -- --port=1234 ../data/system/scenarios/montlake/weekday.bin
// > curl http://localhost:1234/get-time
@ -30,18 +30,24 @@ 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()));
static ref FLAGS: RwLock<SimFlags> = RwLock::new(SimFlags::for_test("tmp"));
static ref EDITS: RwLock<Option<String>> = RwLock::new(None);
static ref EDITS: RwLock<Option<MapEdits>> = RwLock::new(None);
}
#[tokio::main]
async fn main() {
let mut args = CmdArgs::new();
let mut timer = Timer::new("setup headless");
let sim_flags = SimFlags::from_args(&mut args);
let port = args.required("--port").parse::<u16>().unwrap();
*EDITS.write().unwrap() = args.optional("--edits");
let load_edits = args.optional("--edits");
args.done();
let (mut map, sim, _) = sim_flags.load(&mut Timer::new("setup headless"));
let (mut map, sim, _) = sim_flags.load(&mut timer);
if let Some(path) = load_edits {
let edits = MapEdits::load(&map, path, &mut timer).unwrap();
*EDITS.write().unwrap() = Some(edits);
}
apply_edits(&mut map);
*MAP.write().unwrap() = map;
*SIM.write().unwrap() = sim;
@ -243,7 +249,7 @@ fn handle_command(
})
.collect(),
})),
// Querying the map
// Controlling the map
"/map/get-edits" => {
let mut edits = map.get_edits().clone();
edits.commands.clear();
@ -252,16 +258,21 @@ fn handle_command(
&edits, map,
)))
}
"/map/set-edits" => {
let perma: PermanentMapEdits = abstutil::from_json(body)?;
let edits = PermanentMapEdits::from_permanent(perma, map)?;
*EDITS.write().unwrap() = Some(edits);
apply_edits(map);
Ok(format!("loaded edits"))
}
_ => Err("Unknown command".into()),
}
}
fn apply_edits(map: &mut Map) {
if let Some(name) = EDITS.read().unwrap().as_ref() {
let mut timer = Timer::new(format!("apply edits {}", name));
let edits =
MapEdits::load(map, abstutil::path_edits(map.get_name(), name), &mut timer).unwrap();
map.must_apply_edits(edits, &mut timer);
if let Some(edits) = EDITS.read().unwrap().as_ref() {
let mut timer = Timer::new(format!("apply edits {}", edits.edits_name));
map.must_apply_edits(edits.clone(), &mut timer);
}
}

View File

@ -20,7 +20,7 @@ pub struct Assets {
}
impl Assets {
pub fn new(font_dir: String) -> Assets {
pub fn new() -> Assets {
let mut a = Assets {
default_line_height: RefCell::new(0.0),
text_cache: RefCell::new(LruCache::new(500)),
@ -29,6 +29,9 @@ impl Assets {
font_to_id: HashMap::new(),
text_opts: Options::default(),
};
// TODO These paths are now hardcoded for WASM. This is reasonable, since the fonts
// available are currently fixed anyway. For widgetry to become a library, need to figure
// out how to override these.
a.text_opts.fontdb = fontdb::Database::new();
a.text_opts.fontdb.load_font_data(
include_bytes!("../../data/system/fonts/BungeeInline-Regular.ttf").to_vec(),

View File

@ -216,7 +216,7 @@ pub fn run<G: 'static + GUI, F: FnOnce(&mut EventCtx) -> G>(settings: Settings,
let monitor_scale_factor = prerender_innards.monitor_scale_factor();
let prerender = Prerender {
assets: Assets::new(abstutil::path("system/fonts")),
assets: Assets::new(),
num_uploads: Cell::new(0),
inner: prerender_innards,
scale_factor: RefCell::new(settings.scale_factor.unwrap_or(monitor_scale_factor)),