def get(): """Logic for GET events. Returns the current episode ID and log content""" req_args = utils.parse_args(_PARSER) close_ep = req_args.get("close_ep", False) if not in_agent_mode(): return responses.bad_request_resp( "Episode data only recorded when in Agent mode") ep_file_path = bb_logging.EP_FILE if not ep_file_path: return responses.bad_request_resp("No episode being recorded") if close_ep: err = utils.sim_proxy().simulation.reset() if err: return responses.internal_err_resp( f"Couldn't reset simulation: {err}") if not ep_file_path.exists(): return responses.internal_err_resp("Could not find episode file") lines = list(line.rstrip("\n") for line in open(ep_file_path)) data = { "cur_ep_id": bb_logging.EP_ID, "cur_ep_file": str(ep_file_path.absolute()), "log": lines, } return responses.ok_resp(data)
def get(): sim_props = utils.sim_proxy().simulation.properties if not isinstance(sim_props, SimProperties): return responses.internal_err_resp( f"Couldn't get the sim properties: {sim_props}") callsigns = utils.sim_proxy().aircraft.callsigns if not isinstance(callsigns, list): return responses.internal_err_resp( f"Couldn't get the callsigns: {callsigns}") data = { "callsigns": [str(x) for x in callsigns], "dt": sim_props.dt, "mode": Settings.SIM_MODE.name, "scenario_name": sim_props.scenario_name, "scenario_time": sim_props.scenario_time, "sector_name": sim_props.sector_name, "seed": sim_props.seed, "sim_type": Settings.SIM_TYPE.name, "speed": sim_props.speed, "state": sim_props.state.name, "utc_datetime": str(sim_props.utc_datetime), } return responses.ok_resp(data)
def get(): """ Logic for GET events. If the request contains an identifier to an existing aircraft, then information about its route (FMS flightplan) is returned """ req_args = utils.parse_args(_PARSER) callsign = req_args[utils.CALLSIGN_LABEL] resp = utils.check_exists(utils.sim_proxy(), callsign) if resp: return resp route_info = utils.sim_proxy().aircraft.route(callsign) if not isinstance(route_info, tuple): if route_info == "Aircraft has no route": return responses.bad_request_resp(route_info) return responses.internal_err_resp(route_info) data = { utils.CALLSIGN_LABEL: str(callsign), "route_name": route_info[0], "next_waypoint": route_info[1], "route_waypoints": route_info[2], } return responses.ok_resp(data)
def get(): """Logic for GET events. Returns properties for the specified aircraft""" req_args = utils.parse_args(_PARSER) callsign = req_args[utils.CALLSIGN_LABEL] sim_props = utils.sim_proxy().simulation.properties if not isinstance(sim_props, SimProperties): return responses.internal_err_resp(sim_props) if callsign: resp = utils.check_exists(utils.sim_proxy(), callsign) if resp: return resp props = utils.sim_proxy().aircraft.properties(callsign) if not isinstance(props, AircraftProperties): return internal_err_resp(props) data = utils.convert_aircraft_props(props) data.update({"scenario_time": sim_props.scenario_time}) return responses.ok_resp(data) # else: get_all_properties props = utils.sim_proxy().aircraft.all_properties if isinstance(props, str): return responses.internal_err_resp( f"Couldn't get the aircraft properties: {props}") if not props: return responses.bad_request_resp("No aircraft in the simulation") data = {} for prop in props.values(): data.update(utils.convert_aircraft_props(prop)) data["scenario_time"] = sim_props.scenario_time return responses.ok_resp(data)
def post(): """Shuts down the BlueBird server""" req_args = utils.parse_args(_PARSER) sim_quit = utils.sim_proxy().shutdown( shutdown_sim=bool(req_args["stop_sim"])) sim_quit_msg = f"(Sim shutdown ok = {sim_quit})" # TODO Check we still get a response before this executes. If not, need to set # this to fire on a timer try: shutdown_fn = request.environ.get("werkzeug.server.shutdown") if not shutdown_fn: return responses.internal_err_resp( f"No shutdown function available. {sim_quit_msg}") shutdown_fn() except Exception as exc: return responses.internal_err_resp( f"Could not shutdown: {exc}. {sim_quit_msg}") data = f"BlueBird shutting down! {sim_quit_msg}" return responses.ok_resp(data)
def get(): """Returns the sector defined in the current simulation""" sector: SectorWrapper = utils.sim_proxy().simulation.sector if not sector: return responses.bad_request_resp("No sector has been set") # TODO (RKM 2019-12-20) Check what exceptions this can throw try: geojson_str = geojson.dumps(sector.element) except Exception as exc: return responses.internal_err_resp(f"Couldn't get sector geojson: {exc}") return responses.ok_resp({"name": sector.name, "content": geojson_str})
def check_exists(sim_proxy: SimProxy, callsign: types.Callsign, negate: bool = False) -> Optional[Response]: """Checks if an aircraft exists, and returns an appropriate response if not""" exists = sim_proxy.aircraft.exists(callsign) if not isinstance(exists, bool): return responses.internal_err_resp( f"Could not check if the aircraft exists: {exists}") if not exists and not negate: return responses.bad_request_resp( f'Aircraft "{callsign}" does not exist') if exists and negate: return responses.bad_request_resp( f'Aircraft "{callsign}" already exists') return None
def post(): """ Logic for POST events. Returns the simulator to a previous state given a logfile :return: """ if Settings.SIM_MODE != SimMode.Agent: return responses.bad_request_resp("Can only be used in agent mode") if Settings.SIM_TYPE != SimType.BlueSky: return responses.bad_request_resp( f"Method not supported for the {Settings.SIM_TYPE} simulator") req_args = parse_args(_PARSER) if bool(req_args["filename"]) == bool(req_args["lines"]): return responses.bad_request_resp( "Either filename or lines must be specified") target_time = req_args["time"] if target_time <= 0: return responses.bad_request_resp( "Target time must be greater than 0") props = sim_proxy().simulation.properties if isinstance(props, str): return responses.internal_err_resp( f"Could not get the sim properties: {props}") prev_dt = props.dt _LOGGER.debug("Starting log reload") # Reset now so the current episode log is closed err = sim_proxy().simulation.reset() if err: return responses.internal_err_resp(f"Simulation not reset: {err}") if req_args["filename"]: log_path = Path(os.getcwd(), req_args["filename"]) if not log_path.exists(): return responses.bad_request_resp( f'Could not find episode file {req_args["filename"]}') with open(req_args["filename"]) as log_file: lines = list(log_file) else: lines = req_args["lines"] _LOGGER.debug("Parsing log content") parsed_scn = parse_lines(lines, target_time) if isinstance(parsed_scn, str): return responses.bad_request_resp( f"Could not parse episode content: {parsed_scn}") # TODO Move regex to outer scope and comment # Assert that the requested time is not past the end of the log last_data = next(x for x in reversed(lines) if re.match(r".*A \[(\d+)\] (.*)$", x)) last_time = int(re.search(r"\[(.*)]", last_data).group(1)) if target_time > last_time: return responses.bad_request_resp( "Error: Target time was greater than the latest time in the log" ) # err = validate_scenario(parsed_scn["lines"]) if err: return responses.bad_request_resp( "Could not create a valid scenario from the given log") # All good - do the reload _LOGGER.debug("Setting the simulator seed") err = sim_proxy().simulation.set_seed(int(parsed_scn["seed"])) if err: return responses.internal_err_resp(f"Could not set seed {err}") scn_name = f"reloads/{str(uuid.uuid4())[:8]}.scn" _LOGGER.debug("Uploading the new scenario") # store_local_scn(scn_name, parsed_scn["lines"]) err = sim_proxy().simulation.upload_new_scenario( scn_name, parsed_scn["lines"]) if err: return responses.internal_err_resp( f"Error uploading scenario: {err}") _LOGGER.info("Starting the new scenario") err = sim_proxy().simulation.load_scenario(scn_name, start_paused=True) if err: return responses.internal_err_resp( f"Could not start scenario after upload {err}") props = sim_proxy().simulation.properties if isinstance(props, str): return responses.internal_err_resp( f"Could not get the sim properties: {props}") diff = target_time - props.scenario_time if diff: # Naive approach - set DTMULT to target, then STEP once... _LOGGER.debug( f"Time difference is {diff}. Stepping to {target_time}") err = sim_proxy().simulation.set_speed(diff) if err: return responses.internal_err_resp( f"Could not change speed: {err}") _LOGGER.debug("Performing step") err = sim_proxy().simulation.step() if err: return responses.internal_err_resp( f"Could not step simulations: {err}") else: _LOGGER.debug("Simulation already at required time") # Reset DTMULT to the previous value err = sim_proxy().simulation.set_speed(prev_dt) if err: return responses.internal_err_resp( "Episode reloaded, but could not reset DTMULT to previous value" ) # TODO Do we want to check before/after positions here and check if the # differences are acceptable? return responses.ok_resp()