Skip to content

Commit

Permalink
Merge remote-tracking branch 'interuss/main' into fix-225
Browse files Browse the repository at this point in the history
  • Loading branch information
mickmis committed Sep 29, 2023
2 parents 6ead69a + d7a3696 commit 0bed060
Show file tree
Hide file tree
Showing 37 changed files with 911 additions and 1,144 deletions.
9 changes: 6 additions & 3 deletions monitoring/mock_uss/scdsc/database.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import json
from typing import Dict, Optional

from monitoring.monitorlib import scd
from monitoring.monitorlib.multiprocessing import SynchronizedValue
from monitoring.monitorlib.scd_automated_testing import scd_injection_api
from implicitdict import ImplicitDict
from uas_standards.astm.f3548.v21.api import (
OperationalIntentReference,
OperationalIntent,
)


class FlightRecord(ImplicitDict):
"""Representation of a flight in a USS"""

op_intent_injection: scd_injection_api.OperationalIntentTestInjection
flight_authorisation: scd_injection_api.FlightAuthorisationData
op_intent_reference: scd.OperationalIntentReference
op_intent_reference: OperationalIntentReference
locked: bool = False


class Database(ImplicitDict):
"""Simple in-memory pseudo-database tracking the state of the mock system"""

flights: Dict[str, Optional[FlightRecord]] = {}
cached_operations: Dict[str, scd.OperationalIntent] = {}
cached_operations: Dict[str, OperationalIntent] = {}


