Beispiel #1
0
def test_check_exists():
    """Tests for check_exists"""

    sim_pros_mock = mock.Mock()
    callsign = types.Callsign("FAKE")

    # Test error handling

    sim_pros_mock.aircraft.exists.return_value = "Error"
    with api.FLASK_APP.test_request_context():
        resp = utils.check_exists(sim_pros_mock, callsign)
    assert isinstance(resp, Response)
    assert resp.status_code == HTTPStatus.INTERNAL_SERVER_ERROR
    assert resp.data.decode(
    ) == "Could not check if the aircraft exists: Error"

    # Test missing callsign

    sim_pros_mock.aircraft.exists.return_value = False
    with api.FLASK_APP.test_request_context():
        resp = utils.check_exists(sim_pros_mock, callsign)
    assert isinstance(resp, Response)
    assert resp.status_code == HTTPStatus.BAD_REQUEST
    assert resp.data.decode() == 'Aircraft "FAKE" does not exist'

    callsign = types.Callsign("TEST")

    # Test valid callsign

    sim_pros_mock.aircraft.exists.return_value = True
    with api.FLASK_APP.test_request_context():
        resp = utils.check_exists(sim_pros_mock, callsign)
    assert not resp
Beispiel #2
0
def pairwise_separation_metric(*args, **kwargs):
    """
    The Aviary aircraft separation metric function. Expected *args are:
        callsign1: str,
        callsign2: str
    See: https://github.com/alan-turing-institute/aviary/blob/develop/aviary/metrics/separation_metric.py # noqa: E501
    """

    assert len(args) == 2 and all(isinstance(x, str)
                                  for x in args), "Expected 2 string arguments"

    aircraft_controls: ProxyAircraftControls = kwargs["aircraft_controls"]

    props1 = aircraft_controls.properties(types.Callsign(args[0]))
    if not isinstance(props1, props.AircraftProperties):
        err_resp = f": {props1}" if props1 else ""
        raise ValueError(f"Could not get properties for {args[0]}{err_resp}")

    props2 = aircraft_controls.properties(types.Callsign(args[1]))
    if not isinstance(props2, props.AircraftProperties):
        err_resp = f": {props2}" if props2 else ""
        raise ValueError(f"Could not get properties for {args[1]}{err_resp}")

    return aviary_metrics.pairwise_separation_metric(
        lon1=props1.position.lon_degrees,
        lat1=props1.position.lat_degrees,
        alt1=props1.altitude.meters,
        lon2=props2.position.lon_degrees,
        lat2=props2.position.lat_degrees,
        alt2=props2.altitude.meters,
    )
def test_create(scenario_test_data):
    """Tests that ProxyAircraftControls implements create"""

    mock_aircraft_controls = mock.Mock()
    proxy_aircraft_controls = ProxyAircraftControls(mock_aircraft_controls)

    # Test error when callsign exists

    _, sim_data = scenario_test_data
    test_callsign = list(sim_data)[0]

    proxy_aircraft_controls.set_initial_properties(_TEST_SECTOR_ELEMENT,
                                                   TEST_SCENARIO)

    err = proxy_aircraft_controls.create(test_callsign, None, None, None, None,
                                         None)
    assert err == "Aircraft already exists"

    # Test error from create

    mock_create = mock.Mock(return_value="Sim error (create)")
    mock_aircraft_controls.create = mock_create

    new_callsign = types.Callsign("NEW")
    err = proxy_aircraft_controls.create(new_callsign, None, None, None, None,
                                         None)
    assert err == "Sim error (create)"

    # Test error when checking for sim data for new aircraft

    all_properties_mock = mock.PropertyMock(
        return_value="Sim error (all_properties)")
    type(mock_aircraft_controls).all_properties = all_properties_mock
    mock_create.return_value = None

    err = proxy_aircraft_controls.create(new_callsign, None, None, None, None,
                                         None)
    assert err == "Sim error (all_properties)"

    # Test error when no sim data received for newly created aircraft

    new_callsign = types.Callsign("NEW2")
    all_properties_mock.return_value = sim_data
    err = proxy_aircraft_controls.create(new_callsign, None, None, None, None,
                                         None)
    assert err == "New callsign missing from sim data"

    # Test valid response

    all_properties_mock.return_value = {
        **sim_data,
        new_callsign: sim_data[test_callsign],
    }

    err = proxy_aircraft_controls.create(new_callsign, None, None, None, None,
                                         None)
    assert not err
