From 32b3937a0c18361dfcd5a7ff835b7f5832ce71ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Fri, 10 Nov 2023 17:24:59 +0100 Subject: [PATCH 1/2] [mock_uss/scd] Handle retrieval of op intent details when USS is down according to SCD0005+SD0010 --- .../mock_uss/f3548v21/flight_planning.py | 77 +++++++++++++++++-- monitoring/monitorlib/locality.py | 22 ++++++ 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/monitoring/mock_uss/f3548v21/flight_planning.py b/monitoring/mock_uss/f3548v21/flight_planning.py index 6869a4547a..813b77070f 100644 --- a/monitoring/mock_uss/f3548v21/flight_planning.py +++ b/monitoring/mock_uss/f3548v21/flight_planning.py @@ -9,6 +9,7 @@ from monitoring.monitorlib.clients.flight_planning.flight_info import ( FlightInfo, ) +from monitoring.monitorlib.fetch import QueryError from monitoring.uss_qualifier.resources.overrides import apply_overrides from uas_standards.astm.f3548.v21 import api as f3548_v21 from uas_standards.astm.f3548.v21.constants import OiMaxVertices, OiMaxPlanHorizonDays @@ -281,10 +282,12 @@ def op_intent_from_flightrecord( def query_operational_intents( + locality: Locality, area_of_interest: f3548_v21.Volume4D, ) -> List[f3548_v21.OperationalIntent]: """Retrieve a complete set of operational intents in an area, including details. + :param locality: Locality applicable to this query :param area_of_interest: Area where intersecting operational intents must be discovered :return: Full definition for every operational intent discovered """ @@ -314,10 +317,22 @@ def query_operational_intents( updated_op_intents = [] for op_intent_ref in get_details_for: - op_intent, _ = scd_client.get_operational_intent_details( - utm_client, op_intent_ref.uss_base_url, op_intent_ref.id - ) - updated_op_intents.append(op_intent) + try: + op_intent, _ = scd_client.get_operational_intent_details( + utm_client, op_intent_ref.uss_base_url, op_intent_ref.id + ) + updated_op_intents.append(op_intent) + except QueryError as e: + if op_intent_ref.uss_availability == f3548_v21.UssAvailabilityState.Down: + # if the USS does not respond to request for details, and if it marked as down at the DSS, then we don't + # have to fail and can assume specific values for details + op_intent = get_down_uss_op_intent( + locality, area_of_interest, op_intent_ref + ) + updated_op_intents.append(op_intent) + else: + # if the USS is not marked as down we just let the error bubble up + raise e result.extend(updated_op_intents) with db as tx: @@ -327,6 +342,58 @@ def query_operational_intents( return result +def get_down_uss_op_intent( + locality: Locality, + area_of_interest: f3548_v21.Volume4D, + op_intent_ref: f3548_v21.OperationalIntentReference, +) -> f3548_v21.OperationalIntent: + """This function determines the operational intent to be considered in case its managing USS is determined to be + down and does not respond to the requests for details. + + Note: This function will populate volumes (for accepted or activated states) and off_nominal_volumes (for contingent + and non-conforming states) with the area of interest that was requested. The reason is that later on the function + `check_for_disallowed_conflicts` will need to evaluate again those conflicts to determine pre-existing conflicts. + TODO: A better approach to this issue would be to store the area in conflict when a flight is planned with a + conflict, that way we can just retrieve the conflicting area instead of having to compute again the intersection + between the flight to be planned and the conflicting operational intent. + """ + # USS is declared as down and does not answer for details : minimum - 1 + if op_intent_ref == f3548_v21.OperationalIntentState.Accepted: + return f3548_v21.OperationalIntent( + reference=op_intent_ref, + details=f3548_v21.OperationalIntentDetails( + volumes=[area_of_interest], + priority=locality.lowest_bound_priority(), + ), + ) + + elif op_intent_ref == f3548_v21.OperationalIntentState.Activated: + return f3548_v21.OperationalIntent( + reference=op_intent_ref, + details=f3548_v21.OperationalIntentDetails( + volumes=[area_of_interest], + priority=locality.highest_priority(), + ), + ) + + elif ( + op_intent_ref == f3548_v21.OperationalIntentState.Contingent + or op_intent_ref == f3548_v21.OperationalIntentState.Nonconforming + ): + return f3548_v21.OperationalIntent( + reference=op_intent_ref, + details=f3548_v21.OperationalIntentDetails( + off_nominal_volumes=[area_of_interest], + priority=locality.highest_priority(), + ), + ) + + else: + raise ValueError( + f"operational intent {op_intent_ref.id}: invalid state {op_intent_ref.state}" + ) + + def check_op_intent( new_flight: FlightRecord, existing_flight: Optional[FlightRecord], @@ -360,7 +427,7 @@ def check_op_intent( + new_flight.op_intent.details.off_nominal_volumes ) vol4 = v1.bounding_volume.to_f3548v21() - op_intents = query_operational_intents(vol4) + op_intents = query_operational_intents(locality, vol4) # Check for intersections log( diff --git a/monitoring/monitorlib/locality.py b/monitoring/monitorlib/locality.py index 5fcdba720e..44d820c23e 100644 --- a/monitoring/monitorlib/locality.py +++ b/monitoring/monitorlib/locality.py @@ -27,6 +27,16 @@ def allows_same_priority_intersections(self, priority: int) -> bool: """Returns true iff locality allows intersections between two operations at this priority level for ASTM F3548-21""" raise NotImplementedError(Locality._NOT_IMPLEMENTED_MSG) + @abstractmethod + def lowest_bound_priority(self) -> int: + """Returns the lowest bound priority status for ASTM F3548-21, which is a priority level lower than the lowest priority bound defined by the regulator of this locality""" + raise NotImplementedError(Locality._NOT_IMPLEMENTED_MSG) + + @abstractmethod + def highest_priority(self) -> int: + """Returns the highest priority level for ASTM F3548-21 defined by the regulator of this locality""" + raise NotImplementedError(Locality._NOT_IMPLEMENTED_MSG) + def __str__(self): return self.__class__.__name__ @@ -56,6 +66,12 @@ def is_uspace_applicable(self) -> bool: def allows_same_priority_intersections(self, priority: int) -> bool: return False + def lowest_bound_priority(self) -> int: + return -1 + + def highest_priority(self) -> int: + return 100 + class UnitedStatesIndustryCollaboration(Locality): @classmethod @@ -67,3 +83,9 @@ def is_uspace_applicable(self) -> bool: def allows_same_priority_intersections(self, priority: int) -> bool: return False + + def lowest_bound_priority(self) -> int: + return -1 + + def highest_priority(self) -> int: + return 100 From dc4d0ec05d7f2ef628b4859345e071f168b1c836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Tue, 14 Nov 2023 14:42:03 +0100 Subject: [PATCH 2/2] set US locality highest_priority to 0 --- monitoring/monitorlib/locality.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/monitoring/monitorlib/locality.py b/monitoring/monitorlib/locality.py index 44d820c23e..3f656f9df5 100644 --- a/monitoring/monitorlib/locality.py +++ b/monitoring/monitorlib/locality.py @@ -88,4 +88,4 @@ def lowest_bound_priority(self) -> int: return -1 def highest_priority(self) -> int: - return 100 + return 0 # as of the time of writing this, this value has not been subject to a firm decision