From 8c944219a7613d6da5d349e9c9fa7b6ebb49ac88 Mon Sep 17 00:00:00 2001 From: Trishna Saeharaseelan <trishna.saeharaseelan@noc.ac.uk> Date: Wed, 23 Nov 2022 13:32:10 +0000 Subject: [PATCH] refactor: schemas and ntoes for observation data --- README.md | 22 ++++-- __init__.py | 71 +---------------- docs/generate_swagger.py | 34 +++++++- formats/__init__.py | 78 +++++++++++++++++++ formats/autonomy_configuration.py | 101 ++++++++++++++++++++++++ formats/observation.py | 28 +++++++ formats/platform_status.py | 124 ++++++++++++++++++++++++++++++ 7 files changed, 382 insertions(+), 76 deletions(-) create mode 100644 formats/__init__.py create mode 100644 formats/autonomy_configuration.py create mode 100644 formats/observation.py create mode 100644 formats/platform_status.py diff --git a/README.md b/README.md index b1f2d1c..7370ea6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,19 @@ # Message Formats (DRAFT) Consists of all messages transferred into and out of the Communications Backbone. Message type schemas will be developed once reviewing each platform's data and statuses defined by each partner. -## Types of Messages by Software Component +## Message Types +* All messages will be wrapped in a `message_wrapper` that contains a standard `message_header`. +| Type | Summary of File | Human-Readable | SHARE FILE (Wrapped in Backbone message) | +| -------------------------- | ------------------------------------------------------------------------------------------------ | -------------- |-------------------------------------------| +| `autonomy_configuration` | Â Autonomy Engine's Configuration file | Yes | No | +| `mission_plan` | Â Encoded/serialized platform-specific mission plan **(shared filepath)** | No | Yes | +| `platform_status\` | Â Encoded/serialized platform-specific platform status **(shared filepath)** | No | Yes | +| `platform_status` | Decoded/de-serialized generic platform status | Yes | No | +| `observation` | Â Encoded/serialized observation data from platform for Autonomy Engine **(shared filepath)** | No | Yes | +| `observation` | Â Decoded/de-serialized observation data from platform for Autonomy Engine | Yes | No | +------------------------------------ + +## Breakdown of Message Sources & Types ### 1/ Autonomy Engine #### Transmit * mission plan _(sent per platform)_ @@ -14,7 +26,7 @@ Consists of all messages transferred into and out of the Communications Backbone * decoded ecosub status message * decoded reav status message * decoded autosub-hover status message -* decoded ecosub survey data _(from squad 1 platforms)_ +* decoded ecosub observation data _(from squad 1 platforms)_ ---------------------------------- @@ -23,13 +35,13 @@ Consists of all messages transferred into and out of the Communications Backbone * compiled ecosub mission plan * decoded ecosub status message * configuration file -* decoded ecosub survey data +* decoded ecosub observation data #### Receive * mission plan * encoded ecosub status message -* encoded ecosub survey data +* encoded ecosub observation data ---------------------------------- @@ -63,7 +75,7 @@ Consists of all messages transferred into and out of the Communications Backbone * encoded ecosub status message * encoded (or is this already decoded?) reav status message * encoded autosub status message -* encoded ecosub survey data +* encoded ecosub observation data * TBD: * are there acknowledgment messages from platforms? * how are we receiving beacon messages per platform? diff --git a/__init__.py b/__init__.py index 2de03b1..923b71d 100644 --- a/__init__.py +++ b/__init__.py @@ -4,74 +4,11 @@ import os __all__ = [ - os.path.splitext(os.path.basename(x))[0] - for x in os.listdir(os.path.dirname(__file__)) - if x.endswith(".py") and x != "__init__.py" + "docs", + "formats", + # "parsers", ] + app = Flask(__name__) api = Api(app) - -message_types = [ - "platform_status" -] # TODO: Add full range of message types once scoped out - -message_header_schema = api.model( - "MessageHeader", - { - "timestamp": fields.String( # TODO: Update to Datetime UTC format - required=True, - description="Timestamp of message", - example="2022-11-16T00:00:00Z", - ), - "source": fields.String( - required=True, - description="Where is this message from", - example="autonomy_engine", - ), - "destination": fields.String( - required=True, - description="What is the destination of this message", - example="ah-1", - ), - "encoded": fields.Boolean( - required=True, - description="Indicate that message raw (encoded) or decoded. " - + "Options: encoded=True, decoded=False", - example=False, - ), - "type": fields.String( - required=True, - description="Type of message", - example="platform_status", - ), - }, -) - -platform_schema = api.model( - "PlatformSchema", - { - "platform_ID": fields.Integer( - required=True, - description="unique identifier for platform", - example="ah-1", - ), - "serial": fields.Integer( - required=True, - description="platform serial number", - example="ah-1", - ), - "model": fields.Integer( - required=True, - description="platform serial number", - example="ah-1", - ), - "active": fields.Boolean( - required=False, - description="platform in mission", - example=True, - ), - }, -) -# TODO: Add generic positions schema -# TODO: Define units for all schemas diff --git a/docs/generate_swagger.py b/docs/generate_swagger.py index 99a33a5..519363f 100644 --- a/docs/generate_swagger.py +++ b/docs/generate_swagger.py @@ -215,7 +215,27 @@ gps_schema = api.model( ), }, ) - +observation_schema = api.model( + "Observation", + { + "message": fields.Nested( + message_header_schema, + required=True, + description="Message header", + ), + "platform": fields.Nested(platform_schema), + "time": fields.String( + required=True, + description="Timestamp of message", + example="2022-11-16T00:00:00Z", + ), + "discuss_other_fields": fields.String( + required=False, + description="", # we can track the version of the AE plan? + example="", + ), + } +) platform_status_message_schema = api.model( "PlatformStatusMessage", { @@ -349,20 +369,26 @@ ns4 = api.namespace("mission_plan", description="Mission Plan Format Per Platfor @ns4.route("/ecosub") class MissionPlanEcosub(Resource): - @ns4.response(200, "Success", message_header_schema) + @ns4.response(200, "Success", {}) def get(self): pass @ns4.route("/reav") class MissionPlanReav(Resource): - @ns4.response(200, "Success", message_header_schema) + @ns4.response(200, "Success", {}) def get(self): pass @ns4.route("/autosubhover") class MissionPlanAutosubHover(Resource): - @ns4.response(200, "Success", message_header_schema) + @ns4.response(200, "Success", {}) def get(self): pass +ns5 = api.namespace("observation", description="Observation Format --> Per Platform or generic?") +@ns5.route("") +class Observation(Resource): + @ns5.response(200, "Success", observation_schema) + def get(self): + pass if __name__ == "__main__": app.run() diff --git a/formats/__init__.py b/formats/__init__.py new file mode 100644 index 0000000..1d12dbb --- /dev/null +++ b/formats/__init__.py @@ -0,0 +1,78 @@ +from flask_restx import fields +from . import api +import os + + +__all__ = [ + os.path.splitext(os.path.basename(x))[0] + for x in os.listdir(os.path.dirname(__file__)) + if x.endswith(".py") and x != "__init__.py" +] + +message_types = [ + "platform_status", + "mission_plan_ecosub", + "mission_plan_reav", + "mission_plan_autosub", +] # TODO: Add full range of message types once scoped out + +message_header_schema = api.model( + "MessageHeader", + { + "timestamp": fields.DateTime( + required=True, + description="Timestamp of message", + example="2022-11-16T00:00:00Z", + ), + "source": fields.String( + required=True, + description="Where is this message from", + example="autonomy_engine", + ), + "destination": fields.String( + required=True, + description="What is the destination of this message", + example="ah-1", + ), + "encoded": fields.Boolean( + required=True, + description="Indicate that message raw (encoded) or decoded. " + + "Options: encoded=True, decoded=False", + example=False, + ), + "type": fields.String( + required=True, + description="Type of message", + example="platform_status", + ), + # "payload":# TODO: schema applicable changes according to "type" + }, +) + +platform_schema = api.model( + "PlatformSchema", + { + "platform_ID": fields.Integer( + required=True, + description="unique identifier for platform", + example="ah-1", + ), + "serial": fields.Integer( + required=True, + description="platform serial number", + example="ah-1", + ), + "model": fields.Integer( + required=True, + description="platform serial number", + example="ah-1", + ), + "active": fields.Boolean( + required=False, + description="platform in mission", + example=True, + ), + }, +) +# TODO: Add generic positions schema +# TODO: Define units for all schemas diff --git a/formats/autonomy_configuration.py b/formats/autonomy_configuration.py new file mode 100644 index 0000000..d2fc29d --- /dev/null +++ b/formats/autonomy_configuration.py @@ -0,0 +1,101 @@ +""" + schemas: configuration sent to Autonomy Engine (i.e. during an emergency, + if a platform needs to be removed from the mission planning) +""" +from . import api, message_header_schema, platform_schema +from flask_restx import fields + + +squad_metadata_schema = api.model( + "SquadMetadataSchema", + { + "squad_ID": fields.Integer( + required=True, + description="platform serial number", + example="ah-1", + ), + "no_of_platforms": fields.Integer( + required=True, + description="number of platform serial number", + example="ah-1", + ), + "platforms": fields.List( + fields.Nested(platform_schema), + required=True, + description="Metadata pf each platform", + ), + "squad_mission_type": fields.String( + required=True, + description="Survey or Detail", + example="survey", + ), + "squad_state": fields.Boolean( + required=True, + description="True if given Squad is executing mission type " + + "according to the Autonomy Engine plan", + example=False, + ), + }, +) + +constraints_schema = api.model( + "ConstraintsSchema", + # TODO: Should this be per platform instead of squad? + { + "min_altitude": fields.Float( + required=True, + description="Minimum altitude set for squad.", + example=15.2, + ), + "min_velocity": fields.Float( + required=True, + description="Minimum velocity set for squad.", + example=0.1, + ), + "max_velocity": fields.Float( + required=True, + description="Maximum altitude set for squad.", + example=0.9, + ), + }, +) + +environment_config_schema = api.model( # TODO: Discuss how regions are defined + "EnvironmentConfig", + { + "region_of_interest": fields.String(), + "exclusion_zone": fields.String(), + }, +) +# Main Autonomy Configuration Schema +autonomy_configuration_schema = api.model( + "SquadConfigurationSchema", + { + "message": fields.Nested( + message_header_schema, + required=True, + description="Message header", + ), + "ID": fields.Integer( + required=True, + description="Unique identifier tagged to version of this" + + " configuration plan", + example=3, + ), + "time": fields.String( + required=True, + description="", + example="", + ), + "squads": fields.Nested( + squad_metadata_schema, + required=False, + description="Details of each squad", + ), + "environment": fields.Nested( + environment_config_schema, + required=False, + description="Region of interest and exclusion zone", + ), + }, +) diff --git a/formats/observation.py b/formats/observation.py new file mode 100644 index 0000000..8f2517a --- /dev/null +++ b/formats/observation.py @@ -0,0 +1,28 @@ +""" + schema: platform-specific decoded status message (DRAFT) +""" +from . import message_header_schema, platform_schema, api +from flask_restx import fields + + +observation_schema = api.model( + "Observation", + { + "message": fields.Nested( + message_header_schema, + required=True, + description="Message header", + ), + "platform": fields.Nested(platform_schema), + "time": fields.String( + required=True, + description="Timestamp of message", + example="2022-11-16T00:00:00Z", + ), + "discuss_other_fields": fields.String( + required=False, + description="", # we can track the version of the AE plan? + example="", + ), + } +) \ No newline at end of file diff --git a/formats/platform_status.py b/formats/platform_status.py new file mode 100644 index 0000000..703c401 --- /dev/null +++ b/formats/platform_status.py @@ -0,0 +1,124 @@ +""" + schema: platform-specific decoded status message (DRAFT) +""" +from . import message_header_schema, platform_schema, api +from flask_restx import fields + +# TODO: Discuss nomenclature > platform or vehicle? + +gps_schema = api.model( + "GPS", + { + "gps_source": fields.Float( # TODO: TBD with partners + required=False, + description="Source of gps position. E.g. Beacon", + example="internal", + ), + "latitude_type": fields.String( + required=False, + description="", + example="", + ), + "longitude_type": fields.String( + required=False, + description="", + example="", + ), + "latitude": fields.Float( + required=False, + description="Latitude in <insert units>", + example="", + ), + "longitude": fields.Float( + required=False, + description="Longitude in <insert units>", + example="", + ), + "depth": fields.Float( + required=False, + description="", + example="", + ), + "gps_fix_seconds_ago": fields.Float( + required=False, + description="", + example="", + ), + }, +) + +platform_status_message_schema = api.model( + "PlatformStatusMessage", + { + "message": fields.Nested( + message_header_schema, + required=True, + description="Message header", + ), + "platform": fields.Nested(platform_schema), + "time": fields.String( + required=True, + description="Timestamp of message", + example="2022-11-16T00:00:00Z", + ), + "version": fields.Integer( + required=False, + description="", # we can track the version of the AE plan? + example="", + ), + "platform_state": fields.String( + # TODO: Define dictionary with potential STATES of each platform + required=True, + description="Current state executed by platform. E.g. " + + "STOP, IDLE, ABORT.", + example="IDLE", + ), + "mission_track_number": fields.Integer( + required=False, + description=( + "Track number - stage in mission (e.g. " + + "4 --> Waypoint 3 to Waypoint 4)" + ), + example=4, + ), + "range_to_go": fields.Float( + required=False, + description="Estimated distance to reach next waypoint", + example=124.3, + ), + "c2_health_status": fields.String( + required=False, + description="Health status extracted by respective platform's C2 " + + "if any diagnosis available checks on sensors", + example="Warning", + ), + "gps_data": fields.Nested(gps_schema), + "battery_voltage": fields.Float( + required=True, + description="Volts", + example=23.0, + ), + "battery_current": fields.Float( + required=False, + description="Amps", + example=1.2, + ), + "battery_current_per_hour": fields.Float( + required=False, + description="Amp-Hours", + example=1.2, + ), + "battery_wattage": fields.Float( + required=False, + description="Watts", + example=23.0, + ), + "battery_wattage_per_hour": fields.Float( + required=False, + description="Watt-Hours", + example=23.0, + ), + }, +) + +# TBD: Do we append beacon positions with platform positions? -- GitLab