Beispiel #4
0
def test_convert_aircraft_props():
    """Tests for convert_aircraft_props"""

    ac_props = props.AircraftProperties(
        aircraft_type="A380",
        altitude=types.Altitude(18_500),
        callsign=types.Callsign("TEST"),
        cleared_flight_level=types.Altitude("FL225"),
        ground_speed=types.GroundSpeed(23),
        heading=types.Heading(47),
        initial_flight_level=types.Altitude(18_500),
        position=types.LatLon(43.8, 123.4),
        requested_flight_level=types.Altitude(25_000),
        route_name=None,
        vertical_speed=types.VerticalSpeed(32),
    )

    converted = utils.convert_aircraft_props(ac_props)
    assert isinstance(converted, dict)
    assert len(converted) == 1

    converted_props = converted["TEST"]
    assert len(converted_props) == 9
    assert converted_props["actype"] == "A380"
    assert converted_props["cleared_fl"] == 22_500
    assert converted_props["current_fl"] == 18_500
    assert converted_props["gs"] == 23.0
    assert converted_props["hdg"] == 47
    assert converted_props["lat"] == 43.8
    assert converted_props["lon"] == 123.4
    assert converted_props["requested_fl"] == 25_000
    assert converted_props["vs"] == 32.0
def test_exists(scenario_test_data):
    """Tests that ProxyAircraftControls implements exists"""

    mock_aircraft_controls = mock.Mock()
    proxy_aircraft_controls = ProxyAircraftControls(mock_aircraft_controls)

    # Test error error from callsigns property

    all_properties_mock = mock.PropertyMock(return_value="Sim error")
    type(mock_aircraft_controls).all_properties = all_properties_mock

    err = proxy_aircraft_controls.exists(None)
    assert err == "Sim error"

    # Test False for unknown callsign

    full_data, sim_data = scenario_test_data
    all_properties_mock.return_value = sim_data

    exists = proxy_aircraft_controls.exists(types.Callsign("MISS"))
    assert exists is False

    # Test Tru for known callsign

    proxy_aircraft_controls.set_initial_properties(_TEST_SECTOR_ELEMENT,
                                                   TEST_SCENARIO)

    exists = proxy_aircraft_controls.exists(list(full_data)[0])
    assert exists is True
def test_properties(scenario_test_data):
    """Tests that ProxyAircraftControls implements properties"""

    mock_aircraft_controls = mock.Mock()
    proxy_aircraft_controls = ProxyAircraftControls(mock_aircraft_controls)

    # Test error from all_properties

    all_properties_mock = mock.PropertyMock(return_value="Sim error")
    type(mock_aircraft_controls).all_properties = all_properties_mock

    err = proxy_aircraft_controls.properties(None)
    assert err == "Sim error"

    # Test error for unknown callsign

    proxy_aircraft_controls.set_initial_properties(_TEST_SECTOR_ELEMENT,
                                                   TEST_SCENARIO)

    full_data, sim_data = scenario_test_data
    all_properties_mock.return_value = sim_data

    err = proxy_aircraft_controls.properties(types.Callsign("MISS"))
    assert err == "Unknown callsign MISS"

    # Test valid response

    test_callsign = list(full_data)[0]
    aircraft_props = proxy_aircraft_controls.properties(test_callsign)
    assert aircraft_props == full_data[test_callsign]
