Skip to content

Commit

Permalink
[uss_qualifier/utm/flight_intent_validation] Add intent recently ende…
Browse files Browse the repository at this point in the history
…d test step (#555)

* [mock_uss/f3548v21/flight_planning] Validate operational intent are not in the past

* [uss_qualifier/utm/flight_intent_validation] Add intent recently ended test step
  • Loading branch information
mickmis authored Mar 19, 2024
1 parent 80f77c1 commit f5b7079
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ FlightIntentsResource that provides the following flight intents:
- `valid_flight`: a valid operational intent upon which other invalid ones are derived, in `Accepted` state
- `valid_activated`: state mutation `Activated`
- `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
- `invalid_recently_ended`: end time that is within 5 to 10 seconds in the past, relative to TimeOfEvaluation
- `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
Expand Down Expand Up @@ -46,6 +47,25 @@ to reject or accept the flight. If the USS indicates that the injection attempt

#### [Validate flight intent too far ahead of time not planned](../validate_not_shared_operational_intent.md)

### Attempt to plan recently ended flight intent test step
The user flight intent that the test driver attempts to plan has a reference time that has recently ended by just slightly more than `TimeSyncMaxDifferentialSeconds`=5 seconds.
As such, if the USS synchronizes its time correctly, the planning attempt should be rejected.

#### 🛑 Incorrectly planned check
If the USS successfully plans the flight or otherwise fails to indicate a rejection, it means that it failed to validate
that the intent provided was in the past. Therefore, this check will fail if the USS indicates success in creating the
flight from the user flight intent, per one of the following requirements:
- the USS does not implement properly the interface _getOperationalIntentDetails_ as required by **[astm.f3548.v21.USS0105](../../../../requirements/astm/f3548/v21.md)**, which specifies that _The end time may not be in the past_; or
- the USS did not synchronize its time within `TimeSyncMaxDifferentialSeconds`=5 seconds of an industry-recognized time source as required by **[astm.f3548.v21.GEN0100](../../../../requirements/astm/f3548/v21.md)**; or
- the USS did not use the synchronized time for the operational intent timestamps, as required by **[astm.f3548.v21.GEN0105](../../../../requirements/astm/f3548/v21.md)**.

#### 🛑 Failure check
All flight intent data provided was complete and correct. It should have been processed successfully, allowing the USS
to reject or accept the flight. If the USS indicates that the injection attempt failed, this check will fail per
**[interuss.automated_testing.flight_planning.ExpectedBehavior](../../../../requirements/interuss/automated_testing/flight_planning.md)**.

#### [Validate recently ended flight intent not planned](../validate_not_shared_operational_intent.md)

## Validate transition to Ended state after cancellation test case
### Plan flight intent test step

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
from datetime import timedelta
from typing import Dict

import arrow

from monitoring.monitorlib.clients.flight_planning.flight_info import (
AirspaceUsageState,
UasState,
)
from monitoring.monitorlib.clients.flight_planning.flight_info_template import (
FlightInfoTemplate,
)
from monitoring.monitorlib.clients.flight_planning.planning import (
FlightPlanStatus,
PlanningActivityResult,
)
from monitoring.monitorlib.geotemporal import Volume4DCollection
from monitoring.monitorlib.temporal import TimeDuringTest, Time
from monitoring.uss_qualifier.resources.flight_planning.flight_intent_validation import (
ExpectedFlightIntent,
validate_flight_intent_templates,
)
from monitoring.uss_qualifier.suites.suite import ExecutionContext
from uas_standards.astm.f3548.v21.api import OperationalIntentState
from uas_standards.astm.f3548.v21.constants import OiMaxPlanHorizonDays, Scope
from uas_standards.astm.f3548.v21.constants import (
OiMaxPlanHorizonDays,
Scope,
TimeSyncMaxDifferentialSeconds,
)

from monitoring.uss_qualifier.resources.astm.f3548.v21 import DSSInstanceResource
from monitoring.uss_qualifier.resources.astm.f3548.v21.dss import DSSInstance
Expand All @@ -28,6 +51,7 @@
cleanup_flights,
submit_flight_intent,
delete_flight_intent,
submit_flight,
)
from uas_standards.interuss.automated_testing.scd.v1.api import (
InjectFlightResponseResult,
Expand All @@ -40,6 +64,7 @@ class FlightIntentValidation(TestScenario):
valid_activated: FlightIntent

invalid_too_far_away: FlightIntent
invalid_recently_ended_template: FlightInfoTemplate

valid_conflict_tiny_overlap: FlightIntent

Expand All @@ -60,6 +85,8 @@ def __init__(
}
)

