Exemple #1
0
    def post():
        """Logic for POST events"""

        req_args = utils.parse_args(_PARSER)

        if not utils.sim_proxy().simulation.sector:
            return responses.bad_request_resp(
                "A sector definition is required before uploading a scenario")

        # TODO(rkm 2020-01-12) Should never include the file extension
        name = req_args["name"]
        if not name:
            return responses.bad_request_resp("Scenario name must be provided")

        content = req_args["content"]

        if content:
            err = validate_json_scenario(content)
            if err:
                return responses.bad_request_resp(
                    f"Invalid scenario content: {err}")

        scenario = ScenarioWrapper(name, content)
        err = utils.sim_proxy().simulation.load_scenario(scenario)

        return responses.checked_resp(err, HTTPStatus.CREATED)
    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)
Exemple #3
0
    def post():
        """Upload a new sector definition"""

        req_args = utils.parse_args(_PARSER)

        sector_name = req_args["name"]

        if not sector_name:
            return responses.bad_request_resp("Sector name must be provided")

        sector_json = req_args["content"]

        if sector_json:
            sector_element = validate_geojson_sector(sector_json)
            if not isinstance(sector_element, SectorElement):
                return responses.bad_request_resp(
                    f"Invalid sector content: {sector_element}"
                )
        else:
            sector_element = None

        sector = SectorWrapper(sector_name, sector_element)
        err = utils.sim_proxy().simulation.load_sector(sector)

        return responses.checked_resp(err, HTTPStatus.CREATED)
Exemple #4
0
    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)
Exemple #5
0
    def post():
        """
        Logic for POST events. If the request contains valid aircraft information, then
        a request is sent to the simulator to create it
        """

        req_args = utils.parse_args(_PARSER)
        callsign = req_args[utils.CALLSIGN_LABEL]

        resp = utils.check_exists(utils.sim_proxy(), callsign, negate=True)
        if resp:
            return resp

        position_or_resp = utils.try_parse_lat_lon(req_args)
        if not isinstance(position_or_resp, LatLon):
            return position_or_resp

        if not req_args["type"]:
            return responses.bad_request_resp(
                "Aircraft type must be specified")

        err = utils.sim_proxy().aircraft.create(
            callsign,
            req_args["type"],
            position_or_resp,
            req_args["hdg"],
            req_args["alt"],
            req_args["gspd"],
        )

        return responses.checked_resp(err, HTTPStatus.CREATED)
Exemple #6
0
    def post():
        """Logic for POST events. Sets the seed of the simulator"""

        req_args = utils.parse_args(_PARSER)
        seed: int = req_args["value"]

        if not is_valid_seed(seed):
            return responses.bad_request_resp(
                "Invalid seed specified. Must be a positive integer less than 2^32"
            )

        err = utils.sim_proxy().simulation.set_seed(seed)
        return responses.checked_resp(err)
Exemple #7
0
    def post():
        """Logic for POST events. Sets the speed multiplier for the simulation"""

        req_args = utils.parse_args(_PARSER)
        multiplier = round(req_args["multiplier"], 2)

        if multiplier <= 0:
            return responses.bad_request_resp(
                "Multiplier must be greater than 0")

        # TODO Check if we still need to keep track of step_dt in the client
        err = utils.sim_proxy().simulation.set_speed(multiplier)

        return responses.checked_resp(err)
Exemple #8
0
    def post():
        """
        Logic for POST events. If the request contains an existing aircraft ID, then a
        request is sent to alter its altitude
        """

        req_args = utils.parse_args(_PARSER_POST)
        callsign: Callsign = req_args[utils.CALLSIGN_LABEL]
        fl_cleared: Altitude = req_args["alt"]

        err = utils.sim_proxy().aircraft.set_cleared_fl(
            callsign, fl_cleared, vspd=req_args.get("vspd"))

        return responses.checked_resp(err)
Exemple #9
0
    def post():
        """
        Logic for POST events. If the request contains an existing aircraft ID, then a
        request is sent to alter its ground speed
        """

        req_args = utils.parse_args(_PARSER)

        callsign = req_args[utils.CALLSIGN_LABEL]
        resp = utils.check_exists(utils.sim_proxy(), callsign)
        if resp:
            return resp

        err = utils.sim_proxy().aircraft.set_ground_speed(callsign, req_args["gspd"])

        return checked_resp(err)
Exemple #10
0
    def post():
        """
        Logic for POST events. If the request contains an existing aircraft ID, then a
        request is sent to alter its heading
        """

        req_args = utils.parse_args(_PARSER)

        callsign = req_args[utils.CALLSIGN_LABEL]
        resp = utils.check_exists(utils.sim_proxy(), callsign)
        if resp:
            return resp

        heading = req_args["hdg"]

        err = utils.sim_proxy().aircraft.set_heading(callsign, heading)

        return responses.checked_resp(err)
Exemple #11
0
    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)
Exemple #13
0
    def post():
        """
        Requests that the specified aircraft proceeds immediately to the specified
        waypoint
        """

        req_args = utils.parse_args(_PARSER)
        waypoint_name = req_args["waypoint"]

        if not waypoint_name:
            return responses.bad_request_resp(
                "Waypoint name must be specified")

        callsign = req_args[utils.CALLSIGN_LABEL]

        resp = utils.check_exists(utils.sim_proxy(), callsign)
        if resp:
            return resp

        err = utils.sim_proxy().aircraft.direct_to_waypoint(
            callsign, waypoint_name)

        return responses.checked_resp(err)
Exemple #14
0
    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()
Exemple #15
0
 def get():
     utils.parse_args(parser)