Beispiel #7
0
 def _convert_to_ac_props(
     self,
     data: dict,
 ) -> Union[Dict[types.Callsign, props.AircraftProperties], str]:
     ac_props = {}
     try:
         for i in range(len(data["id"])):
             callsign = types.Callsign(data["id"][i])
             ac_props[callsign] = props.AircraftProperties(
                 aircraft_type=data["actype"][i],
                 altitude=types.Altitude(data["alt"][i] / METERS_PER_FOOT),
                 callsign=callsign,
                 cleared_flight_level=None,
                 ground_speed=types.GroundSpeed(int(data["gs"][i])),
                 heading=types.Heading(int(data["trk"][i])),
                 initial_flight_level=None,
                 position=types.LatLon(data["lat"][i], data["lon"][i]),
                 requested_flight_level=None,
                 route_name=None,
                 vertical_speed=types.VerticalSpeed(
                     int(data["vs"][i] * 60 / METERS_PER_FOOT)),
             )
         return ac_props
     except Exception:
         return f"Error parsing ac data from stream: {traceback.format_exc()}"
Beispiel #8
0
    def set_initial_properties(self, sector_element: SectorElement,
                               scenario_content: dict) -> None:
        """
        Set any properties which are not tracked by the simulator - i.e. the flight
        levels, routes, and aircraft types
        """

        for route in sector_element.routes():
            self._routes[route.name] = route

        new_props: Dict[types.Callsign, AircraftProperties] = {}
        for aircraft in scenario_content["aircraft"]:
            callsign = types.Callsign(aircraft["callsign"])
            new_props[callsign] = AircraftProperties.from_data(aircraft)
            if "route" not in aircraft:
                new_props[callsign].route_name = None
                continue
            # Match the route name to the waypoints in the scenario data
            aircraft_route_waypoints = [
                x["fixName"] for x in aircraft["route"]
            ]
            new_props[callsign].route_name = next(
                x for x in self._routes
                if self._routes[x].fix_names() == aircraft_route_waypoints)

        self._ac_props = new_props
        self._data_valid = False
 def callsigns(self) -> Union[List[types.Callsign], str]:
     callsigns = self._mc_client().get_active_callsigns()
     self._raise_for_no_data(callsigns)
     if callsigns is None:
         return []
     if not isinstance(callsigns, list):
         return callsigns
     return [types.Callsign(x) for x in callsigns]
Beispiel #10
0
def test_aircraft_properties_from_data():
    aircraft_data = TEST_SCENARIO["aircraft"][0]
    assert AircraftProperties.from_data(aircraft_data) == AircraftProperties(
        aircraft_type=aircraft_data["type"],
        altitude=types.Altitude(f'FL{aircraft_data["currentFlightLevel"]}'),
        callsign=types.Callsign(aircraft_data["callsign"]),
        cleared_flight_level=types.Altitude(
            f'FL{aircraft_data["clearedFlightLevel"]}'),
        ground_speed=None,
        heading=None,
        initial_flight_level=types.Altitude(
            f'FL{aircraft_data["currentFlightLevel"]}'),
        position=types.LatLon(aircraft_data["startPosition"][1],
                              aircraft_data["startPosition"][0]),
        requested_flight_level=types.Altitude(
            f'FL{aircraft_data["requestedFlightLevel"]}'),
        route_name=None,
        vertical_speed=None,
    )
 def _parse_aircraft_properties(
     ac_props: dict,
 ) -> Union[props.AircraftProperties, str]:
     try:
         # TODO Not currently available: gs, hdg, pos, vs
         return props.AircraftProperties(
             aircraft_type=ac_props["flight-data"]["type"],
             altitude=types.Altitude(f'FL{ac_props["pos"]["afl"]}'),
             callsign=types.Callsign(ac_props["flight-data"]["callsign"]),
             cleared_flight_level=None,
             ground_speed=types.GroundSpeed(ac_props["pos"]["speed"]),
             heading=types.Heading(0),
             initial_flight_level=None,
             position=types.LatLon(ac_props["pos"]["lat"], ac_props["pos"]["long"]),
             requested_flight_level=None,
             route_name=None,
             vertical_speed=types.VerticalSpeed(0),
         )
     except Exception:
         return f"Error parsing AircraftProperties: {traceback.format_exc()}"