# parse intents in deprecated FlightIntent format
# TODO: adapt scenario to use FlightInfoTemplate everywhere instead
_flight_intents = {
k: FlightIntent.from_flight_info_template(v)
for k, v in flight_intents.get_flight_intents().items()
Expand All @@ -69,9 +96,6 @@ def __init__(
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(
extents
).bounding_volume.to_f3548v21()

try:
(
Expand Down Expand Up @@ -143,15 +167,47 @@ def __init__(
f"`{self.me()}` TestScenario requirements for flight_intents not met: {e}"
)

# parse intents in FlightInfoTemplate format
expected_flight_intents = [
ExpectedFlightIntent(
"invalid_recently_ended",
"Recently Ended Flight",
usage_state=AirspaceUsageState.Planned,
uas_state=UasState.Nominal,
),
]

templates = flight_intents.get_flight_intents()
try:
validate_flight_intent_templates(templates, expected_flight_intents)
except ValueError as e:
raise ValueError(
f"`{self.me()}` TestScenario requirements for flight_intents not met: {e}"
)

for efi in expected_flight_intents:
intent = FlightIntent.from_flight_info_template(templates[efi.intent_id])
extents.extend(intent.request.operational_intent.volumes)
extents.extend(intent.request.operational_intent.off_nominal_volumes)
setattr(self, f"{efi.intent_id}_template", templates[efi.intent_id])

self._intents_extent = Volume4DCollection.from_interuss_scd_api(
extents
).bounding_volume.to_f3548v21()

def run(self, context: ExecutionContext):
times = {
TimeDuringTest.StartOfTestRun: Time(context.start_time),
TimeDuringTest.StartOfScenario: Time(arrow.utcnow().datetime),
}
self.begin_test_scenario(context)
self.record_note(
"Tested USS",
f"{self.tested_uss.config.participant_id}",
)

self.begin_test_case("Attempt to plan invalid flight intents")
self._attempt_invalid()
self._attempt_invalid(times)
self.end_test_case()

self.begin_test_case("Validate transition to Ended state after cancellation")
Expand All @@ -164,7 +220,7 @@ def run(self, context: ExecutionContext):

self.end_test_scenario()

def _attempt_invalid(self):
def _attempt_invalid(self, times: Dict[TimeDuringTest, Time]):
self.begin_test_step("Attempt to plan flight intent too far ahead of time")
with OpIntentValidator(
self,
Expand All @@ -184,6 +240,46 @@ def _attempt_invalid(self):
validator.expect_not_shared()
self.end_test_step()

self.begin_test_step("Attempt to plan recently ended flight intent")
times[TimeDuringTest.TimeOfEvaluation] = Time(arrow.utcnow().datetime)
invalid_recently_ended = self.invalid_recently_ended_template.resolve(times)

invalid_recently_ended_end_time = (
invalid_recently_ended.basic_information.area.time_end.datetime
)
recently_ended_min_end_time = times[
TimeDuringTest.TimeOfEvaluation
].datetime - timedelta(seconds=TimeSyncMaxDifferentialSeconds + 5)
recently_ended_max_end_time = times[
TimeDuringTest.TimeOfEvaluation
].datetime - timedelta(seconds=TimeSyncMaxDifferentialSeconds)
if invalid_recently_ended_end_time < recently_ended_min_end_time:
raise ValueError(
f"`{self.me()}` TestScenario requirements for flight_intents not met: invalid_recently_ended does not end recently (ends at {invalid_recently_ended_end_time}, minimum allowed {recently_ended_min_end_time})"
)
if invalid_recently_ended_end_time > recently_ended_max_end_time:
raise ValueError(
f"`{self.me()}` TestScenario requirements for flight_intents not met: invalid_recently_ended does not end in the past (ends at {invalid_recently_ended_end_time}, maximum allowed {recently_ended_max_end_time})"
)

with OpIntentValidator(
self,
self.tested_uss,
self.dss,
self._intents_extent,
) as validator:
submit_flight(
self,
"Incorrectly planned",
{(PlanningActivityResult.Rejected, FlightPlanStatus.NotPlanned)},
{PlanningActivityResult.Failed: "Failure"},
self.tested_uss.client,
invalid_recently_ended,
)

validator.expect_not_shared()
self.end_test_step()

def _validate_ended_cancellation(self):
self.begin_test_step("Plan flight intent")
with OpIntentValidator(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ intents:
time_during_test: StartOfTestRun
offset: 32d

invalid_recently_ended:
delta:
source: valid_flight
mutation:
# end_time < now - TimeSyncMaxDifferentialSeconds = 5 seconds
basic_information:
area:
- start_time:
offset_from:
starting_from:
time_during_test: TimeOfEvaluation
offset: -1m
end_time:
offset_from:
starting_from:
time_during_test: TimeOfEvaluation
offset: -5s
duration:

valid_conflict_tiny_overlap:
delta:
source: valid_flight
Expand Down

0 comments on commit f5b7079

Please sign in to comment.