db = SynchronizedValue(
Expand Down
73 changes: 39 additions & 34 deletions monitoring/mock_uss/scdsc/routes_injection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@
import traceback
from datetime import datetime, timedelta
import time
from typing import List, Tuple, Dict, Optional
from typing import List, Tuple
import uuid

import flask
from loguru import logger
import requests.exceptions

from monitoring.mock_uss.scdsc.routes_scdsc import op_intent_from_flightrecord
from monitoring.monitorlib.geo import Polygon
from monitoring.monitorlib.geotemporal import Volume4D, Volume4DCollection
from uas_standards.astm.f3548.v21.api import (
OperationalIntent,
PutOperationalIntentDetailsParameters,
ImplicitSubscriptionParameters,
PutOperationalIntentReferenceParameters,
)
from uas_standards.astm.f3548.v21.constants import OiMaxPlanHorizonDays, OiMaxVertices

from monitoring.mock_uss.config import KEY_BASE_URL, KEY_BEHAVIOR_LOCALITY
Expand Down Expand Up @@ -37,7 +45,7 @@
from monitoring.mock_uss import webapp, require_config_value
from monitoring.mock_uss.auth import requires_scope
from monitoring.mock_uss.scdsc import database, utm_client
from monitoring.mock_uss.scdsc.database import db, FlightRecord
from monitoring.mock_uss.scdsc.database import db
from monitoring.monitorlib.uspace import problems_with_flight_authorisation


Expand All @@ -55,7 +63,7 @@ def _make_stacktrace(e) -> str:

def query_operational_intents(
area_of_interest: scd.Volume4D,
) -> List[scd.OperationalIntent]:
) -> List[OperationalIntent]:
"""Retrieve a complete set of operational intents in an area, including details.
:param area_of_interest: Area where intersecting operational intents must be discovered
Expand Down Expand Up @@ -190,7 +198,9 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict,
)

# Validate max planning horizon for creation
start_time = scd.start_of(req_body.operational_intent.volumes)
start_time = Volume4DCollection.from_f3548v21(
req_body.operational_intent.volumes
).time_start.datetime
time_delta = start_time - datetime.now(tz=start_time.tzinfo)
if (
time_delta.days > OiMaxPlanHorizonDays
Expand Down Expand Up @@ -269,27 +279,17 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict,
logger.debug(
f"[inject_flight/{pid}:{flight_id}] Obtaining latest operational intent information"
)
start_time = scd.start_of(req_body.operational_intent.volumes)
end_time = scd.end_of(req_body.operational_intent.volumes)
area = scd.rect_bounds_of(req_body.operational_intent.volumes)
alt_lo, alt_hi = scd.meter_altitude_bounds_of(
vol4 = Volume4DCollection.from_f3548v21(
req_body.operational_intent.volumes
)
vol4 = scd.make_vol4(
start_time,
end_time,
alt_lo,
alt_hi,
polygon=scd.make_polygon(latlngrect=area),
)
).bounding_volume.to_f3548v21()
op_intents = query_operational_intents(vol4)

# Check for intersections
step_name = "checking for intersections"
logger.debug(
f"[inject_flight/{pid}:{flight_id}] Checking for intersections with {', '.join(op_intent.reference.id for op_intent in op_intents)}"
)
v1 = req_body.operational_intent.volumes
v1 = Volume4DCollection.from_f3548v21(req_body.operational_intent.volumes)
for op_intent in op_intents:
if (
existing_flight
Expand All @@ -315,24 +315,26 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict,
)
continue

v2 = op_intent.details.volumes + op_intent.details.off_nominal_volumes
v2 = Volume4DCollection.from_f3548v21(
op_intent.details.volumes + op_intent.details.off_nominal_volumes
)

if (
existing_flight
and existing_flight.op_intent_reference.state
== OperationalIntentState.Activated
and req_body.operational_intent.state
== OperationalIntentState.Activated
and (
scd.vol4s_intersect(existing_flight.op_intent_injection.volumes, v2)
)
and Volume4DCollection.from_f3548v21(
existing_flight.op_intent_injection.volumes
).intersects_vol4s(v2)
):
logger.debug(
f"[inject_flight/{pid}:{flight_id}] intersection with {op_intent.reference.id} not considered: modification of Activated operational intent with a pre-existing conflict"
)
continue

if scd.vol4s_intersect(v1, v2):
if v1.intersects_vol4s(v2):
notes = f"Requested flight (priority {req_body.operational_intent.priority}) intersected {op_intent.reference.manager}'s operational intent {op_intent.reference.id} (priority {op_intent.details.priority})"
return (
InjectFlightResponse(
Expand All @@ -347,13 +349,13 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict,
f"[inject_flight/{pid}:{flight_id}] Sharing operational intent with DSS"
)
base_url = "{}/mock/scd".format(webapp.config[KEY_BASE_URL])
req = scd.PutOperationalIntentReferenceParameters(
req = PutOperationalIntentReferenceParameters(
extents=req_body.operational_intent.volumes
+ req_body.operational_intent.off_nominal_volumes,
key=[op.reference.ovn for op in op_intents],
state=req_body.operational_intent.state,
uss_base_url=base_url,
new_subscription=scd.ImplicitSubscriptionParameters(uss_base_url=base_url),
new_subscription=ImplicitSubscriptionParameters(uss_base_url=base_url),
)
if existing_flight:
id = existing_flight.op_intent_reference.id
Expand All @@ -372,15 +374,15 @@ def inject_flight(flight_id: str, req_body: InjectFlightRequest) -> Tuple[dict,
# Notify subscribers
subscriber_list = ", ".join(s.uss_base_url for s in result.subscribers)
step_name = f"notifying subscribers {{{subscriber_list}}}"
operational_intent = scd.OperationalIntent(
operational_intent = OperationalIntent(
reference=result.operational_intent_reference,
details=req_body.operational_intent,
)
for subscriber in result.subscribers:
if subscriber.uss_base_url == base_url:
# Do not notify ourselves
continue
update = scd.PutOperationalIntentDetailsParameters(
update = PutOperationalIntentDetailsParameters(
operational_intent_id=result.operational_intent_reference.id,
operational_intent=operational_intent,
subscriptions=subscriber.subscriptions,
Expand Down Expand Up @@ -512,7 +514,7 @@ def delete_flight(flight_id) -> Tuple[dict, int]:
if subscriber.uss_base_url == base_url:
# Do not notify ourselves
continue
update = scd.PutOperationalIntentDetailsParameters(
update = PutOperationalIntentDetailsParameters(
operational_intent_id=result.operational_intent_reference.id,
subscriptions=subscriber.subscriptions,
)
Expand Down Expand Up @@ -579,17 +581,20 @@ def make_result(success: bool, msg: str) -> ClearAreaResponse:
try:
# Find operational intents in the DSS
step_name = "constructing DSS operational intent query"
start_time = scd.start_of([req.extent])
end_time = scd.end_of([req.extent])
area = scd.rect_bounds_of([req.extent])
alt_lo, alt_hi = scd.meter_altitude_bounds_of([req.extent])
vol4 = scd.make_vol4(
# TODO: Simply use the req.extent 4D volume more directly
extent = Volume4D.from_f3548v21(req.extent)
start_time = extent.time_start.datetime
end_time = extent.time_end.datetime
area = extent.rect_bounds
alt_lo = extent.volume.altitude_lower_wgs84_m()
alt_hi = extent.volume.altitude_upper_wgs84_m()
vol4 = Volume4D.from_values(
start_time,
end_time,
alt_lo,
alt_hi,
polygon=scd.make_polygon(latlngrect=area),
)
polygon=Polygon.from_latlng_rect(latlngrect=area),
).to_f3548v21()
step_name = "finding operational intents in the DSS"
op_intent_refs = scd_client.query_operational_intent_references(
utm_client, vol4
Expand Down
16 changes: 11 additions & 5 deletions monitoring/mock_uss/scdsc/routes_scdsc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
from monitoring.mock_uss import webapp
from monitoring.mock_uss.auth import requires_scope
from monitoring.mock_uss.scdsc.database import db, FlightRecord
from uas_standards.astm.f3548.v21.api import (
GetOperationalIntentDetailsResponse,
ErrorResponse,
OperationalIntent,
OperationalIntentDetails,
)


@webapp.route("/mock/scd/uss/v1/operational_intents/<entityid>", methods=["GET"])
Expand All @@ -23,7 +29,7 @@ def scdsc_get_operational_intent_details(entityid: str):
if flight is None:
return (
flask.jsonify(
scd.ErrorResponse(
ErrorResponse(
message="Operational intent {} not known by this USS".format(
entityid
)
Expand All @@ -33,16 +39,16 @@ def scdsc_get_operational_intent_details(entityid: str):
)

# Return nominal response with details
response = scd.GetOperationalIntentDetailsResponse(
response = GetOperationalIntentDetailsResponse(
operational_intent=op_intent_from_flightrecord(flight),
)
return flask.jsonify(response), 200


def op_intent_from_flightrecord(flight: FlightRecord) -> scd.OperationalIntent:
return scd.OperationalIntent(
def op_intent_from_flightrecord(flight: FlightRecord) -> OperationalIntent:
return OperationalIntent(
reference=flight.op_intent_reference,
details=scd.OperationalIntentDetails(
details=OperationalIntentDetails(
volumes=flight.op_intent_injection.volumes,
off_nominal_volumes=flight.op_intent_injection.off_nominal_volumes,
priority=flight.op_intent_injection.priority,
Expand Down
3 changes: 1 addition & 2 deletions monitoring/mock_uss/tracer/observation_areas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

from implicitdict import ImplicitDict

from monitoring.monitorlib.geo import Volume4D
from monitoring.monitorlib.geotemporal import Volume4D
from monitoring.monitorlib.infrastructure import AuthSpec
from monitoring.monitorlib.rid import RIDVersion


ObservationAreaID = str
"""Unique identifier of observation area."""

Expand Down
4 changes: 2 additions & 2 deletions monitoring/mock_uss/tracer/routes/observation_areas.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from monitoring.mock_uss.tracer.tracer_poll import TASK_POLL_OBSERVATION_AREAS
from monitoring.monitorlib import fetch
import monitoring.monitorlib.fetch.rid
import monitoring.monitorlib.fetch.scd
from monitoring.monitorlib.geo import Volume4D, Volume3D
from monitoring.monitorlib.geo import Volume3D
from monitoring.monitorlib.geotemporal import Volume4D


@webapp.route("/tracer/observation_areas", methods=["GET"])
Expand Down
Loading

0 comments on commit 0bed060

Please sign in to comment.