diff --git a/README.md b/README.md index b1f2d1c90853af594d99b992e853f8e9ae283e9e..7370ea61ada50b18312801e9933b941c1c5ab840 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 2de03b19ceda158e0f790d7af4e655c98c573654..923b71dd0175a50b758a361620d0704df69971ff 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 99a33a5e9759f20dac9b0e0110d6249ccb5a7621..519363fcf5ccee63d184d66323b2b63778b4c594 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 0000000000000000000000000000000000000000..1d12dbbb301ebeda081bb3169a68d6995955d792 --- /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 0000000000000000000000000000000000000000..d2fc29d1b56f12d95294b4c952d503b0cd552c31 --- /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 0000000000000000000000000000000000000000..8f2517a7b88e5195b39249f1326333bcdf5faa8e --- /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 0000000000000000000000000000000000000000..703c401843d891c0fe1a3c8a636ac57c55846eb6 --- /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?