From 1c9caad6b7ff0c4ffaf8662452d6ad3f95308e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Tue, 19 Sep 2023 15:55:16 +0200 Subject: [PATCH 1/5] [uss_qualifier/scenarios/utm] Fix #178 --- .../flight_intent_validation.py | 24 +++++---- .../conflict_equal_priority_not_permitted.py | 21 +++++--- .../conflict_higher_priority.py | 27 ++++++++-- .../modify_activated_flight_intent.md | 10 ++-- .../prioritization_test_steps.py | 52 ++++++++++--------- .../scenarios/flight_planning/test_steps.py | 30 +++++++---- 6 files changed, 101 insertions(+), 63 deletions(-) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py index dc018aac97..546f46b56b 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py @@ -1,10 +1,12 @@ from uas_standards.astm.f3548.v21.api import OperationalIntentState from uas_standards.astm.f3548.v21.constants import OiMaxPlanHorizonDays +from uas_standards.interuss.automated_testing.scd.v1.api import ( + InjectFlightResponseResult, +) from monitoring.monitorlib import scd from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( Capability, - InjectFlightResult, ) from monitoring.uss_qualifier.resources.astm.f3548.v21 import DSSInstanceResource from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance @@ -211,8 +213,8 @@ def _attempt_invalid(self): self, "Attempt to plan flight intent too far ahead of time", "Incorrectly planned", - {InjectFlightResult.Rejected}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Rejected}, + {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, self.invalid_too_far_away.request, ) @@ -229,8 +231,8 @@ def _attempt_invalid_offnominal(self): self, "Attempt to plan flight with an off-nominal volume", "Incorrectly planned", - {InjectFlightResult.Rejected}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Rejected}, + {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, self.invalid_accepted_offnominal.request, ) @@ -244,8 +246,8 @@ def _attempt_invalid_offnominal(self): self, "Attempt to modify planned flight with an off-nominal volume", "Incorrectly modified", - {InjectFlightResult.Rejected}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Rejected}, + {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, self.invalid_accepted_offnominal.request, ) @@ -271,8 +273,8 @@ def _attempt_invalid_offnominal(self): self, "Attempt to modify activated flight with an off-nominal volume", "Incorrectly modified", - {InjectFlightResult.Rejected}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Rejected}, + {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, self.invalid_activated_offnominal.request, ) @@ -332,8 +334,8 @@ def _validate_precision_intersection(self): self, "Attempt to plan flight conflicting by a tiny overlap", "Incorrectly planned", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, self.valid_conflict_tiny_overlap.request, ) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py index c108218b86..07067b43b6 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py @@ -1,11 +1,13 @@ from typing import Optional from uas_standards.astm.f3548.v21.api import OperationalIntentState +from uas_standards.interuss.automated_testing.scd.v1.api import ( + InjectFlightResponseResult, +) from monitoring.monitorlib import scd from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( Capability, - InjectFlightResult, ) from monitoring.uss_qualifier.resources.astm.f3548.v21 import DSSInstanceResource from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance @@ -397,13 +399,13 @@ def _modify_activated_flight_preexisting_conflict( self, "Declare flight 2 non-conforming", "Successful transition to non-conforming state", - {InjectFlightResult.Planned, InjectFlightResult.Rejected}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Planned, InjectFlightResponseResult.Rejected}, + {InjectFlightResponseResult.Failed: "Failure"}, self.control_uss, self.flight_2_equal_prio_nonconforming_time_range_A.request, self.flight_2_id, ) - if resp_flight_2.result == InjectFlightResult.Rejected: + if resp_flight_2.result == InjectFlightResponseResult.Rejected: msg = f"{self.control_uss.config.participant_id} rejected transition to a Nonconforming state because it does not support CMSA role, execution of the scenario was stopped without failure" self.record_note("Control USS does not support CMSA role", msg) raise ScenarioCannotContinueError(msg) @@ -421,14 +423,17 @@ def _modify_activated_flight_preexisting_conflict( self, "Attempt to modify activated flight 1 in conflict with activated flight 2", "Successful modification or rejection", - {InjectFlightResult.ReadyToFly, InjectFlightResult.Rejected}, - {InjectFlightResult.Failed: "Failure"}, + { + InjectFlightResponseResult.ReadyToFly, + InjectFlightResponseResult.Rejected, + }, + {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, self.flight_1_activated_time_range_A_extended.request, self.flight_1_id, ) - if resp_flight_1.result == InjectFlightResult.ReadyToFly: + if resp_flight_1.result == InjectFlightResponseResult.ReadyToFly: validate_shared_operational_intent( self, self.tested_uss, @@ -437,7 +442,7 @@ def _modify_activated_flight_preexisting_conflict( self.flight_1_activated_time_range_A_extended.request, resp_flight_1.operational_intent_id, ) - elif resp_flight_1.result == InjectFlightResult.Rejected: + elif resp_flight_1.result == InjectFlightResponseResult.Rejected: validate_shared_operational_intent( self, self.tested_uss, diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py index cba0d78edc..fb0b8e1267 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py @@ -1,6 +1,9 @@ from typing import Optional from uas_standards.astm.f3548.v21.api import OperationalIntentState +from uas_standards.interuss.automated_testing.scd.v1.api import ( + InjectFlightResponseResult, +) from monitoring.monitorlib import scd from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( @@ -371,21 +374,37 @@ def _modify_activated_flight_conflict_preexisting(self) -> str: self.flight_2_id, ) - resp_flight_1 = modify_activated_flight_intent( + resp_flight_1_modif = modify_activated_flight_intent( self, "Modify activated flight 1 in conflict with activated flight 2", self.tested_uss, self.flight_1_activated_time_range_A_extended.request, self.flight_1_id, + preexisting_conflict=True, ) + # The tested USS may respond NotSupported to the modification of the activated flight in conflict. + # If that's the case, it will not impact the rest of the test scenario. + if ( + resp_flight_1_modif.result == InjectFlightResponseResult.NotSupported + or resp_flight_1_modif.operational_intent_id is None + ): + flight_1_op_intent_id = resp_flight_1.operational_intent_id + else: + flight_1_op_intent_id = resp_flight_1_modif.operational_intent_id + + if resp_flight_1_modif.result == InjectFlightResponseResult.NotSupported: + flight_1_op_intent = self.flight_1_activated_time_range_A + else: + flight_1_op_intent = self.flight_1_activated_time_range_A_extended + validate_shared_operational_intent( self, self.tested_uss, self.dss, "Validate flight 1 sharing", - self.flight_1_activated_time_range_A_extended.request, - resp_flight_1.operational_intent_id, + flight_1_op_intent.request, + flight_1_op_intent_id, ) validate_shared_operational_intent( self, @@ -396,7 +415,7 @@ def _modify_activated_flight_conflict_preexisting(self) -> str: resp_flight_2.operational_intent_id, ) - return resp_flight_1.operational_intent_id + return flight_1_op_intent_id def _attempt_modify_activated_flight_conflict(self, flight_1_op_intent_id: str): resp_flight_2 = modify_activated_flight_intent( diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md b/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md index f3d7773c15..3a21d1a21d 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md +++ b/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md @@ -5,10 +5,12 @@ successfully modified by a flight planner. See `modify_activated_flight_intent` ## Successful modification check -All flight intent data provided is correct and valid and free of conflict in space and time, therefore it should have -been modified by the USS per **interuss.automated_testing.flight_planning.ExpectedBehavior**. -If the USS fails to modify the flight, wrongly indicates a conflict, or wrongly indicates the activated state of the -flight, this check will fail. +All flight intent data provided is correct and valid. The (already activated) provided flight intent may be in conflict with +another activated flight, but only if this conflict already existed before the modification was initiated. +Therefore, the USS should have either successfully modified the flight per **interuss.automated_testing.flight_planning.ExpectedBehavior**, +or indicated that the operation is not supported. +If the USS fails to modify the flight (or to indicate that the modification is not supported), wrongly indicates a +conflict, or wrongly indicates the activated state of the flight, this check will fail. ## Failure check diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py b/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py index 5b6d9285a2..548fce9a59 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/prioritization_test_steps.py @@ -1,10 +1,12 @@ from typing import Optional, Tuple from uas_standards.astm.f3548.v21.api import OperationalIntentState +from uas_standards.interuss.automated_testing.scd.v1.api import ( + InjectFlightResponseResult, +) from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( InjectFlightRequest, - InjectFlightResult, InjectFlightResponse, ) from monitoring.uss_qualifier.resources.flight_planning.flight_planner import ( @@ -38,8 +40,8 @@ def plan_priority_conflict_flight_intent( scenario, test_step, "Incorrectly planned", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, )[0] @@ -67,8 +69,8 @@ def modify_planned_priority_conflict_flight_intent( scenario, test_step, "Incorrectly modified", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -97,8 +99,8 @@ def activate_priority_conflict_flight_intent( scenario, test_step, "Incorrectly activated", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -127,8 +129,8 @@ def modify_activated_priority_conflict_flight_intent( scenario, test_step, "Incorrectly modified", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -156,8 +158,8 @@ def plan_conflict_flight_intent( scenario, test_step, "Incorrectly planned", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, )[0] @@ -185,8 +187,8 @@ def modify_planned_conflict_flight_intent( scenario, test_step, "Incorrectly modified", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -215,8 +217,8 @@ def activate_conflict_flight_intent( scenario, test_step, "Incorrectly activated", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -245,8 +247,8 @@ def modify_activated_conflict_flight_intent( scenario, test_step, "Incorrectly modified", - {InjectFlightResult.ConflictWithFlight}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ConflictWithFlight}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -276,8 +278,8 @@ def plan_permitted_conflict_flight_intent( scenario, test_step, "Successful planning", - {InjectFlightResult.Planned}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Planned}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, ) @@ -305,8 +307,8 @@ def modify_planned_permitted_conflict_flight_intent( scenario, test_step, "Successful modification", - {InjectFlightResult.Planned}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Planned}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -335,8 +337,8 @@ def activate_permitted_conflict_flight_intent( scenario, test_step, "Successful activation", - {InjectFlightResult.ReadyToFly}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ReadyToFly}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -365,8 +367,8 @@ def modify_activated_permitted_conflict_flight_intent( scenario, test_step, "Successful modification", - {InjectFlightResult.ReadyToFly}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ReadyToFly}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py b/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py index 9bf85ad654..6c7f2cc58b 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py @@ -3,13 +3,15 @@ from typing import List, Union, Optional, Tuple, Iterable, Set, Dict from uas_standards.astm.f3548.v21.api import OperationalIntentState +from uas_standards.interuss.automated_testing.scd.v1.api import ( + InjectFlightResponseResult, +) from monitoring.monitorlib.fetch import QueryError from monitoring.monitorlib.scd import bounding_vol4 from monitoring.monitorlib.scd_automated_testing.scd_injection_api import ( InjectFlightRequest, Capability, - InjectFlightResult, InjectFlightResponse, DeleteFlightResult, DeleteFlightResponse, @@ -254,8 +256,8 @@ def plan_flight_intent( scenario, test_step, "Successful planning", - {InjectFlightResult.Planned}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Planned}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, ) @@ -283,8 +285,8 @@ def activate_flight_intent( scenario, test_step, "Successful activation", - {InjectFlightResult.ReadyToFly}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.ReadyToFly}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -313,8 +315,8 @@ def modify_planned_flight_intent( scenario, test_step, "Successful modification", - {InjectFlightResult.Planned}, - {InjectFlightResult.Failed: "Failure"}, + {InjectFlightResponseResult.Planned}, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -327,8 +329,11 @@ def modify_activated_flight_intent( flight_planner: FlightPlanner, flight_intent: InjectFlightRequest, flight_id: str, + preexisting_conflict: bool = False, ) -> InjectFlightResponse: """Modify an activated flight intent that should result in success. + If the activated flight intent to modify has a pre-existing conflict, the USS is allowed to return `NotSupported`. + Use `preexisting_conflict=True` in this case. This function implements the test step described in modify_activated_flight_intent.md. @@ -339,12 +344,15 @@ def modify_activated_flight_intent( flight_intent, OperationalIntentState.Activated, scenario, test_step ) + expected_results = {InjectFlightResponseResult.ReadyToFly} + if preexisting_conflict: + expected_results.add(InjectFlightResponseResult.NotSupported) return submit_flight_intent( scenario, test_step, "Successful modification", - {InjectFlightResult.ReadyToFly}, - {InjectFlightResult.Failed: "Failure"}, + expected_results, + {InjectFlightResponseResult.Failed: "Failure"}, flight_planner, flight_intent, flight_id, @@ -355,8 +363,8 @@ def submit_flight_intent( scenario: TestScenarioType, test_step: str, success_check: str, - expected_results: Set[InjectFlightResult], - failed_checks: Dict[InjectFlightResult, str], + expected_results: Set[InjectFlightResponseResult], + failed_checks: Dict[InjectFlightResponseResult, str], flight_planner: FlightPlanner, flight_intent: InjectFlightRequest, flight_id: Optional[str] = None, From 71c354441ce72cbb6fa9789f7cb1d3ffdd60603b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Fri, 6 Oct 2023 11:09:57 +0200 Subject: [PATCH 2/5] add req for op intent coverage; update according to feedback; optional severity --- .../automated_testing/flight_planning.md | 5 ++ .../flight_intent_validation.py | 6 +-- .../conflict_higher_priority.md | 27 ++++++++-- .../conflict_higher_priority.py | 8 ++- .../scenarios/astm/utm/test_steps.py | 35 ++++++++++--- .../utm/validate_shared_operational_intent.md | 6 +++ .../modify_activated_flight_intent.md | 40 ++++++++++++--- .../scenarios/flight_planning/test_steps.py | 51 ++++++++++++++----- .../uss_qualifier/suites/astm/utm/f3548_21.md | 7 ++- .../suites/faa/uft/message_signing.md | 7 ++- .../suites/uspace/flight_auth.md | 7 ++- .../suites/uspace/required_services.md | 7 ++- 12 files changed, 159 insertions(+), 47 deletions(-) diff --git a/monitoring/uss_qualifier/requirements/interuss/automated_testing/flight_planning.md b/monitoring/uss_qualifier/requirements/interuss/automated_testing/flight_planning.md index 7e4f6ae599..63c7169b31 100644 --- a/monitoring/uss_qualifier/requirements/interuss/automated_testing/flight_planning.md +++ b/monitoring/uss_qualifier/requirements/interuss/automated_testing/flight_planning.md @@ -12,4 +12,9 @@ TODO: Describe requirements ### ExpectedBehavior +### FlightCoveredByOperationalIntent +For InterUSS to effectively test the requirements of ASTM F3548-21, a USS under test must act as if there is a +regulatory requirement requiring all flights it manages to provide operational intents according to ASTM F3548-21 at all +times for all flights it manages. + ### DeleteFlightSuccess diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py index 59eec2e0ad..226cb91a99 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py @@ -2,9 +2,6 @@ from monitoring.uss_qualifier.common_data_definitions import Severity from uas_standards.astm.f3548.v21.api import OperationalIntentState from uas_standards.astm.f3548.v21.constants import OiMaxPlanHorizonDays -from uas_standards.interuss.automated_testing.scd.v1.api import ( - InjectFlightResponseResult, -) from monitoring.uss_qualifier.resources.astm.f3548.v21 import DSSInstanceResource from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance @@ -32,6 +29,9 @@ submit_flight_intent, delete_flight_intent, ) +from uas_standards.interuss.automated_testing.scd.v1.api import ( + InjectFlightResponseResult, +) class FlightIntentValidation(TestScenario): diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md index 4c446e4049..15e012b15f 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md @@ -174,13 +174,30 @@ The test driver activates flight 2, which should be done successfully given that ### [Validate flight 2 sharing test step](../../validate_shared_operational_intent.md) ### [Modify activated flight 1 in conflict with activated flight 2 test step](../../../../flight_planning/modify_activated_flight_intent.md) -Before execution of this step, flights 1 and 2 are activated and in conflict. -The test driver modifies flight 1 in a way that still conflicts with flight 2. -Even though flight 2 is the highest-priority flight, because the conflict existed before the modification was initiated, -the modification is accepted per **[astm.f3548.v21.SCD0030](../../../../../requirements/astm/f3548/v21.md)**. +Before execution of this step, flights 1 and 2 are activated and in conflict. Flight 2 is the highest-priority flight. +The test driver attempts to modify flight 1 in a way that still conflicts with flight 2. + +The successful outcomes of the modification attempts: +1. Even though flight 2 is the highest-priority flight, because the conflict existed before the modification was + initiated, an accepted modification is considered a success per **[astm.f3548.v21.SCD0030](../../../../../requirements/astm/f3548/v21.md)**. +2. Due to the conflict, the USS may decide to be more conservative and to not support the modification. This is + considered a success as there is no positive requirement for the USS to accept the modification. + +A rejected modification will indicate a low severity failure. Indeed, in some situations a rejection may not be strictly +speaking a failure to meet a requirement. This could be the case for example if the USS does not support directly update +of intents and instead delete the previous one and create a new one. Since we cannot distinguish between an actual +failure to meet the requirement and a reasonable behavior due to implementation limitations, we indicate a low severity +failure which won't actually fail the test. + +In any case, whatever is the outcome of this step, there should not be any impact on the rest of the execution of the +scenario. An intent should exist (this is checked in the next step) and it should be either the previous or the modified +intent, both of which make no difference in the next steps. ### [Validate flight 1 sharing test step](../../validate_shared_operational_intent.md) -The first flight should have been modified. +If the modification was accepted, flight 1 should have been modified. +If the modification was not supported, flight 1 should not have been modified. +If the modification was rejected, flight 1 should not have been modified and should still exist. If it does not exist, +it means that there is an active flight without an operational intent, which is a failure to meet **[interuss.automated_testing.flight_planning.FlightCoveredByOperationalIntent](../../../../../requirements/interuss/automated_testing/flight_planning.md)**. ## Attempt to modify activated flight in conflict test case diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py index 733d4a993a..165694c73a 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py @@ -451,15 +451,13 @@ def _modify_activated_flight_conflict_preexisting( preexisting_conflict=True, ) - # The tested USS may respond NotSupported to the modification of the activated flight in conflict. - # If that's the case, it will not impact the rest of the test scenario. - if resp.result == InjectFlightResponseResult.NotSupported: + if resp.result == InjectFlightResponseResult.ReadyToFly: flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A.request + self.flight_1_activated_time_range_A_extended.request ) else: flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A_extended.request + self.flight_1_activated_time_range_A.request ) return flight_1_oi_ref, flight_2_oi_ref diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py b/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py index ca7f590eed..46bf3e9c60 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/test_steps.py @@ -168,17 +168,36 @@ def expect_shared( oi_ref = self._new_oi_ref elif self._new_oi_ref is None: - # we expect the original op intent to have been either modified or left untouched, thus must be among the returned op intents - # exception made if skip_if_not_found=True and op intent was deleted: step is skipped + # We expect the original op intent to have been either modified or left untouched, thus must be among + # the returned op intents. If additionally the op intent corresponds to an active flight, we fail a + # different appropriate check. Exception made if skip_if_not_found=True and op intent was deleted: step + # is skipped. modified_oi_ref = self._find_after_oi(self._orig_oi_ref.id) if modified_oi_ref is None: if not skip_if_not_found: - check.record_failed( - summary="Operational intent reference not found in DSS", - severity=Severity.High, - details=f"USS {self._flight_planner.participant_id} was supposed to have shared with the DSS an updated operational intent by modifying it, but no matching operational intent references were found in the DSS in the area of the flight intent", - query_timestamps=[self._after_query.request.timestamp], - ) + if ( + flight_intent.operational_intent.state + == OperationalIntentState.Activated + ): + with self._scenario.check( + "Operational intent for active flight not deleted", + [self._flight_planner.participant_id], + ) as active_flight_check: + active_flight_check.record_failed( + summary="Operational intent reference for active flight not found in DSS", + severity=Severity.High, + details=f"USS {self._flight_planner.participant_id} was supposed to have shared with the DSS an updated operational intent by modifying it, but no matching operational intent references were found in the DSS in the area of the flight intent", + query_timestamps=[ + self._after_query.request.timestamp + ], + ) + else: + check.record_failed( + summary="Operational intent reference not found in DSS", + severity=Severity.High, + details=f"USS {self._flight_planner.participant_id} was supposed to have shared with the DSS an updated operational intent by modifying it, but no matching operational intent references were found in the DSS in the area of the flight intent", + query_timestamps=[self._after_query.request.timestamp], + ) else: self._scenario.record_note( self._flight_planner.participant_id, diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/validate_shared_operational_intent.md b/monitoring/uss_qualifier/scenarios/astm/utm/validate_shared_operational_intent.md index 35430975b7..b6fac6fae6 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/validate_shared_operational_intent.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/validate_shared_operational_intent.md @@ -10,6 +10,12 @@ This step verifies that a created flight is shared properly per ASTM F3548-21 by If a reference to the operational intent for the flight is not found in the DSS, this check will fail per **astm.f3548.v21.USS0005** and **astm.f3548.v21.OPIN0025**. +## Operational intent for active flight not deleted check + +If an activated operational intent is expected to exist after it has been modified or activated and that it is not found +in the DSS, this means that there is an active flight without a corresponding operational intent, then this check will +fail per **[interuss.automated_testing.flight_planning.FlightCoveredByOperationalIntent](../../../requirements/interuss/automated_testing/flight_planning.md)**. + ## Operational intent details retrievable check If the operational intent details for the flight cannot be retrieved from the USS, this check will fail per **astm.f3548.v21.USS0105** and **astm.f3548.v21.OPIN0025**. diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md b/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md index 3a21d1a21d..9b7db65ab2 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md +++ b/monitoring/uss_qualifier/scenarios/flight_planning/modify_activated_flight_intent.md @@ -1,16 +1,40 @@ # Modify activated flight test step -This page describes the content of a common test case where a valid user flight intent in activated state should be -successfully modified by a flight planner. See `modify_activated_flight_intent` in [test_steps.py](test_steps.py). +This page describes the content of a common test case where a valid user flight intent in activated state is tentatively +modified by a flight planned. Multiple outcomes may be valid. +See `modify_activated_flight_intent` in [test_steps.py](test_steps.py). ## Successful modification check -All flight intent data provided is correct and valid. The (already activated) provided flight intent may be in conflict with -another activated flight, but only if this conflict already existed before the modification was initiated. -Therefore, the USS should have either successfully modified the flight per **interuss.automated_testing.flight_planning.ExpectedBehavior**, -or indicated that the operation is not supported. -If the USS fails to modify the flight (or to indicate that the modification is not supported), wrongly indicates a -conflict, or wrongly indicates the activated state of the flight, this check will fail. +All flight intent data provided is correct and valid. The (already activated) provided flight intent may be in conflict +with another activated flight, but only if this conflict already existed before the modification was initiated. + +If the provided flight intent is not in conflict with another intent the USS should have successfully modified the +flight per **[astm.f3548.v21.SCD0030](../../requirements/astm/f3548/v21.md)**. +If the USS fails to modify the flight, wrongly indicates a conflict, or wrongly indicates the activated state of the +flight, this check will fail. + +If the provided flight intent is in conflict with another intent and that a pre-existing conflict was present, the USS +may have decided to be more conservative and to not support modification. +In such case, the USS may indicate that the operation is not supported instead of modifying the flight per **[astm.f3548.v21.SCD0030](../../requirements/astm/f3548/v21.md)**. +If the USS fails to modify the flight, or fails to indicate that the modification is not supported, or wrongly indicates +the activated state of the flight, this check will fail. + +Do take note that if the USS rejects the modification when a pre-existing conflict was present, this check will not fail, +but the following *Rejected modification check* will. Refer to this check for more information. + +## Rejected modification check + +If the provided flight intent is in conflict with another intent and that a pre-existing conflict was present, the USS +may have rejected the modification instead of modifying it or indicating that the modification is not supported. This +could be the case for example if the USS does not support directly update of intents and instead delete the previous one +and create a new one. This may or may not be strictly speaking a failure to meet a requirement, but we cannot +distinguish between an actual failure to meet the requirement and a reasonable behavior due to implementation +limitations. + +As such, if the pre-existing conflict was present, and that the USS rejected the modification, this check will fail with +a low severity per **[astm.f3548.v21.SCD0030](../../requirements/astm/f3548/v21.md)**. This won't actually fail the test +but will serve as a warning. ## Failure check diff --git a/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py b/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py index 40804df96a..502a7f2eff 100644 --- a/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py +++ b/monitoring/uss_qualifier/scenarios/flight_planning/test_steps.py @@ -1,11 +1,8 @@ import inspect -from typing import List, Optional, Tuple, Iterable, Set, Dict +from typing import List, Optional, Tuple, Iterable, Set, Dict, Union from monitoring.monitorlib.geotemporal import Volume4DCollection from uas_standards.astm.f3548.v21.api import OperationalIntentState -from uas_standards.interuss.automated_testing.scd.v1.api import ( - InjectFlightResponseResult, -) from monitoring.monitorlib.fetch import QueryError from uas_standards.interuss.automated_testing.scd.v1.api import ( @@ -187,9 +184,8 @@ def modify_activated_flight_intent( flight_id: str, preexisting_conflict: bool = False, ) -> InjectFlightResponse: - """Modify an activated flight intent that should result in success. - If the activated flight intent to modify has a pre-existing conflict, the USS is allowed to return `NotSupported`. - Use `preexisting_conflict=True` in this case. + """Attempt to modify an activated flight intent. + If present, a pre-existing conflict must be indicated with `preexisting_conflict=True`. This function implements the test step described in modify_activated_flight_intent.md. @@ -200,15 +196,35 @@ def modify_activated_flight_intent( flight_intent, OperationalIntentState.Activated, scenario, test_step ) - expected_results = {InjectFlightResponseResult.ReadyToFly} if preexisting_conflict: - expected_results.add(InjectFlightResponseResult.NotSupported) + expected_results = { + InjectFlightResponseResult.ReadyToFly, + InjectFlightResponseResult.NotSupported, + # the following two results are considered expected in order to fail another check as low severity + InjectFlightResponseResult.Rejected, + InjectFlightResponseResult.ConflictWithFlight, + } + failed_checks = { + InjectFlightResponseResult.Failed: "Failure", + InjectFlightResponseResult.Rejected: ( + "Rejected modification", + Severity.Low, + ), + InjectFlightResponseResult.ConflictWithFlight: ( + "Rejected modification", + Severity.Low, + ), + } + else: + expected_results = {InjectFlightResponseResult.ReadyToFly} + failed_checks = {InjectFlightResponseResult.Failed: "Failure"} + return submit_flight_intent( scenario, test_step, "Successful modification", expected_results, - {InjectFlightResponseResult.Failed: "Failure"}, + failed_checks, flight_planner, flight_intent, flight_id, @@ -220,13 +236,14 @@ def submit_flight_intent( test_step: str, success_check: str, expected_results: Set[InjectFlightResponseResult], - failed_checks: Dict[InjectFlightResponseResult, str], + failed_checks: Dict[InjectFlightResponseResult, Union[str, Tuple[str, Severity]]], flight_planner: FlightPlanner, flight_intent: InjectFlightRequest, flight_id: Optional[str] = None, ) -> Tuple[InjectFlightResponse, Optional[str]]: """Submit a flight intent with an expected result. - A check fail is considered of high severity and as such will raise an ScenarioCannotContinueError. + A check fail is considered by default of high severity and as such will raise an ScenarioCannotContinueError. + The severity of each failed check may be overridden if needed. This function does not directly implement a test step. @@ -253,13 +270,19 @@ def submit_flight_intent( notes_suffix = f': "{resp.notes}"' if "notes" in resp and resp.notes else "" for unexpected_result, failed_test_check in failed_checks.items(): + if isinstance(failed_test_check, str): + check_name = failed_test_check + check_severity = Severity.High + else: + check_name, check_severity = failed_test_check + with scenario.check( - failed_test_check, [flight_planner.participant_id] + check_name, [flight_planner.participant_id] ) as specific_failed_check: if resp.result == unexpected_result: specific_failed_check.record_failed( summary=f"Flight unexpectedly {resp.result}", - severity=Severity.High, + severity=check_severity, details=f'{flight_planner.participant_id} indicated {resp.result} rather than the expected {" or ".join(expected_results)}{notes_suffix}', query_timestamps=[query.request.timestamp], ) diff --git a/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md b/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md index c3f4c1c418..9b83332c44 100644 --- a/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md +++ b/monitoring/uss_qualifier/suites/astm/utm/f3548_21.md @@ -112,7 +112,7 @@ Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents - interuss
.automated_testing
.flight_planning
+ interuss
.automated_testing
.flight_planning
ClearArea Implemented Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents @@ -127,4 +127,9 @@ Implemented Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + + FlightCoveredByOperationalIntent + Implemented + Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + diff --git a/monitoring/uss_qualifier/suites/faa/uft/message_signing.md b/monitoring/uss_qualifier/suites/faa/uft/message_signing.md index 7efaf4f0f9..050dde31a4 100644 --- a/monitoring/uss_qualifier/suites/faa/uft/message_signing.md +++ b/monitoring/uss_qualifier/suites/faa/uft/message_signing.md @@ -109,7 +109,7 @@ Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents - interuss
.automated_testing
.flight_planning
+ interuss
.automated_testing
.flight_planning
ClearArea Implemented Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents @@ -124,4 +124,9 @@ Implemented Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + + FlightCoveredByOperationalIntent + Implemented + Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + diff --git a/monitoring/uss_qualifier/suites/uspace/flight_auth.md b/monitoring/uss_qualifier/suites/uspace/flight_auth.md index 93884674fc..f7dd88ba0a 100644 --- a/monitoring/uss_qualifier/suites/uspace/flight_auth.md +++ b/monitoring/uss_qualifier/suites/uspace/flight_auth.md @@ -109,7 +109,7 @@ Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents - interuss
.automated_testing
.flight_planning
+ interuss
.automated_testing
.flight_planning
ClearArea Implemented Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents @@ -124,4 +124,9 @@ Implemented Flight authorisation validation
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + + FlightCoveredByOperationalIntent + Implemented + Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + diff --git a/monitoring/uss_qualifier/suites/uspace/required_services.md b/monitoring/uss_qualifier/suites/uspace/required_services.md index ac5f5092ea..a690bb7ab8 100644 --- a/monitoring/uss_qualifier/suites/uspace/required_services.md +++ b/monitoring/uss_qualifier/suites/uspace/required_services.md @@ -479,7 +479,7 @@ Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents - interuss
.automated_testing
.flight_planning
+ interuss
.automated_testing
.flight_planning
ClearArea Implemented Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents @@ -494,6 +494,11 @@ Implemented Flight authorisation validation
Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + + FlightCoveredByOperationalIntent + Implemented + Nominal planning: conflict with higher priority
Nominal planning: not permitted conflict with equal priority
Validation of operational intents + interuss
.automated_testing
.rid
.injection
DeleteTestSuccess From 56c1e14557787e73c3def5fe8aa1d9443c7c617a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Tue, 10 Oct 2023 11:50:49 +0200 Subject: [PATCH 3/5] [uss_qualifier/utm] Use active volumes for activated op intents; [mock_uss/scdsc] Enforce active volume for activated op intent; Fix #217 --- monitoring/mock_uss/scdsc/routes_injection.py | 21 ++ monitoring/monitorlib/geotemporal.py | 7 + .../flight_intent_validation.md | 5 + .../flight_intent_validation.py | 29 ++- .../conflict_equal_priority_not_permitted.md | 23 ++- .../conflict_equal_priority_not_permitted.py | 188 +++++++++--------- .../conflict_higher_priority.md | 23 ++- .../conflict_higher_priority.py | 183 +++++++++-------- .../flight_intents/conflicting_flights.json | 8 +- .../flight_intents/invalid_flight_auths.json | 8 +- .../invalid_flight_intents.yaml | 6 +- .../flight_intents/priority_preemption.json | 91 ++++----- .../flight_intents/conflicting_flights.yaml | 15 +- .../invalid_flight_intents.yaml | 10 +- .../flight_intents/priority_preemption.yaml | 88 ++++---- 15 files changed, 389 insertions(+), 316 deletions(-) diff --git a/monitoring/mock_uss/scdsc/routes_injection.py b/monitoring/mock_uss/scdsc/routes_injection.py index 53058bda70..2fd4af59e0 100644 --- a/monitoring/mock_uss/scdsc/routes_injection.py +++ b/monitoring/mock_uss/scdsc/routes_injection.py @@ -5,6 +5,7 @@ from typing import List, Tuple import uuid +import arrow import flask from loguru import logger import requests.exceptions @@ -245,6 +246,26 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict, 200, ) + # Validate intent is currently active if in Activated state + # I.e. at least one volume has start time in the past and end time in the future + now = arrow.utcnow() + if req_body.operational_intent.state == OperationalIntentState.Activated: + active_volume = False + for vol in ( + req_body.operational_intent.volumes + + req_body.operational_intent.off_nominal_volumes + ): + if vol.time_start.value.datetime <= now <= vol.time_end.value.datetime: + active_volume = True + if not active_volume: + return ( + InjectFlightResponse( + result=InjectFlightResponseResult.Rejected, + notes=f"Operational intent is activated but has no volume currently active (now: {now})", + ), + 200, + ) + # Check if this is an existing flight being modified deadline = datetime.utcnow() + DEADLOCK_TIMEOUT while True: diff --git a/monitoring/monitorlib/geotemporal.py b/monitoring/monitorlib/geotemporal.py index 3d38b90823..d8b810157e 100644 --- a/monitoring/monitorlib/geotemporal.py +++ b/monitoring/monitorlib/geotemporal.py @@ -547,3 +547,10 @@ def from_interuss_scd_api( def to_f3548v21(self) -> List[f3548v21.Volume4D]: return [v.to_f3548v21() for v in self.volumes] + + def has_active_volume(self, time_ref: datetime) -> bool: + active_volume = False + for vol in self.volumes: + if vol.time_start.datetime <= time_ref <= vol.time_end.datetime: + active_volume = True + return active_volume diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md index 9d572c3d46..58f4b39760 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.md @@ -19,6 +19,11 @@ FlightIntentsResource that provides the following flight intents: - `invalid_too_far_away`: reference time mutation: reference time pulled back so that it is like the operational intent is attempted to be planned more than OiMaxPlanHorizon = 30 days ahead of time - `valid_conflict_tiny_overlap`: volumes mutation: has a volume that overlaps with `valid_op_intent` just above IntersectionMinimumPrecision = 1cm in a way that must result as a conflict +Because the scenario involves activation of intents, all activated intents must be active during the execution of the +test scenario, i.e. they must start before the reference time plus planning time duration. Additionally, their end time +must leave sufficient time for the execution of the test scenario. For the sake of simplicity, it is recommended to set +the start and end times of all the intents to the same range. + ### tested_uss FlightPlannerResource that will be tested for its validation of operational intents. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py index 226cb91a99..58ba15b117 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py @@ -1,3 +1,5 @@ +import arrow + from monitoring.monitorlib.geotemporal import Volume4DCollection from monitoring.uss_qualifier.common_data_definitions import Severity from uas_standards.astm.f3548.v21.api import OperationalIntentState @@ -59,10 +61,10 @@ def __init__( self.tested_uss = tested_uss.flight_planner self.dss = dss.dss - flight_intents = flight_intents.get_flight_intents() + _flight_intents = flight_intents.get_flight_intents() extents = [] - for intent in flight_intents.values(): + for intent in _flight_intents.values(): extents.extend(intent.request.operational_intent.volumes) extents.extend(intent.request.operational_intent.off_nominal_volumes) self._intents_extent = Volume4DCollection.from_interuss_scd_api( @@ -78,14 +80,25 @@ def __init__( self.invalid_activated_offnominal, self.valid_conflict_tiny_overlap, ) = ( - flight_intents["valid_flight"], - flight_intents["valid_activated"], - flight_intents["invalid_too_far_away"], - flight_intents["invalid_accepted_offnominal"], - flight_intents["invalid_activated_offnominal"], - flight_intents["valid_conflict_tiny_overlap"], + _flight_intents["valid_flight"], + _flight_intents["valid_activated"], + _flight_intents["invalid_too_far_away"], + _flight_intents["invalid_accepted_offnominal"], + _flight_intents["invalid_activated_offnominal"], + _flight_intents["valid_conflict_tiny_overlap"], ) + now = arrow.utcnow() + for intent_name, intent in _flight_intents.items(): + if ( + intent.request.operational_intent.state + == OperationalIntentState.Activated + ): + assert Volume4DCollection.from_interuss_scd_api( + intent.request.operational_intent.volumes + + intent.request.operational_intent.off_nominal_volumes + ), f"at least one volume of activated intent {intent_name} must be active now (now is {now})" + assert ( self.valid_flight.request.operational_intent.state == OperationalIntentState.Accepted diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md index 45b861167b..323af0aab3 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.md @@ -21,7 +21,7 @@ Otherwise, the FlightIntentsResource must provide the following flight intents: - + < @@ -29,7 +29,7 @@ Otherwise, the FlightIntentsResource must provide the following flight intents: - + @@ -37,44 +37,49 @@ Otherwise, the FlightIntentsResource must provide the following flight intents: - + - + - + - + - + - + - +
Flight intent IDFlight intent IDFlight name Priority StateMust not conflict with
flight_1_planned_time_range_Aflight_1_planned_vol_A Flight 1 Any (but all the same) AcceptedN/A
flight_1_activated_time_range_Aflight_1_activated_vol_A Activated
flight_1_activated_time_range_A_extendedflight_1_activated_vol_A_extended Flight 1m Activated Flight 2 N/A
flight_1_planned_time_range_Bflight_1_planned_vol_B Flight 1c Planned N/A Flight 2
flight_1_activated_time_range_Bflight_1_activated_vol_B Activated
flight_2_equal_prio_planned_time_range_Bflight_2_equal_prio_planned_vol_B Flight 2 Planned Flight 1, Flight 1m Flight 1c
flight_2_equal_prio_activated_time_range_Bflight_2_equal_prio_activated_vol_B Activated
flight_2_equal_prio_nonconforming_time_range_Aflight_2_equal_prio_nonconforming_vol_A Nonconforming
+Because the scenario involves activation of intents, all activated intents must be active during the execution of the +test scenario, i.e. they must start before the reference time plus planning time duration. Additionally, their end time +must leave sufficient time for the execution of the test scenario. For the sake of simplicity, it is recommended to set +the start and end times of all the intents to the same range. + ### tested_uss FlightPlannerResource that is under test and will manage flight 1. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py index 938f66ec90..7c0e88f331 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py @@ -1,5 +1,6 @@ from typing import Optional +import arrow from uas_standards.astm.f3548.v21.api import ( OperationalIntentReference, ) @@ -48,16 +49,16 @@ class ConflictEqualPriorityNotPermitted(TestScenario): flight_1_id: Optional[str] = None - flight_1_planned_time_range_A: FlightIntent - flight_1_activated_time_range_A: FlightIntent - flight_1_activated_time_range_A_extended: FlightIntent - flight_1_planned_time_range_B: FlightIntent - flight_1_activated_time_range_B: FlightIntent + flight_1_planned_vol_A: FlightIntent + flight_1_activated_vol_A: FlightIntent + flight_1_activated_vol_A_extended: FlightIntent + flight_1_planned_vol_B: FlightIntent + flight_1_activated_vol_B: FlightIntent flight_2_id: Optional[str] = None - flight_2_equal_prio_planned_time_range_B: FlightIntent - flight_2_equal_prio_activated_time_range_B: FlightIntent - flight_2_equal_prio_nonconforming_time_range_A: FlightIntent + flight_2_equal_prio_planned_vol_B: FlightIntent + flight_2_equal_prio_activated_vol_B: FlightIntent + flight_2_equal_prio_nonconforming_vol_A: FlightIntent tested_uss: FlightPlanner control_uss: FlightPlanner @@ -83,10 +84,10 @@ def __init__( ) raise ScenarioCannotContinueError(msg) - flight_intents = flight_intents.get_flight_intents() + _flight_intents = flight_intents.get_flight_intents() extents = [] - for intent in flight_intents.values(): + for intent in _flight_intents.values(): extents.extend(intent.request.operational_intent.volumes) extents.extend(intent.request.operational_intent.off_nominal_volumes) self._intents_extent = Volume4DCollection.from_interuss_scd_api( @@ -95,97 +96,108 @@ def __init__( try: ( - self.flight_1_planned_time_range_A, - self.flight_1_activated_time_range_A, - self.flight_1_activated_time_range_A_extended, - self.flight_1_planned_time_range_B, - self.flight_1_activated_time_range_B, - self.flight_2_equal_prio_planned_time_range_B, - self.flight_2_equal_prio_activated_time_range_B, - self.flight_2_equal_prio_nonconforming_time_range_A, + self.flight_1_planned_vol_A, + self.flight_1_activated_vol_A, + self.flight_1_activated_vol_A_extended, + self.flight_1_planned_vol_B, + self.flight_1_activated_vol_B, + self.flight_2_equal_prio_planned_vol_B, + self.flight_2_equal_prio_activated_vol_B, + self.flight_2_equal_prio_nonconforming_vol_A, ) = ( - flight_intents["flight_1_planned_time_range_A"], - flight_intents["flight_1_activated_time_range_A"], - flight_intents["flight_1_activated_time_range_A_extended"], - flight_intents["flight_1_planned_time_range_B"], - flight_intents["flight_1_activated_time_range_B"], - flight_intents["flight_2_equal_prio_planned_time_range_B"], - flight_intents["flight_2_equal_prio_activated_time_range_B"], - flight_intents["flight_2_equal_prio_nonconforming_time_range_A"], + _flight_intents["flight_1_planned_vol_A"], + _flight_intents["flight_1_activated_vol_A"], + _flight_intents["flight_1_activated_vol_A_extended"], + _flight_intents["flight_1_planned_vol_B"], + _flight_intents["flight_1_activated_vol_B"], + _flight_intents["flight_2_equal_prio_planned_vol_B"], + _flight_intents["flight_2_equal_prio_activated_vol_B"], + _flight_intents["flight_2_equal_prio_nonconforming_vol_A"], ) + now = arrow.utcnow() + for intent_name, intent in _flight_intents.items(): + if ( + intent.request.operational_intent.state + == OperationalIntentState.Activated + ): + assert Volume4DCollection.from_interuss_scd_api( + intent.request.operational_intent.volumes + + intent.request.operational_intent.off_nominal_volumes + ), f"at least one volume of activated intent {intent_name} must be active now (now is {now})" + assert ( - self.flight_1_planned_time_range_A.request.operational_intent.state + self.flight_1_planned_vol_A.request.operational_intent.state == OperationalIntentState.Accepted - ), "flight_1_planned_time_range_A must have state Accepted" + ), "flight_1_planned_vol_A must have state Accepted" assert ( - self.flight_1_activated_time_range_A.request.operational_intent.state + self.flight_1_activated_vol_A.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_1_activated_time_range_A must have state Activated" + ), "flight_1_activated_vol_A must have state Activated" assert ( - self.flight_1_activated_time_range_A_extended.request.operational_intent.state + self.flight_1_activated_vol_A_extended.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_1_activated_time_range_A_extended must have state Activated" + ), "flight_1_activated_vol_A_extended must have state Activated" assert ( - self.flight_1_planned_time_range_B.request.operational_intent.state + self.flight_1_planned_vol_B.request.operational_intent.state == OperationalIntentState.Accepted - ), "flight_1_planned_time_range_B must have state Accepted" + ), "flight_1_planned_vol_B must have state Accepted" assert ( - self.flight_1_activated_time_range_B.request.operational_intent.state + self.flight_1_activated_vol_B.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_1_activated_time_range_B must have state Activated" + ), "flight_1_activated_vol_B must have state Activated" assert ( - self.flight_2_equal_prio_planned_time_range_B.request.operational_intent.state + self.flight_2_equal_prio_planned_vol_B.request.operational_intent.state == OperationalIntentState.Accepted - ), "flight_2_equal_prio_planned_time_range_B must have state Accepted" + ), "flight_2_equal_prio_planned_vol_B must have state Accepted" assert ( - self.flight_2_equal_prio_activated_time_range_B.request.operational_intent.state + self.flight_2_equal_prio_activated_vol_B.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_2_equal_prio_activated_time_range_B must have state Activated" + ), "flight_2_equal_prio_activated_vol_B must have state Activated" assert ( - self.flight_2_equal_prio_nonconforming_time_range_A.request.operational_intent.state + self.flight_2_equal_prio_nonconforming_vol_A.request.operational_intent.state == OperationalIntentState.Nonconforming - ), "flight_2_equal_prio_nonconforming_time_range_A must have state Nonconforming" + ), "flight_2_equal_prio_nonconforming_vol_A must have state Nonconforming" assert ( - self.flight_2_equal_prio_planned_time_range_B.request.operational_intent.priority - == self.flight_1_planned_time_range_A.request.operational_intent.priority + self.flight_2_equal_prio_planned_vol_B.request.operational_intent.priority + == self.flight_1_planned_vol_A.request.operational_intent.priority ), "flight_2 must have priority equal to flight_1" assert not Volume4DCollection.from_interuss_scd_api( - self.flight_1_planned_time_range_A.request.operational_intent.volumes + self.flight_1_planned_vol_A.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_2_equal_prio_planned_time_range_B.request.operational_intent.volumes + self.flight_2_equal_prio_planned_vol_B.request.operational_intent.volumes ) - ), "flight_1_planned_time_range_A and flight_2_equal_prio_planned_time_range_B must not intersect" + ), "flight_1_planned_vol_A and flight_2_equal_prio_planned_vol_B must not intersect" assert not Volume4DCollection.from_interuss_scd_api( - self.flight_1_planned_time_range_A.request.operational_intent.volumes + self.flight_1_planned_vol_A.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_1_activated_time_range_B.request.operational_intent.volumes + self.flight_1_activated_vol_B.request.operational_intent.volumes ) - ), "flight_1_planned_time_range_A and flight_1_activated_time_range_B must not intersect" + ), "flight_1_planned_vol_A and flight_1_activated_vol_B must not intersect" assert Volume4DCollection.from_interuss_scd_api( - self.flight_1_activated_time_range_B.request.operational_intent.volumes + self.flight_1_activated_vol_B.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_2_equal_prio_activated_time_range_B.request.operational_intent.volumes + self.flight_2_equal_prio_activated_vol_B.request.operational_intent.volumes ) - ), "flight_1_activated_time_range_B and flight_2_equal_prio_activated_time_range_B must intersect" + ), "flight_1_activated_vol_B and flight_2_equal_prio_activated_vol_B must intersect" assert Volume4DCollection.from_interuss_scd_api( - self.flight_1_activated_time_range_A.request.operational_intent.volumes + self.flight_1_activated_vol_A.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_2_equal_prio_nonconforming_time_range_A.request.operational_intent.off_nominal_volumes + self.flight_2_equal_prio_nonconforming_vol_A.request.operational_intent.off_nominal_volumes ) - ), "flight_1_activated_time_range_A.volumes and flight_2_equal_prio_nonconforming_time_range_A.off_nominal_volumes must intersect" + ), "flight_1_activated_vol_A.volumes and flight_2_equal_prio_nonconforming_vol_A.off_nominal_volumes must intersect" assert ( len( - self.flight_2_equal_prio_nonconforming_time_range_A.request.operational_intent.off_nominal_volumes + self.flight_2_equal_prio_nonconforming_vol_A.request.operational_intent.off_nominal_volumes ) > 0 - ), "flight_2_equal_prio_nonconforming_time_range_A must have off-nominal volume" + ), "flight_2_equal_prio_nonconforming_vol_A must have off-nominal volume" except KeyError as e: raise ValueError( @@ -262,14 +274,14 @@ def _setup(self) -> bool: self, "Area clearing", [ - self.flight_1_planned_time_range_A, - self.flight_1_activated_time_range_A, - self.flight_1_activated_time_range_A_extended, - self.flight_1_planned_time_range_B, - self.flight_1_activated_time_range_B, - self.flight_2_equal_prio_planned_time_range_B, - self.flight_2_equal_prio_activated_time_range_B, - self.flight_2_equal_prio_nonconforming_time_range_A, + self.flight_1_planned_vol_A, + self.flight_1_activated_vol_A, + self.flight_1_activated_vol_A_extended, + self.flight_1_planned_vol_B, + self.flight_1_activated_vol_B, + self.flight_2_equal_prio_planned_vol_B, + self.flight_2_equal_prio_activated_vol_B, + self.flight_2_equal_prio_nonconforming_vol_A, ], [self.tested_uss, self.control_uss], ) @@ -288,10 +300,10 @@ def _attempt_plan_flight_conflict(self) -> OperationalIntentReference: self, "Plan flight 2", self.control_uss, - self.flight_2_equal_prio_planned_time_range_B.request, + self.flight_2_equal_prio_planned_vol_B.request, ) flight_2_oi_ref = validator.expect_shared( - self.flight_2_equal_prio_planned_time_range_B.request + self.flight_2_equal_prio_planned_vol_B.request ) with OpIntentValidator( @@ -306,11 +318,11 @@ def _attempt_plan_flight_conflict(self) -> OperationalIntentReference: self, "Activate flight 2", self.control_uss, - self.flight_2_equal_prio_activated_time_range_B.request, + self.flight_2_equal_prio_activated_vol_B.request, self.flight_2_id, ) flight_2_oi_ref = validator.expect_shared( - self.flight_2_equal_prio_activated_time_range_B.request + self.flight_2_equal_prio_activated_vol_B.request ) with OpIntentValidator( @@ -324,7 +336,7 @@ def _attempt_plan_flight_conflict(self) -> OperationalIntentReference: self, "Attempt to plan flight 1", self.tested_uss, - self.flight_1_planned_time_range_B.request, + self.flight_1_planned_vol_B.request, ) validator.expect_not_shared() @@ -342,7 +354,7 @@ def _attempt_activate_flight_conflict(self): self, "Attempt to directly activate conflicting flight 1", self.tested_uss, - self.flight_1_activated_time_range_B.request, + self.flight_1_activated_vol_B.request, self.flight_1_id, ) validator.expect_not_shared() @@ -361,10 +373,10 @@ def _attempt_modify_planned_flight_conflict( self, "Plan flight 1", self.tested_uss, - self.flight_1_planned_time_range_A.request, + self.flight_1_planned_vol_A.request, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_planned_time_range_A.request + self.flight_1_planned_vol_A.request ) with OpIntentValidator( @@ -379,11 +391,11 @@ def _attempt_modify_planned_flight_conflict( self, "Attempt to modify planned flight 1 into conflict", self.tested_uss, - self.flight_1_planned_time_range_B.request, + self.flight_1_planned_vol_B.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_planned_time_range_A.request, skip_if_not_found=True + self.flight_1_planned_vol_A.request, skip_if_not_found=True ) return flight_1_oi_ref @@ -403,11 +415,11 @@ def _attempt_modify_activated_flight_conflict( self, "Activate flight 1", self.tested_uss, - self.flight_1_activated_time_range_A.request, + self.flight_1_activated_vol_A.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A.request + self.flight_1_activated_vol_A.request ) with OpIntentValidator( @@ -422,11 +434,11 @@ def _attempt_modify_activated_flight_conflict( self, "Attempt to modify activated flight 1 into conflict", self.tested_uss, - self.flight_1_activated_time_range_B.request, + self.flight_1_activated_vol_B.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A.request, skip_if_not_found=True + self.flight_1_activated_vol_A.request, skip_if_not_found=True ) return flight_1_oi_ref @@ -448,11 +460,11 @@ def _modify_activated_flight_preexisting_conflict( self, "Activate flight 1", self.tested_uss, - self.flight_1_activated_time_range_A.request, + self.flight_1_activated_vol_A.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A.request + self.flight_1_activated_vol_A.request ) # TODO: the following call requires the control USS to support CMSA role, @@ -477,7 +489,7 @@ def _modify_activated_flight_preexisting_conflict( }, {InjectFlightResponseResult.Failed: "Failure"}, self.control_uss, - self.flight_2_equal_prio_nonconforming_time_range_A.request, + self.flight_2_equal_prio_nonconforming_vol_A.request, self.flight_2_id, ) if resp_flight_2.result == InjectFlightResponseResult.Rejected: @@ -486,7 +498,7 @@ def _modify_activated_flight_preexisting_conflict( raise ScenarioCannotContinueError(msg) validator.expect_shared( - self.flight_2_equal_prio_nonconforming_time_range_A.request + self.flight_2_equal_prio_nonconforming_vol_A.request ) with OpIntentValidator( @@ -507,17 +519,15 @@ def _modify_activated_flight_preexisting_conflict( }, {InjectFlightResponseResult.Failed: "Failure"}, self.tested_uss, - self.flight_1_activated_time_range_A_extended.request, + self.flight_1_activated_vol_A_extended.request, self.flight_1_id, ) if resp_flight_1.result == InjectFlightResponseResult.ReadyToFly: - validator.expect_shared( - self.flight_1_activated_time_range_A_extended.request - ) + validator.expect_shared(self.flight_1_activated_vol_A_extended.request) elif resp_flight_1.result == InjectFlightResponseResult.Rejected: validator.expect_shared( - self.flight_1_activated_time_range_A.request, skip_if_not_found=True + self.flight_1_activated_vol_A.request, skip_if_not_found=True ) def cleanup(self): diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md index 15e012b15f..debfc437a0 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.md @@ -16,7 +16,7 @@ FlightIntentsResource that provides the following flight intents: - + @@ -24,7 +24,7 @@ FlightIntentsResource that provides the following flight intents: - + @@ -32,29 +32,29 @@ FlightIntentsResource that provides the following flight intents: - + - + - + - + - + @@ -62,17 +62,22 @@ FlightIntentsResource that provides the following flight intents: - + - +
Flight intent IDFlight intent ID Flight name Priority StateMust not conflict with
flight_1_planned_time_range_Aflight_1_planned_vol_A Flight 1 Any AcceptedFlight 2m
flight_1_activated_time_range_Aflight_1_activated_vol_A Activated
flight_1_planned_time_range_A_extendedflight_1_planned_vol_A_extended Flight 1m Accepted Flight 2 N/A
flight_1_activated_time_range_A_extendedflight_1_activated_vol_A_extended Activated
flight_1_activated_time_range_Bflight_1_activated_vol_B Flight 1c Activated Flight 2 N/A
flight_2_planned_time_range_Aflight_2_planned_vol_A Flight 2 Higher than Flight 1* AcceptedN/A
flight_2_activated_time_range_Aflight_2_activated_vol_A Activated
flight_2_activated_time_range_Bflight_2_activated_vol_B Flight 2m Activated Flight 1c Flight 1
+Because the scenario involves activation of intents, all activated intents must be active during the execution of the +test scenario, i.e. they must start before the reference time plus planning time duration. Additionally, their end time +must leave sufficient time for the execution of the test scenario. For the sake of simplicity, it is recommended to set +the start and end times of all the intents to the same range. + ### tested_uss FlightPlannerResource that is under test and will manage flight 1. diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py index 165694c73a..88683f7272 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py @@ -1,5 +1,6 @@ from typing import Optional, Tuple +import arrow from uas_standards.astm.f3548.v21.api import ( OperationalIntentReference, ) @@ -47,16 +48,16 @@ class ConflictHigherPriority(TestScenario): flight_1_id: Optional[str] = None - flight_1_planned_time_range_A: FlightIntent - flight_1_planned_time_range_A_extended: FlightIntent - flight_1_activated_time_range_A: FlightIntent - flight_1_activated_time_range_A_extended: FlightIntent - flight_1_activated_time_range_B: FlightIntent + flight_1_planned_vol_A: FlightIntent + flight_1_planned_vol_A_extended: FlightIntent + flight_1_activated_vol_A: FlightIntent + flight_1_activated_vol_A_extended: FlightIntent + flight_1_activated_vol_B: FlightIntent flight_2_id: Optional[str] = None - flight_2_planned_time_range_A: FlightIntent - flight_2_activated_time_range_A: FlightIntent - flight_2_activated_time_range_B: FlightIntent + flight_2_planned_vol_A: FlightIntent + flight_2_activated_vol_A: FlightIntent + flight_2_activated_vol_B: FlightIntent tested_uss: FlightPlanner control_uss: FlightPlanner @@ -74,10 +75,10 @@ def __init__( self.control_uss = control_uss.flight_planner self.dss = dss.dss - flight_intents = flight_intents.get_flight_intents() + _flight_intents = flight_intents.get_flight_intents() extents = [] - for intent in flight_intents.values(): + for intent in _flight_intents.values(): extents.extend(intent.request.operational_intent.volumes) extents.extend(intent.request.operational_intent.off_nominal_volumes) self._intents_extent = Volume4DCollection.from_interuss_scd_api( @@ -86,86 +87,96 @@ def __init__( try: ( - self.flight_1_planned_time_range_A, - self.flight_1_planned_time_range_A_extended, - self.flight_1_activated_time_range_A, - self.flight_1_activated_time_range_A_extended, - self.flight_1_activated_time_range_B, - self.flight_2_planned_time_range_A, - self.flight_2_activated_time_range_A, - self.flight_2_activated_time_range_B, + self.flight_1_planned_vol_A, + self.flight_1_planned_vol_A_extended, + self.flight_1_activated_vol_A, + self.flight_1_activated_vol_A_extended, + self.flight_1_activated_vol_B, + self.flight_2_planned_vol_A, + self.flight_2_activated_vol_A, + self.flight_2_activated_vol_B, ) = ( - flight_intents["flight_1_planned_time_range_A"], - flight_intents["flight_1_planned_time_range_A_extended"], - flight_intents["flight_1_activated_time_range_A"], - flight_intents["flight_1_activated_time_range_A_extended"], - flight_intents["flight_1_activated_time_range_B"], - flight_intents["flight_2_planned_time_range_A"], - flight_intents["flight_2_activated_time_range_A"], - flight_intents["flight_2_activated_time_range_B"], + _flight_intents["flight_1_planned_vol_A"], + _flight_intents["flight_1_planned_vol_A_extended"], + _flight_intents["flight_1_activated_vol_A"], + _flight_intents["flight_1_activated_vol_A_extended"], + _flight_intents["flight_1_activated_vol_B"], + _flight_intents["flight_2_planned_vol_A"], + _flight_intents["flight_2_activated_vol_A"], + _flight_intents["flight_2_activated_vol_B"], ) + now = arrow.utcnow() + for intent_name, intent in _flight_intents.items(): + if ( + intent.request.operational_intent.state + == OperationalIntentState.Activated + ): + assert Volume4DCollection.from_interuss_scd_api( + intent.request.operational_intent.volumes + + intent.request.operational_intent.off_nominal_volumes + ), f"at least one volume of activated intent {intent_name} must be active now (now is {now})" + assert ( - self.flight_1_planned_time_range_A.request.operational_intent.state + self.flight_1_planned_vol_A.request.operational_intent.state == OperationalIntentState.Accepted - ), "flight_1_planned_time_range_A must have state Accepted" + ), "flight_1_planned_vol_A must have state Accepted" assert ( - self.flight_1_planned_time_range_A_extended.request.operational_intent.state + self.flight_1_planned_vol_A_extended.request.operational_intent.state == OperationalIntentState.Accepted - ), "flight_1_planned_time_range_A_extended must have state Accepted" + ), "flight_1_planned_vol_A_extended must have state Accepted" assert ( - self.flight_1_activated_time_range_A.request.operational_intent.state + self.flight_1_activated_vol_A.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_1_activated_time_range_A must have state Activated" + ), "flight_1_activated_vol_A must have state Activated" assert ( - self.flight_1_activated_time_range_A_extended.request.operational_intent.state + self.flight_1_activated_vol_A_extended.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_1_activated_time_range_A_extended must have state Activated" + ), "flight_1_activated_vol_A_extended must have state Activated" assert ( - self.flight_1_activated_time_range_B.request.operational_intent.state + self.flight_1_activated_vol_B.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_1_activated_time_range_B must have state Activated" + ), "flight_1_activated_vol_B must have state Activated" assert ( - self.flight_2_planned_time_range_A.request.operational_intent.state + self.flight_2_planned_vol_A.request.operational_intent.state == OperationalIntentState.Accepted - ), "flight_2_planned_time_range_A must have state Accepted" + ), "flight_2_planned_vol_A must have state Accepted" assert ( - self.flight_2_activated_time_range_A.request.operational_intent.state + self.flight_2_activated_vol_A.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_2_activated_time_range_A must have state Activated" + ), "flight_2_activated_vol_A must have state Activated" assert ( - self.flight_2_activated_time_range_B.request.operational_intent.state + self.flight_2_activated_vol_B.request.operational_intent.state == OperationalIntentState.Activated - ), "flight_2_activated_time_range_B must have state Activated" + ), "flight_2_activated_vol_B must have state Activated" - # TODO: check that the time ranges are equal where they need to be # TODO: check that flight data is the same across the different versions of the flight assert ( - self.flight_2_planned_time_range_A.request.operational_intent.priority - > self.flight_1_planned_time_range_A.request.operational_intent.priority + self.flight_2_planned_vol_A.request.operational_intent.priority + > self.flight_1_planned_vol_A.request.operational_intent.priority ), "flight_2 must have higher priority than flight_1" assert Volume4DCollection.from_interuss_scd_api( - self.flight_1_planned_time_range_A.request.operational_intent.volumes + self.flight_1_planned_vol_A.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_2_planned_time_range_A.request.operational_intent.volumes + self.flight_2_planned_vol_A.request.operational_intent.volumes ) - ), "flight_1_planned_time_range_A and flight_2_planned_time_range_A must intersect" + ), "flight_1_planned_vol_A and flight_2_planned_vol_A must intersect" assert Volume4DCollection.from_interuss_scd_api( - self.flight_1_planned_time_range_A.request.operational_intent.volumes + self.flight_1_planned_vol_A.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_1_planned_time_range_A_extended.request.operational_intent.volumes + self.flight_1_planned_vol_A_extended.request.operational_intent.volumes ) - ), "flight_1_planned_time_range_A and flight_1_planned_time_range_A_extended must intersect" + ), "flight_1_planned_vol_A and flight_1_planned_vol_A_extended must intersect" assert not Volume4DCollection.from_interuss_scd_api( - self.flight_1_planned_time_range_A.request.operational_intent.volumes + self.flight_1_planned_vol_A.request.operational_intent.volumes ).intersects_vol4s( Volume4DCollection.from_interuss_scd_api( - self.flight_1_activated_time_range_B.request.operational_intent.volumes + self.flight_1_activated_vol_B.request.operational_intent.volumes ) - ), "flight_1_planned_time_range_A and flight_1_activated_time_range_B must not intersect" + ), "flight_1_planned_vol_A and flight_1_activated_vol_B must not intersect" except KeyError as e: raise ValueError( @@ -241,14 +252,14 @@ def _setup(self) -> bool: self, "Area clearing", [ - self.flight_1_planned_time_range_A, - self.flight_1_planned_time_range_A_extended, - self.flight_1_activated_time_range_A, - self.flight_1_activated_time_range_A_extended, - self.flight_1_activated_time_range_B, - self.flight_2_planned_time_range_A, - self.flight_2_activated_time_range_A, - self.flight_2_activated_time_range_B, + self.flight_1_planned_vol_A, + self.flight_1_planned_vol_A_extended, + self.flight_1_activated_vol_A, + self.flight_1_activated_vol_A_extended, + self.flight_1_activated_vol_B, + self.flight_2_planned_vol_A, + self.flight_2_activated_vol_A, + self.flight_2_activated_vol_B, ], [self.tested_uss, self.control_uss], ) @@ -267,9 +278,9 @@ def _attempt_plan_flight_conflict(self): self, "Plan flight 2", self.control_uss, - self.flight_2_planned_time_range_A.request, + self.flight_2_planned_vol_A.request, ) - validator.expect_shared(self.flight_2_planned_time_range_A.request) + validator.expect_shared(self.flight_2_planned_vol_A.request) with OpIntentValidator( self, @@ -282,7 +293,7 @@ def _attempt_plan_flight_conflict(self): self, "Attempt to plan flight 1", self.tested_uss, - self.flight_1_planned_time_range_A.request, + self.flight_1_planned_vol_A.request, ) validator.expect_not_shared() @@ -305,10 +316,10 @@ def _attempt_modify_planned_flight_conflict( self, "Plan flight 1", self.tested_uss, - self.flight_1_planned_time_range_A.request, + self.flight_1_planned_vol_A.request, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_planned_time_range_A.request + self.flight_1_planned_vol_A.request ) with OpIntentValidator( @@ -322,9 +333,9 @@ def _attempt_modify_planned_flight_conflict( self, "Plan flight 2", self.control_uss, - self.flight_2_planned_time_range_A.request, + self.flight_2_planned_vol_A.request, ) - validator.expect_shared(self.flight_2_planned_time_range_A.request) + validator.expect_shared(self.flight_2_planned_vol_A.request) with OpIntentValidator( self, @@ -338,11 +349,11 @@ def _attempt_modify_planned_flight_conflict( self, "Attempt to modify planned flight 1 in conflict", self.tested_uss, - self.flight_1_planned_time_range_A_extended.request, + self.flight_1_planned_vol_A_extended.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_planned_time_range_A.request, skip_if_not_found=True + self.flight_1_planned_vol_A.request, skip_if_not_found=True ) return flight_1_oi_ref @@ -362,11 +373,11 @@ def _attempt_activate_flight_conflict( self, "Attempt to activate conflicting flight 1", self.tested_uss, - self.flight_1_activated_time_range_A.request, + self.flight_1_activated_vol_A.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_planned_time_range_A.request, skip_if_not_found=True + self.flight_1_planned_vol_A.request, skip_if_not_found=True ) return flight_1_oi_ref @@ -391,11 +402,11 @@ def _modify_activated_flight_conflict_preexisting( self, "Activate flight 1", self.tested_uss, - self.flight_1_activated_time_range_A.request, + self.flight_1_activated_vol_A.request, self.flight_1_id, ) flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A.request + self.flight_1_activated_vol_A.request ) with OpIntentValidator( @@ -409,10 +420,10 @@ def _modify_activated_flight_conflict_preexisting( self, "Plan flight 2", self.control_uss, - self.flight_2_planned_time_range_A.request, + self.flight_2_planned_vol_A.request, ) flight_2_oi_ref = validator.expect_shared( - self.flight_2_planned_time_range_A.request + self.flight_2_planned_vol_A.request ) with OpIntentValidator( @@ -427,11 +438,11 @@ def _modify_activated_flight_conflict_preexisting( self, "Activate flight 2", self.control_uss, - self.flight_2_activated_time_range_A.request, + self.flight_2_activated_vol_A.request, self.flight_2_id, ) flight_2_oi_ref = validator.expect_shared( - self.flight_2_activated_time_range_A.request + self.flight_2_activated_vol_A.request ) with OpIntentValidator( @@ -446,18 +457,18 @@ def _modify_activated_flight_conflict_preexisting( self, "Modify activated flight 1 in conflict with activated flight 2", self.tested_uss, - self.flight_1_activated_time_range_A_extended.request, + self.flight_1_activated_vol_A_extended.request, self.flight_1_id, preexisting_conflict=True, ) if resp.result == InjectFlightResponseResult.ReadyToFly: flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A_extended.request + self.flight_1_activated_vol_A_extended.request ) else: flight_1_oi_ref = validator.expect_shared( - self.flight_1_activated_time_range_A.request + self.flight_1_activated_vol_A.request ) return flight_1_oi_ref, flight_2_oi_ref @@ -479,10 +490,10 @@ def _attempt_modify_activated_flight_conflict( self, "Modify activated flight 2 to not conflict with activated flight 1", self.control_uss, - self.flight_2_activated_time_range_B.request, + self.flight_2_activated_vol_B.request, self.flight_2_id, ) - validator.expect_shared(self.flight_2_activated_time_range_B.request) + validator.expect_shared(self.flight_2_activated_vol_B.request) with OpIntentValidator( self, @@ -496,11 +507,11 @@ def _attempt_modify_activated_flight_conflict( self, "Attempt to modify activated flight 1 in conflict", self.tested_uss, - self.flight_1_activated_time_range_B.request, + self.flight_1_activated_vol_B.request, self.flight_1_id, ) validator.expect_shared( - self.flight_1_activated_time_range_A_extended.request, + self.flight_1_activated_vol_A_extended.request, skip_if_not_found=True, ) diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json b/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json index 429a13a41b..59b6248886 100644 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/conflicting_flights.json @@ -248,11 +248,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:14.483425+00:00", + "value": "2023-02-12T10:27:14.483425+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:14.483425+00:00", + "value": "2023-02-12T10:52:14.483425+00:00", "format": "RFC3339" } } @@ -581,11 +581,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:14.483425+00:00", + "value": "2023-02-12T10:27:14.483425+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:14.483425+00:00", + "value": "2023-02-12T10:52:14.483425+00:00", "format": "RFC3339" } } diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json index d9a45adeb4..daa0f3d57a 100644 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_auths.json @@ -36,11 +36,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:05.359502+00:00", + "value": "2023-02-12T10:27:05.359502+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:05.359502+00:00", + "value": "2023-02-12T10:52:05.359502+00:00", "format": "RFC3339" } } @@ -103,11 +103,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:05.359502+00:00", + "value": "2023-02-12T10:27:05.359502+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:05.359502+00:00", + "value": "2023-02-12T10:52:05.359502+00:00", "format": "RFC3339" } } diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml index 7034978c94..c8c624d89f 100644 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/invalid_flight_intents.yaml @@ -23,10 +23,10 @@ intents: reference: W84 units: M time_start: - value: '2023-02-12T10:37:05.359502+00:00' + value: '2023-02-12T10:27:05.359502+00:00' format: RFC3339 time_end: - value: '2023-02-12T10:42:05.359502+00:00' + value: '2023-02-12T10:52:05.359502+00:00' format: RFC3339 state: Accepted off_nominal_volumes: [ ] @@ -77,7 +77,7 @@ intents: reference: W84 units: M time_start: - value: '2023-02-12T10:42:05.359502+00:00' + value: '2023-02-12T10:27:05.359502+00:00' format: RFC3339 time_end: value: '2023-02-12T10:57:05.359502+00:00' diff --git a/monitoring/uss_qualifier/test_data/che/flight_intents/priority_preemption.json b/monitoring/uss_qualifier/test_data/che/flight_intents/priority_preemption.json index b2fbc18685..b7830c0b1b 100644 --- a/monitoring/uss_qualifier/test_data/che/flight_intents/priority_preemption.json +++ b/monitoring/uss_qualifier/test_data/che/flight_intents/priority_preemption.json @@ -1,6 +1,6 @@ { "intents": { - "flight_1_planned_time_range_A": { + "flight_1_planned_vol_A": { "full": { "reference_time": "2023-02-12T10:34:14.483425+00:00", "request": { @@ -240,11 +240,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:14.483425+00:00", + "value": "2023-02-12T10:27:14.483425+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:14.483425+00:00", + "value": "2023-02-12T10:52:14.483425+00:00", "format": "RFC3339" } } @@ -273,9 +273,9 @@ } } }, - "flight_1_activated_time_range_A": { + "flight_1_activated_vol_A": { "delta": { - "source": "flight_1_planned_time_range_A", + "source": "flight_1_planned_vol_A", "mutation": { "request": { "operational_intent": { @@ -285,17 +285,18 @@ } } }, - "flight_1_planned_time_range_A_extended": { + "flight_1_planned_vol_A_extended": { "delta": { - "source": "flight_1_planned_time_range_A", + "source": "flight_1_planned_vol_A", "mutation": { "request": { "operational_intent": { "volumes": [ { - "time_start": { - "value": "2023-02-12T10:36:12.256896+00:00", - "format": "RFC3339" + "volume": { + "altitude_lower": { + "value": 575.0 + } } } ] @@ -304,9 +305,9 @@ } } }, - "flight_1_activated_time_range_A_extended": { + "flight_1_activated_vol_A_extended": { "delta": { - "source": "flight_1_planned_time_range_A_extended", + "source": "flight_1_planned_vol_A_extended", "mutation": { "request": { "operational_intent": { @@ -316,21 +317,21 @@ } } }, - "flight_1_planned_time_range_B": { + "flight_1_planned_vol_B": { "delta": { - "source": "flight_1_planned_time_range_A", + "source": "flight_1_planned_vol_A", "mutation": { "request": { "operational_intent": { "volumes": [ { - "time_start": { - "value": "2023-02-12T10:44:41.251669+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:48:55.233778+00:00", - "format": "RFC3339" + "volume": { + "altitude_lower": { + "value": 650.0 + }, + "altitude_upper": { + "value": 705.0 + } } } ] @@ -339,9 +340,9 @@ } } }, - "flight_1_activated_time_range_B": { + "flight_1_activated_vol_B": { "delta": { - "source": "flight_1_planned_time_range_B", + "source": "flight_1_planned_vol_B", "mutation": { "request": { "operational_intent": { @@ -351,7 +352,7 @@ } } }, - "flight_2_planned_time_range_A": { + "flight_2_planned_vol_A": { "full": { "reference_time": "2023-02-12T10:34:14.483425+00:00", "request": { @@ -639,11 +640,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:14.483425+00:00", + "value": "2023-02-12T10:27:14.483425+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:14.483425+00:00", + "value": "2023-02-12T10:52:14.483425+00:00", "format": "RFC3339" } } @@ -672,9 +673,9 @@ } } }, - "flight_2_activated_time_range_A": { + "flight_2_activated_vol_A": { "delta": { - "source": "flight_2_planned_time_range_A", + "source": "flight_2_planned_vol_A", "mutation": { "request": { "operational_intent": { @@ -684,21 +685,21 @@ } } }, - "flight_2_activated_time_range_B": { + "flight_2_activated_vol_B": { "delta": { - "source": "flight_2_activated_time_range_A", + "source": "flight_2_activated_vol_A", "mutation": { "request": { "operational_intent": { "volumes": [ { - "time_start": { - "value": "2023-02-12T10:44:41.251669+00:00", - "format": "RFC3339" - }, - "time_end": { - "value": "2023-02-12T10:48:55.233778+00:00", - "format": "RFC3339" + "volume": { + "altitude_lower": { + "value": 650.0 + }, + "altitude_upper": { + "value": 705.0 + } } } ] @@ -707,9 +708,9 @@ } } }, - "flight_2_equal_prio_planned_time_range_B": { + "flight_2_equal_prio_planned_vol_B": { "delta": { - "source": "flight_2_activated_time_range_B", + "source": "flight_2_activated_vol_B", "mutation": { "request": { "operational_intent": { @@ -720,9 +721,9 @@ } } }, - "flight_2_equal_prio_activated_time_range_B": { + "flight_2_equal_prio_activated_vol_B": { "delta": { - "source": "flight_2_equal_prio_planned_time_range_B", + "source": "flight_2_equal_prio_planned_vol_B", "mutation": { "request": { "operational_intent": { @@ -732,9 +733,9 @@ } } }, - "flight_2_equal_prio_nonconforming_time_range_A": { + "flight_2_equal_prio_nonconforming_vol_A": { "delta": { - "source": "flight_2_equal_prio_activated_time_range_B", + "source": "flight_2_equal_prio_activated_vol_B", "mutation": { "request": { "operational_intent": { @@ -1022,11 +1023,11 @@ } }, "time_start": { - "value": "2023-02-12T10:37:14.483425+00:00", + "value": "2023-02-12T10:27:14.483425+00:00", "format": "RFC3339" }, "time_end": { - "value": "2023-02-12T10:42:14.483425+00:00", + "value": "2023-02-12T10:52:14.483425+00:00", "format": "RFC3339" } } diff --git a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml index a08c345b57..cf7f2a9794 100644 --- a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml +++ b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/conflicting_flights.yaml @@ -1,7 +1,7 @@ intents: first_flight: full: - reference_time: '2023-01-01T00:00:00+00:00' + reference_time: '2023-01-01T00:12:00+00:00' request: operational_intent: volumes: @@ -39,10 +39,10 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:03:00+00:00' + value: '2023-01-01T00:05:00+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:08:00+00:00' + value: '2023-01-01T00:17:00+00:00' format: RFC3339 state: Accepted off_nominal_volumes: [ ] @@ -67,14 +67,11 @@ intents: mutation: request: operational_intent: - volumes: - - time_start: - value: '2023-01-01T00:03:00+00:00' state: Activated conflicting_flight: full: - reference_time: '2023-01-01T00:00:00+00:00' + reference_time: '2023-01-01T00:12:00+00:00' request: operational_intent: volumes: @@ -102,10 +99,10 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:05:20+00:00' + value: '2023-01-01T00:06:20+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:10:40+00:00' + value: '2023-01-01T00:20:40+00:00' format: RFC3339 state: Accepted off_nominal_volumes: [ ] diff --git a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml index c09c16b41f..01ddb804a7 100644 --- a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml +++ b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/invalid_flight_intents.yaml @@ -1,7 +1,7 @@ intents: valid_flight: full: - reference_time: '2023-01-01T00:00:00+00:00' + reference_time: '2023-01-01T00:12:00+00:00' request: operational_intent: volumes: @@ -23,10 +23,10 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:03:00+00:00' + value: '2023-01-01T00:05:00+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:08:00+00:00' + value: '2023-01-01T00:17:00+00:00' format: RFC3339 state: Accepted off_nominal_volumes: [ ] @@ -77,10 +77,10 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:08:00+00:00' + value: '2023-01-01T00:05:00+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:09:00+00:00' + value: '2023-01-01T00:20:00+00:00' format: RFC3339 invalid_activated_offnominal: diff --git a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml index a3d6ceb179..fd663fd895 100644 --- a/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml +++ b/monitoring/uss_qualifier/test_data/usa/kentland/flight_intents/priority_preemption.yaml @@ -1,7 +1,7 @@ intents: - flight_1_planned_time_range_A: + flight_1_planned_vol_A: full: - reference_time: '2023-01-01T00:00:00+00:00' + reference_time: '2023-01-01T00:12:00+00:00' request: operational_intent: volumes: @@ -39,10 +39,10 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:03:00+00:00' + value: '2023-01-01T00:05:00+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:08:00+00:00' + value: '2023-01-01T00:17:00+00:00' format: RFC3339 state: Accepted off_nominal_volumes: [ ] @@ -61,58 +61,57 @@ intents: uas_id: '' uas_type_certificate: '' - flight_1_activated_time_range_A: + flight_1_activated_vol_A: delta: - source: flight_1_planned_time_range_A + source: flight_1_planned_vol_A mutation: request: operational_intent: state: Activated - flight_1_planned_time_range_A_extended: + flight_1_planned_vol_A_extended: delta: - source: flight_1_planned_time_range_A + source: flight_1_planned_vol_A mutation: request: operational_intent: volumes: - - time_start: - value: '2023-01-01T00:02:00+00:00' - format: RFC3339 + - volume: + altitude_lower: + value: 425 - flight_1_activated_time_range_A_extended: + flight_1_activated_vol_A_extended: delta: - source: flight_1_planned_time_range_A_extended + source: flight_1_planned_vol_A_extended mutation: request: operational_intent: state: Activated - flight_1_planned_time_range_B: + flight_1_planned_vol_B: delta: - source: flight_1_planned_time_range_A + source: flight_1_planned_vol_A mutation: request: operational_intent: volumes: - - time_start: - value: '2023-01-01T00:09:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:10:00+00:00' - format: RFC3339 + - volume: + altitude_lower: + value: 602 + altitude_upper: + value: 750 - flight_1_activated_time_range_B: + flight_1_activated_vol_B: delta: - source: flight_1_planned_time_range_B + source: flight_1_planned_vol_B mutation: request: operational_intent: state: Activated - flight_2_planned_time_range_A: + flight_2_planned_vol_A: full: - reference_time: '2023-01-01T00:00:00+00:00' + reference_time: '2023-01-01T00:12:00+00:00' request: operational_intent: volumes: @@ -140,10 +139,10 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:02:00+00:00' + value: '2023-01-01T00:05:00+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:12:15+00:00' + value: '2023-01-01T00:17:00+00:00' format: RFC3339 state: Accepted off_nominal_volumes: [ ] @@ -162,48 +161,47 @@ intents: uas_id: '' uas_type_certificate: '' - flight_2_activated_time_range_A: + flight_2_activated_vol_A: delta: - source: flight_2_planned_time_range_A + source: flight_2_planned_vol_A mutation: request: operational_intent: state: Activated - flight_2_activated_time_range_B: + flight_2_activated_vol_B: delta: - source: flight_2_activated_time_range_A + source: flight_2_activated_vol_A mutation: request: operational_intent: volumes: - - time_start: - value: '2023-01-01T00:09:00+00:00' - format: RFC3339 - time_end: - value: '2023-01-01T00:10:00+00:00' - format: RFC3339 + - volume: + altitude_lower: + value: 483 + altitude_upper: + value: 519 - flight_2_equal_prio_planned_time_range_B: + flight_2_equal_prio_planned_vol_B: delta: - source: flight_2_activated_time_range_B + source: flight_2_activated_vol_B mutation: request: operational_intent: state: Accepted priority: 0 - flight_2_equal_prio_activated_time_range_B: + flight_2_equal_prio_activated_vol_B: delta: - source: flight_2_equal_prio_planned_time_range_B + source: flight_2_equal_prio_planned_vol_B mutation: request: operational_intent: state: Activated - flight_2_equal_prio_nonconforming_time_range_A: + flight_2_equal_prio_nonconforming_vol_A: delta: - source: flight_2_equal_prio_activated_time_range_B + source: flight_2_equal_prio_activated_vol_B mutation: request: operational_intent: @@ -233,8 +231,8 @@ intents: reference: W84 units: M time_start: - value: '2023-01-01T00:02:00+00:00' + value: '2023-01-01T00:05:00+00:00' format: RFC3339 time_end: - value: '2023-01-01T00:12:15+00:00' + value: '2023-01-01T00:17:00+00:00' format: RFC3339 From 2c4b919bac1e83900881db521c5148838046f215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Wed, 18 Oct 2023 11:11:58 +0200 Subject: [PATCH 4/5] fix has_active_volume --- monitoring/mock_uss/scdsc/routes_injection.py | 9 +++------ monitoring/monitorlib/geotemporal.py | 9 ++++----- .../flight_intent_validation/flight_intent_validation.py | 4 +++- .../conflict_equal_priority_not_permitted.py | 4 +++- .../conflict_higher_priority/conflict_higher_priority.py | 4 +++- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/monitoring/mock_uss/scdsc/routes_injection.py b/monitoring/mock_uss/scdsc/routes_injection.py index 2fd4af59e0..90b62e8c15 100644 --- a/monitoring/mock_uss/scdsc/routes_injection.py +++ b/monitoring/mock_uss/scdsc/routes_injection.py @@ -248,15 +248,12 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict, # Validate intent is currently active if in Activated state # I.e. at least one volume has start time in the past and end time in the future - now = arrow.utcnow() + now = arrow.utcnow().datetime if req_body.operational_intent.state == OperationalIntentState.Activated: - active_volume = False - for vol in ( + active_volume = Volume4DCollection.from_interuss_scd_api( req_body.operational_intent.volumes + req_body.operational_intent.off_nominal_volumes - ): - if vol.time_start.value.datetime <= now <= vol.time_end.value.datetime: - active_volume = True + ).has_active_volume(now) if not active_volume: return ( InjectFlightResponse( diff --git a/monitoring/monitorlib/geotemporal.py b/monitoring/monitorlib/geotemporal.py index d8b810157e..24776e0d3b 100644 --- a/monitoring/monitorlib/geotemporal.py +++ b/monitoring/monitorlib/geotemporal.py @@ -549,8 +549,7 @@ def to_f3548v21(self) -> List[f3548v21.Volume4D]: return [v.to_f3548v21() for v in self.volumes] def has_active_volume(self, time_ref: datetime) -> bool: - active_volume = False - for vol in self.volumes: - if vol.time_start.datetime <= time_ref <= vol.time_end.datetime: - active_volume = True - return active_volume + return any( + vol.time_start.datetime <= time_ref <= vol.time_end.datetime + for vol in self.volumes + ) diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py index 58ba15b117..47b28e5ade 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/flight_intent_validation/flight_intent_validation.py @@ -88,7 +88,7 @@ def __init__( _flight_intents["valid_conflict_tiny_overlap"], ) - now = arrow.utcnow() + now = arrow.utcnow().datetime for intent_name, intent in _flight_intents.items(): if ( intent.request.operational_intent.state @@ -97,6 +97,8 @@ def __init__( assert Volume4DCollection.from_interuss_scd_api( intent.request.operational_intent.volumes + intent.request.operational_intent.off_nominal_volumes + ).has_active_volume( + now ), f"at least one volume of activated intent {intent_name} must be active now (now is {now})" assert ( diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py index 7c0e88f331..98c1911af4 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_equal_priority_not_permitted/conflict_equal_priority_not_permitted.py @@ -115,7 +115,7 @@ def __init__( _flight_intents["flight_2_equal_prio_nonconforming_vol_A"], ) - now = arrow.utcnow() + now = arrow.utcnow().datetime for intent_name, intent in _flight_intents.items(): if ( intent.request.operational_intent.state @@ -124,6 +124,8 @@ def __init__( assert Volume4DCollection.from_interuss_scd_api( intent.request.operational_intent.volumes + intent.request.operational_intent.off_nominal_volumes + ).has_active_volume( + now ), f"at least one volume of activated intent {intent_name} must be active now (now is {now})" assert ( diff --git a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py index 88683f7272..d660ae127f 100644 --- a/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py +++ b/monitoring/uss_qualifier/scenarios/astm/utm/nominal_planning/conflict_higher_priority/conflict_higher_priority.py @@ -106,7 +106,7 @@ def __init__( _flight_intents["flight_2_activated_vol_B"], ) - now = arrow.utcnow() + now = arrow.utcnow().datetime for intent_name, intent in _flight_intents.items(): if ( intent.request.operational_intent.state @@ -115,6 +115,8 @@ def __init__( assert Volume4DCollection.from_interuss_scd_api( intent.request.operational_intent.volumes + intent.request.operational_intent.off_nominal_volumes + ).has_active_volume( + now ), f"at least one volume of activated intent {intent_name} must be active now (now is {now})" assert ( From 50ce40a694386e4048d4abdc9723431e3c358be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Misbach?= Date: Wed, 18 Oct 2023 11:24:35 +0200 Subject: [PATCH 5/5] correct mistakes --- monitoring/mock_uss/scdsc/flight_planning.py | 14 ++++++++++++++ monitoring/mock_uss/scdsc/routes_injection.py | 1 - .../resources/netrid/simulation/kml_flights.py | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/monitoring/mock_uss/scdsc/flight_planning.py b/monitoring/mock_uss/scdsc/flight_planning.py index b63b47d9b9..1d69c1e2c0 100644 --- a/monitoring/mock_uss/scdsc/flight_planning.py +++ b/monitoring/mock_uss/scdsc/flight_planning.py @@ -1,6 +1,7 @@ from datetime import datetime from typing import Optional, List, Callable +import arrow from uas_standards.astm.f3548.v21 import api as f3548_v21 from uas_standards.astm.f3548.v21.constants import OiMaxVertices, OiMaxPlanHorizonDays from uas_standards.interuss.automated_testing.scd.v1 import api as scd_api @@ -68,6 +69,19 @@ def validate_request(req_body: scd_api.InjectFlightRequest, locality: Locality) f"Operational intent specifies an off-nominal volume while being in {req_body.operational_intent.state} state" ) + # Validate intent is currently active if in Activated state + # I.e. at least one volume has start time in the past and end time in the future + if req_body.operational_intent.state == OperationalIntentState.Activated: + now = arrow.utcnow().datetime + active_volume = Volume4DCollection.from_interuss_scd_api( + req_body.operational_intent.volumes + + req_body.operational_intent.off_nominal_volumes + ).has_active_volume(now) + if not active_volume: + raise PlanningError( + f"Operational intent is activated but has no volume currently active (now: {now})" + ) + def check_for_disallowed_conflicts( req_body: scd_api.InjectFlightRequest, diff --git a/monitoring/mock_uss/scdsc/routes_injection.py b/monitoring/mock_uss/scdsc/routes_injection.py index 3de427a8ed..c857b372fa 100644 --- a/monitoring/mock_uss/scdsc/routes_injection.py +++ b/monitoring/mock_uss/scdsc/routes_injection.py @@ -5,7 +5,6 @@ from typing import List, Tuple import uuid -import arrow import flask from implicitdict import ImplicitDict, StringBasedDateTime from loguru import logger diff --git a/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py b/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py index 65bed8d23c..bc28374073 100755 --- a/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py +++ b/monitoring/uss_qualifier/resources/netrid/simulation/kml_flights.py @@ -205,7 +205,7 @@ def generate_flight_record( accuracy_v=flight_description.get("accuracy_v"), extrapolated=False, ) - aircraft_height = None # TODO: not None! + aircraft_height = None rid_aircraft_state = RIDAircraftState( timestamp=StringBasedDateTime(timestamp_isoformat), operational_status="Airborne",