Beispiel #12
0
def sector_exit_metric(*args, **kwargs):
    """
    The Aviary sector exit metric function. Expected *args are:
        callsign: Callsign
    See: https://github.com/alan-turing-institute/aviary/blob/develop/aviary/metrics/sector_exit_metric.py # noqa: E501
    """

    assert len(args) == 1 and isinstance(args[0],
                                         str), "Expected 1 string argument"
    callsign = types.Callsign(args[0])

    aircraft_controls: ProxyAircraftControls = kwargs["aircraft_controls"]
    simulator_controls: ProxySimulatorControls = kwargs["simulator_controls"]

    assert simulator_controls.sector, "A sector definition is required"

    current_props = aircraft_controls.properties(callsign)
    assert isinstance(current_props, props.AircraftProperties)

    all_prev_props = aircraft_controls.prev_ac_props()
    prev_props = all_prev_props.get(callsign, None)
    if not all_prev_props or not prev_props:
        # NOTE (RJ 2020-01-30) aircraft_controls.prev_ac_props() is empty before STEP is
        # called for the first time. aviary_metrics.sector_exit_metric() returns None if
        # aircraft is still in the sector
        # NOTE (rkm 2020-01-30) This also covers the case where a new aircraft has been
        # created on this step - i.e. no previous data
        return None

    return aviary_metrics.sector_exit_metric(
        current_props.position.lon_degrees,
        current_props.position.lat_degrees,
        current_props.altitude.meters,
        prev_props.position.lon_degrees,
        prev_props.position.lat_degrees,
        prev_props.altitude.meters,
        prev_props.requested_flight_level,
        simulator_controls.sector.element,
        prev_props.
        route_name,  # TODO(rkm 2020-01-28) Check the required arg type
    )
Beispiel #13
0
def fuel_efficiency_metric(*args, **kwargs):
    """
    The Aviary fuel efficiency meric functions. Expected *args are:
        callsign: Callsign
    See: https://github.com/alan-turing-institute/aviary/blob/develop/aviary/metrics/fuel_efficiency_metric.py # noqa: E501
    """

    assert len(args) == 1 and isinstance(args[0],
                                         str), "Expected 1 string argument"
    callsign = types.Callsign(args[0])

    aircraft_controls: ProxyAircraftControls = kwargs["aircraft_controls"]

    current_props = aircraft_controls.properties(callsign)
    assert isinstance(current_props, props.AircraftProperties)

    return aviary_metrics.fuel_efficiency_metric(
        current_props.altitude.meters,
        current_props.requested_flight_level.meters,
        current_props.initial_flight_level.meters,
    )
Beispiel #14
0
 def from_data(cls, data: Dict[str, Any]) -> "AircraftProperties":
     """
     Create an AircraftProperties from the current sector element and an "aircraft"
     object from the scenario json
     """
     return cls(
         aircraft_type=data["type"],
         altitude=types.Altitude(data["currentFlightLevel"]),
         callsign=types.Callsign(data["callsign"]),
         cleared_flight_level=types.Altitude(data["clearedFlightLevel"]),
         ground_speed=None,
         # TODO(rkm 2020-01-22) Check if we should know the initial heading here
         heading=None,
         position=types.LatLon(data["startPosition"][1],
                               data["startPosition"][0]),
         requested_flight_level=types.Altitude(
             data["requestedFlightLevel"]),
         route_name=None,
         vertical_speed=None,
         initial_flight_level=types.Altitude(data["currentFlightLevel"]),
     )