Commit e113878b authored by Trishna Saeharaseelan's avatar Trishna Saeharaseelan
Browse files

refactor: all messages from flask model to json schema definitions

parent 3a41f4a2
...@@ -3,7 +3,7 @@ This project repository is a collaborative workspace. It consists of all message ...@@ -3,7 +3,7 @@ This project repository is a collaborative workspace. It consists of all message
# Message Types # Message Types
Each message below will be treated as the `payload` that are wrapped in a `full_message_format` that includes a `message_header`: Each message below will be wrapped in a `message_wrapper`:
* `mission_plan`: these would be two message types, i. encoded (platform-specific serialized message) and ii. parsed, human-readable message. * `mission_plan`: these would be two message types, i. encoded (platform-specific serialized message) and ii. parsed, human-readable message.
* `platform_status`: these would be two message types, i. encoded (platform-specific serialized message) and ii. parsed, human-readable message. * `platform_status`: these would be two message types, i. encoded (platform-specific serialized message) and ii. parsed, human-readable message.
* `observation`: this would be desired scientific data sent by the platform * `observation`: this would be desired scientific data sent by the platform
......
...@@ -11,4 +11,3 @@ __all__ = [ ...@@ -11,4 +11,3 @@ __all__ = [
] ]
app = Flask(__name__) app = Flask(__name__)
api = Api(app) api = Api(app)
# api = Marshmallow(app)
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"
]
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
"source": "ecosub_c2", "source": "ecosub_c2",
"destination": "autonomy_engine", "destination": "autonomy_engine",
"encoded": false, "encoded": false,
"type": "platform_status", "type": "observation",
"payload": { "payload": {
"platform_serial":"ecosub-3", "platform_serial":"ecosub-3",
"points_of_interest": [], "points_of_interest": [],
......
from flask_restx import fields # from flask_restx import fields
from . import api # from . import api
import os import os
...@@ -11,99 +11,99 @@ __all__ = [ ...@@ -11,99 +11,99 @@ __all__ = [
# TODO: Define units for all schemas # TODO: Define units for all schemas
message_types = [ # message_types = [
"platform_status", # "platform_status",
"mission_plan_ecosub", # "mission_plan_ecosub",
"mission_plan_reav", # "mission_plan_reav",
"mission_plan_autosub", # "mission_plan_autosub",
] # TODO: Add full range of message types once scoped out # ] # TODO: Add full range of message types once scoped out
full_message_schema = api.model( # full_message_schema = api.model(
"FullMessageSchema", # "FullMessageSchema",
{ # {
"message_ID": fields.String( # "message_ID": fields.String(
required=True, # required=True,
description="UUID assigned to this message", # description="UUID assigned to this message",
example="b427003c-7bc8-11ed-a1eb-0242ac120002", # example="b427003c-7bc8-11ed-a1eb-0242ac120002",
), # ),
"timestamp": fields.DateTime( # "timestamp": fields.DateTime(
required=True, # required=True,
description="Timestamp of message", # description="Timestamp of message",
example="2022-11-16T00:00:00Z", # example="2022-11-16T00:00:00Z",
), # ),
"version": fields.Float( # "version": fields.Float(
required=True, # required=True,
description="Version of comms bacbone messaging format protocol", # description="Version of comms bacbone messaging format protocol",
example=2.0, # example=2.0,
), # ),
"source": fields.String( # "source": fields.String(
required=True, # required=True,
description="Where is this message from", # description="Where is this message from",
example="autonomy_engine", # example="autonomy_engine",
), # ),
"destination": fields.String( # "destination": fields.String(
required=True, # required=True,
description="What is the destination of this message", # description="What is the destination of this message",
example="ah-1", # example="ah-1",
), # ),
"encoded": fields.Boolean( # "encoded": fields.Boolean(
required=True, # required=True,
description="Indicate that message raw (encoded) or decoded. " # description="Indicate that message raw (encoded) or decoded. "
+ "Options: encoded=True, decoded=False", # + "Options: encoded=True, decoded=False",
example=False, # example=False,
), # ),
"type": fields.String( # "type": fields.String(
required=True, # required=True,
description="Type of message", # description="Type of message",
example="platform_status", # example="platform_status",
), # ),
"payload": fields.Raw( # "payload": fields.Raw(
required=True, # required=True,
description="Content of Message", # description="Content of Message",
# example="{}", # # example="{}",
), # ),
}, # },
) # )
constraints_schema = api.model( # constraints_schema = api.model(
"ConstraintsSchema", # "ConstraintsSchema",
{ # {
"min_altitude": fields.Float( # "min_altitude": fields.Float(
required=True, # required=True,
description="Minimum altitude set for squad.", # description="Minimum altitude set for squad.",
example=15.2, # example=15.2,
), # ),
"min_velocity": fields.Float( # "min_velocity": fields.Float(
required=True, # required=True,
description="Minimum velocity set for squad.", # description="Minimum velocity set for squad.",
example=0.1, # example=0.1,
), # ),
"max_velocity": fields.Float( # "max_velocity": fields.Float(
required=True, # required=True,
description="Maximum altitude set for squad.", # description="Maximum altitude set for squad.",
example=0.9, # example=0.9,
), # ),
}, # },
) # )
platform_schema = api.model( # platform_schema = api.model(
"PlatformSchema", # "PlatformSchema",
{ # {
"platform_ID": fields.Integer( # "platform_ID": fields.Integer(
required=True, # required=True,
description="unique identifier for platform", # description="unique identifier for platform",
example=1, # example=1,
), # ),
"serial": fields.String( # "serial": fields.String(
required=True, # required=True,
description="platform serial number", # description="platform serial number",
example="reav-60", # example="reav-60",
), # ),
"model": fields.String( # "model": fields.String(
required=True, # required=True,
description="platform serial number", # description="platform serial number",
example="reav", # example="reav",
), # ),
"constraints": fields.Nested(constraints_schema), # "constraints": fields.Nested(constraints_schema),
}, # },
) # )
...@@ -2,29 +2,44 @@ ...@@ -2,29 +2,44 @@
schemas: Acknowledgement status sent by the surface platform to report schemas: Acknowledgement status sent by the surface platform to report
receipt of message. receipt of message.
""" """
from . import api, full_message_schema acknowledgement_schema = {
from flask_restx import fields "properties": {
"message_ID": {
"type": "string",
acknowledgement_schema = api.model( "description": "Identifier of message received and executed with "
"Acknowledgement",
{
"message": fields.Nested(
full_message_schema,
required=True,
description="Message header",
),
"message_ID": fields.Integer(
required=True,
description="Identifier of message received and executed with "
+ "success for mission plans sent by the Autonomy Engine.", + "success for mission plans sent by the Autonomy Engine.",
example=202, "example": "b427003c-7bc8-11ed-a1eb-0242ac999999",
), },
"status": fields.String( "status": {
required=True, "type": "string",
description="Highest level of acknowledgement. I.e. `c2_received`:" "description": "Highest level of acknowledgement. I.e. `c2_received`:"
+ " Received by C2, `c2_sent`: Sent from C2->Platform, `executed`:" + " Received by C2, `c2_sent`: Sent from C2->Platform, `executed`:"
+ " Executed by platform", + " Executed by platform",
), "example": "executed by platform",
}, },
) },
"required": ["message_ID", "status"],
}
# acknowledgement_schema = api.model(
# "Acknowledgement",
# {
# "message": fields.Nested(
# full_message_schema,
# required=True,
# description="Message header",
# ),
# "message_ID": fields.Integer(
# required=True,
# description="Identifier of message received and executed with "
# + "success for mission plans sent by the Autonomy Engine.",
# example=202,
# ),
# "status": fields.String(
# required=True,
# description="Highest level of acknowledgement. I.e. `c2_received`:"
# + " Received by C2, `c2_sent`: Sent from C2->Platform, `executed`:"
# + " Executed by platform",
# ),
# },
# )
"""
schemas: Message Wrapper is used to wrap all message types that contain
details of where the message is coming from, which end client is its destination
and the type of message.
"""
message_wrapper_schema = {
"type": "object",
"discriminator": {
"propertyName": "message_type",
},
"properties": {
"message_ID": {
"type": "string",
"description": "An identifier for the type of " + "message received.",
"example": "PlatformStatus",
},
"timestamp": {
"type": "date-time",
"description": "Timestamp of message",
"example": "2022-11-16T00:00:00Z",
},
"message_type": {
"type": "Type of message",
"description": "Type of message",
"example": "platform_status",
},
"version": {
"type": "string",
"description": "Version of comms bacbone messaging format protocol",
"example": 2.0,
},
"source": {
"type": "string",
"description": "The sender; Where is this message from",
"example": "autonomy_engine",
},
"destination": {
"type": "string",
"description": "Publisher topic; What is the destination of this message",
"example": "ah1",
},
"encoded": {
"type": "boolean",
"description": "Indicate that message raw (encoded) or decoded. "
+ "Options: encoded=True, decoded=False",
"example": False,
},
"delivery_type": {
"type": "string",
"description": "To publish or broadcast this message.",
"enum": ["broadcast", "publish"],
"example": "publish",
"default": "publish",
},
},
"required": ["message_type"],
}
...@@ -3,83 +3,151 @@ ...@@ -3,83 +3,151 @@
sent to the respective platform's C2 to compile into a platform-specific sent to the respective platform's C2 to compile into a platform-specific
mission plan. mission plan.
""" """
from . import api, full_message_schema # from . import api, full_message_schema
from flask_restx import fields # from flask_restx import fields
action_schema = {
action_schema = api.model( "type": "object",
"AutonomyEngineAction", "properties": {
{ "action": {
"action": fields.String( "type": "string",
required=True, "description": "Autonomy Engine's action from `move`, `payload`,"
description="Autonomy Engine's action from `move`, `payload`,"
+ " `dive`, `send_hits`, `scanline`, `scanpoint`.", + " `dive`, `send_hits`, `scanline`, `scanpoint`.",
example="move", "example": "move",
), },
"flight_style": fields.String( "flight_style": {
required=False, "type": "string",
description="Platform-specific modes/flight styles to perform" "description": "Platform-specific modes/flight styles to perform"
+ " next action", + " next action",
example="orbit", "example": "orbit",
), },
"latitude_waypoint": fields.Float( "latitude_waypoint": {
required=True, "type": "number",
description="Next waypoint, x-coordinate", "description": "Next waypoint, x-coordinate",
example=-4.187143188645706, "example": -4.187143188645706,
), },
"longitude_waypoint": fields.Float( "longitude_waypoint": {
required=True, "type": "number",
description="Next waypoint, y-coordinate", "description": "Next waypoint, y-coordinate",
example=50.37072283932642, "example": 50.37072283932642,
), },
"altitude": fields.Float( "altitude": {
required=False, "type": "number",
description="Altitude of next action", "description": "Altitude of next action",
example=15.0, "example": 15.0,
), },
"depth": fields.Float( "depth": {
required=False, "type": "number",
description="Depth of next action", "description": "Depth of next action",
example=15.0, "example": 15.0,
), },
"activate_payload": fields.Boolean( "activate_payload": {
required=False, "type": "boolean",
description="To activate/deactivate sensor for Autosub " "description": "To activate/deactivate sensor for Autosub "
+ "Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`", + "Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`",
example=True, "example": True,
), },
"send_environmental_data": fields.Boolean( "send_environmental_data": {
required=False, "type": "boolean",
description="To trigger the platform to send list of observations" "description": "To trigger the platform to send list of observations"
+ " if any found", + " if any found",
example=False, "example": False,
), },
}, },
) "required": [
"action",
"latitude_waypoint",
"longitude_waypoint",
],
}
mission_plan_schema = api.model( mission_plan_schema = {
"MissionPlan", "allOf": [{"$ref": "#/components/schemas/Message"}],
{ "type": "object",
"message": fields.Nested( "properties": {
full_message_schema, "plan_ID": {"type": "integer"},
required=True, "platform_serial": {"type": "string"},
description="Message header", "plan": {
), "type": "array",
"plan_ID": fields.Integer( "items": action_schema,
required=True,
description="Identifier of given mission sequence planned",
example=1,
),
"platform_serial": fields.String(
required=True,
description="Serial of target platform to send mission to",
example="reav-60",
),
"plan": fields.List(
fields.Nested(action_schema),
required=True,
description="Sequence of actions/instructions generated by the "
+ " Autonomy Engine that should be compiled by the respective C2.",
),
}, },
) },
"required": ["plan_ID", "platform_serial", "plan"],
}
# action_schema = api.model(
# "AutonomyEngineAction",
# {
# "action": fields.String(
# required=True,
# description="Autonomy Engine's action from `move`, `payload`,"
# + " `dive`, `send_hits`, `scanline`, `scanpoint`.",
# example="move",
# ),
# "flight_style": fields.String(
# required=False,
# description="Platform-specific modes/flight styles to perform"
# + " next action",
# example="orbit",
# ),
# "latitude_waypoint": fields.Float(
# required=True,
# description="Next waypoint, x-coordinate",
# example=-4.187143188645706,
# ),
# "longitude_waypoint": fields.Float(
# required=True,
# description="Next waypoint, y-coordinate",
# example=50.37072283932642,
# ),
# "altitude": fields.Float(
# required=False,
# description="Altitude of next action",
# example=15.0,
# ),
# "depth": fields.Float(
# required=False,
# description="Depth of next action",
# example=15.0,
# ),
# "activate_payload": fields.Boolean(
# required=False,
# description="To activate/deactivate sensor for Autosub "
# + "Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`",
# example=True,
# ),
# "send_environmental_data": fields.Boolean(
# required=False,
# description="To trigger the platform to send list of observations"
# + " if any found",
# example=False,
# ),
# },
# )
# mission_plan_schema = api.model(
# "MissionPlan",
# {
# "message": fields.Nested(
# full_message_schema,
# required=True,
# description="Message header",
# ),
# "plan_ID": fields.Integer(
# required=True,
# description="Identifier of given mission sequence planned",
# example=1,
# ),
# "platform_serial": fields.String(
# required=True,
# description="Serial of target platform to send mission to",
# example="reav-60",
# ),
# "plan": fields.List(
# fields.Nested(action_schema),
# required=True,
# description="Sequence of actions/instructions generated by the "
# + " Autonomy Engine that should be compiled by the respective C2.",
# ),
# },
# )
""" """
schema: platform-specific decoded status message (DRAFT) schema: platform-specific decoded status message (DRAFT)
""" """
from . import full_message_schema, api # from . import full_message_schema, api
from flask_restx import fields # from flask_restx import fields
observation_schema = {
observation_schema = api.model( "allOf": [{"$ref": "#/components/schemas/Message"}],
"Observation", "type": "object",
{ "properties": {
"message": fields.Nested( "platform_serial": {
full_message_schema, "description": "Serial of platform to sendign observations",
required=True, "example": "ecosub-3",
description="Message header", },
),
"platform_serial": fields.String(
required=True,
description="Serial of platform to sendign observations",
example="ecosub-3",
),
# "observation_type" ==> payloads tied to different types maybe? # "observation_type" ==> payloads tied to different types maybe?
# properties of each observation? # properties of each observation?
"points_of_interest": fields.Float( "points_of_interest": {
required=False, "description": "Points from features of interest identified by"
description="Points from features of interest identified by"
+ " platform if any found. DEFINE FORMAT.", + " platform if any found. DEFINE FORMAT.",
example="", "example": "",
), },
"region_surveyed": fields.Float( "region_surveyed": {
required=False, "description": "Region surveyed by given platform. DEFINE FORMAT."
description="Region surveyed by given platform. DEFINE FORMAT."
+ " GEOJSON?", + " GEOJSON?",
example="", "example": "",
), },
"quality_of_points": fields.Float( "quality_of_points": {
required=False, "description": "Quality/strength of points from features of interest"
description="Quality/strength of points from features of interest"
+ " identified by platform. DEFINE FORMAT.", + " identified by platform. DEFINE FORMAT.",
example=0.98, "example": 0.98,
),
"additional_data": fields.Raw(
required=False,
description="Placeholder field for any additional data",
example={"sensor_payload": False},
),
}, },
) "additional_data": {
"description": "Placeholder field for any additional data",
"example": {"sensor_payload": False},
},
},
"required": ["platform_serial"],
}
# observation_schema = api.model(
# "Observation",
# {
# "message": fields.Nested(
# full_message_schema,
# required=True,
# description="Message header",
# ),
# "platform_serial": fields.String(
# required=True,
# description="Serial of platform to sendign observations",
# example="ecosub-3",
# ),
# # "observation_type" ==> payloads tied to different types maybe?
# # properties of each observation?
# "points_of_interest": fields.Float(
# required=False,
# description="Points from features of interest identified by"
# + " platform if any found. DEFINE FORMAT.",
# example="",
# ),
# "region_surveyed": fields.Float(
# required=False,
# description="Region surveyed by given platform. DEFINE FORMAT."
# + " GEOJSON?",
# example="",
# ),
# "quality_of_points": fields.Float(
# required=False,
# description="Quality/strength of points from features of interest"
# + " identified by platform. DEFINE FORMAT.",
# example=0.98,
# ),
# "additional_data": fields.Raw(
# required=False,
# description="Placeholder field for any additional data",
# example={"sensor_payload": False},
# ),
# },
# )
...@@ -2,25 +2,60 @@ ...@@ -2,25 +2,60 @@
schemas: configuration sent to Autonomy Engine (i.e. during an emergency, schemas: configuration sent to Autonomy Engine (i.e. during an emergency,
if a platform needs to be removed from the mission planning) if a platform needs to be removed from the mission planning)
""" """
from . import api, full_message_schema, platform_schema # from . import api, full_message_schema, platform_schema
from flask_restx import fields # from flask_restx import fields
region_schema = api.model( constraints_schema = {
"RegionSchema", "type": "object",
{ "properties": {
"region": fields.Raw( "min_altitude": {
required=True, "type": "number",
description="Using GEOJSON, exact region of interest in rectangle" "description": "Minimum altitude set for squad.",
+ " format polygon", "example": 15.2,
example={ },
"type": "FeatureCollection", "min_velocity": {
"features": [ "type": "number",
{ "description": "Minimum velocity set for squad.",
"type": "Feature", "example": 0.1,
"properties": {}, },
"geometry": { "max_velocity": {
"coordinates": [ "type": "number",
"description": "Maximum altitude set for squad.",
"example": 0.9,
},
},
"required": ["min_altitude", "min_velocity", "max_velocity"],
}
platform_schema = {
"type": "object",
"properties": {
"platform_ID": {
"type": "integer",
"description": "Identifier for platform",
"example": 23,
},
"serial": {
"type": "string",
"description": "platform serial number",
"example": "reav-60",
},
"model": {
"type": "string",
"example": "reav",
},
"constraints": constraints_schema,
},
"required": ["platform_ID", "serial", "model", "constraints"],
}
region_schema = {
"type": "object",
"properties": {
"geometry_coordinates": {
"type": "array", # TODO: Check if config defn is right.
"example": [
[ [
[-4.187143188645706, 50.37072283932642], [-4.187143188645706, 50.37072283932642],
[-4.202697005964865, 50.368816892405874], [-4.202697005964865, 50.368816892405874],
...@@ -28,76 +63,169 @@ region_schema = api.model( ...@@ -28,76 +63,169 @@ region_schema = api.model(
[-4.19449868846155, 50.362267670845654], [-4.19449868846155, 50.362267670845654],
] ]
], ],
"type": "Polygon",
}, },
} },
"description": "Using GEOJSON, exact 4-point region (rectangle shaped)",
"required": ["geometry_coordinates"],
}
squad_metadata_schema = {
"type": "object",
"properties": {
"squad_ID": {
"type": "integer",
"description": "Identifier of given squad",
"example": 23,
},
"no_of_platforms": {
"type": "integer",
"description": "Number of platforms",
"example": 3,
},
"platforms": {
"type": "array",
"items": platform_schema,
"description": "Squad consists of these platforms",
},
"squad_mission_type": {
"type": "string",
"enum": ["tracking", "survey", "inspection"],
"description": "Mission of given squad: `tracking`, `survey`"
+ ", `inspection`",
"example": "survey",
},
"squad_state": {
"type": "string",
"description": "In execution, Waiting.. <define further>",
"example": False,
},
"region_of_interest": region_schema,
"exclusion_zones": {
"type": "array",
"items": region_schema,
"description": "Exclusion zones per squad.",
},
},
"required": [
"squad_ID",
"no_of_platforms",
"platforms",
"squad_mission_type",
"squad_state",
"exclusion_zones",
], ],
}
planning_configuration_schema = {
"allOf": [{"$ref": "#/components/schemas/Message"}],
"type": "object",
"properties": {
"config_ID": {
"type": "integer",
"description": "Unique identifier tagged to version of this"
+ " configuration plan",
"example": 3,
},
"squads": {
"type": "array",
"items": squad_metadata_schema,
}, },
),
}, },
) "required": ["config_ID", "squads"],
}
squad_metadata_schema = api.model( # region_schema = api.model(
"SquadMetadataSchema", # "RegionSchema",
{ # {
"squad_ID": fields.Integer( # "region": fields.Raw(
required=True, # required=True,
description="Identifier of given squad", # description="Using GEOJSON, exact region of interest in rectangle"
example=23, # + " format polygon",
), # example={
"no_of_platforms": fields.Integer( # "type": "FeatureCollection",
required=True, # "features": [
description="Number of platforms", # {
example=3, # "type": "Feature",
), # "properties": {},
"platforms": fields.List( # "geometry": {
fields.Nested(platform_schema), # "coordinates": [
required=True, # [
description="Squad consists of these platforms", # [-4.187143188645706, 50.37072283932642],
), # [-4.202697005964865, 50.368816892405874],
"squad_mission_type": fields.String( # [-4.203156724702808, 50.365640144076906],
required=True, # [-4.19449868846155, 50.362267670845654],
description="Mission of given squad: `tracking`, `survey`" # ]
+ ", `inspection`", # ],
example="survey", # "type": "Polygon",
), # },
"squad_state": fields.Boolean( # }
required=True, # ],
description="In execution, Waiting.. <define further>", # },
example=False, # ),
), # },
"region_of_interest": fields.List( # )
fields.Nested(region_schema),
required=False,
description="Region of interest and exclusion zones per squad.",
),
"exclusion_zones": fields.List(
fields.Nested(region_schema),
required=True,
description="Exclusion zones exclusion zones per squad.",
),
},
)
# squad_metadata_schema = api.model(
# "SquadMetadataSchema",
# {
# "squad_ID": fields.Integer(
# required=True,
# description="Identifier of given squad",
# example=23,
# ),
# "no_of_platforms": fields.Integer(
# required=True,
# description="Number of platforms",
# example=3,
# ),
# "platforms": fields.List(
# fields.Nested(platform_schema),
# required=True,
# description="Squad consists of these platforms",
# ),
# "squad_mission_type": fields.String(
# required=True,
# description="Mission of given squad: `tracking`, `survey`"
# + ", `inspection`",
# example="survey",
# ),
# "squad_state": fields.Boolean(
# required=True,
# description="In execution, Waiting.. <define further>",
# example=False,
# ),
# "region_of_interest": fields.List(
# fields.Nested(region_schema),
# required=False,
# description="Region of interest and exclusion zones per squad.",
# ),
# "exclusion_zones": fields.List(
# fields.Nested(region_schema),
# required=True,
# description="Exclusion zones exclusion zones per squad.",
# ),
# },
# )
planning_configuration_schema = api.model(
"PlanningConfigurationSchema", # planning_configuration_schema = api.model(
{ # "PlanningConfigurationSchema",
"message": fields.Nested( # {
full_message_schema, # "message": fields.Nested(
required=True, # full_message_schema,
description="Message header", # required=True,
), # description="Message header",
"ID": fields.Integer( # ),
required=True, # "ID": fields.Integer(
description="Unique identifier tagged to version of this" # required=True,
+ " configuration plan", # description="Unique identifier tagged to version of this"
example=3, # + " configuration plan",
), # example=3,
"squads": fields.Nested( # ),
squad_metadata_schema, # "squads": fields.Nested(
required=False, # squad_metadata_schema,
description="Details of each squad", # required=False,
), # description="Details of each squad",
}, # ),
) # },
# )
""" """
schema: platform-specific decoded status message (DRAFT) schema: platform-specific decoded status message
""" """
from . import full_message_schema, api # from . import full_message_schema, api
from flask_restx import fields # from flask_restx import fields
gps_schema = api.model(
"GPS",
{
"gps_source": fields.Float( # TODO: TBD with partners
required=False,
description=(
"Source of gps position. E.g. USBL (external),"
+ "platform itself (internal)"
),
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 <DEFINE UNITS>",
example="",
),
"longitude": fields.Float(
required=False,
description="Longitude in <DEFINE UNITS>",
example="",
),
"depth": fields.Float(
required=False,
description="Depth in <DEFINE UNITS>",
example="",
),
"altitude": fields.Float(
required=False,
description="Altitude in <DEFINE UNITS>",
example="",
),
# "gps_fix_seconds_ago"
},
)
sensor_schema = api.model( gps_schema = {
"SensorSchema", "type": "object",
{ "properties": {
"sensor_ID": fields.Integer( "gps_source": {
required=True, "type": "string",
description="unique identifier for platform", "description": "Source of gps position. E.g. USBL (external),"
example=2, + "platform itself (internal)",
), "example": "internal",
"serial": fields.String( },
required=False, "latitude_type": {
description="serial number of sensor", "type": "string",
example="mbes-001", "description": "TODO: Add description",
), },
"sensor_status": fields.Boolean( "longitude_type": {
required=False, "type": "string",
description="Sensor switched on (True) or off (False)", "description": "TODO: Add description",
example=True, },
), "latitude": {
"additional_data": fields.Raw( "type": "number",
required=False, "description": "Latitude in decimal degrees.",
description="Any addition fields/data to be added here", "example": 178.2,
), },
}, "longitude": {
) "type": "number",
"description": "Longitude in decimal degrees.",
"example": -10.122,
},
"depth": {
"type": "number",
"description": "Target depth in metres",
"example": 50,
"default": 0,
},
"altitude": {
"type": "number",
"description": "Target altitude in metres",
"example": 20,
},
},
"required": [
"gps_source",
"latitude",
"longitude",
],
}
sensor_schema = {
"type": "object",
"description": "Scanning sensor on platform available to be controlled by the Autonomy Engine",
"properties": {
"sensor_serial": {
"type": "string",
"description": "serial number of sensor",
"example": "mbes-002a",
},
"sensor_status": {
"type": "boolean",
"description": "Sensor switched on (True) or off (False)",
"example": True,
},
"additional_data": {
"type": "null",
"description": "Any addition fields/data to be added here",
"example": {"payload": [1.2, 434]},
},
},
"required": [],
}
platform_status_message_schema = api.model( platform_status_message_schema = {
"platformStatusMessage", "allOf": [{"$ref": "#/components/schemas/Message"}],
{ "type": "object",
"message": fields.Nested( "properties": {
full_message_schema, "platform_ID": {
required=True, "type": "integer",
description="Message header", "description": "Identifier for platform",
), "example": 1,
"platform_ID": fields.Integer( },
required=True, "platform_timestamp": {
description="unique identifier for platform", "type": "date-time",
example=1, "decription": "Timestamp for onboard platform status message",
), "example": "2022-12-21T00:00:00Z",
"active": fields.Boolean( },
required=False, "active": {
description="When a platform is in deployment (executing a" "type": "boolean",
"description": "When a platform is in deployment (executing a"
+ " mission plan) this should be True", + " mission plan) this should be True",
example=True, "example": True,
), },
"platform_state": fields.String( "platform_state": {
# TODO: Define dictionary with potential STATES of each platform # TODO: Define dictionary with potential STATES of each platform
required=False, "type": "string",
description="Current state executed by platform. E.g. " "description": "Current state executed by platform. E.g. "
+ "STOP, IDLE, ABORT.", + "STOP, IDLE, ABORT.",
example="IDLE", "example": "ABORT",
), },
"autonomy_plan_ID": fields.Integer( "autonomy_plan_ID": {
required=False, "type": "integer",
description="Last mission plan ID (according to Autonomy Engine's" "description": "Last mission plan ID (according to Autonomy Engine's"
+ " mission plan number) executed by platform", + " mission plan number) executed by platform",
example=1, "example": 1,
), },
"mission_track_ID": fields.Integer( "mission_track_ID": {
required=False, "type": "integer",
description=( "description": "Track number - stage in mission (e.g. "
"Track number - stage in mission (e.g. " + "4 --> Waypoint 3 to Waypoint 4)",
+ "4 --> Waypoint 3 to Waypoint 4)" "example": 4,
), },
example=4, "mission_action_ID": {
), "type": "integer",
"mission_action_ID": fields.Integer( "description": "TODO: add description",
required=False, "example": 1,
description="to add description", },
example=1, "range_to_go": {
), "type": "number",
"range_to_go": fields.Float( "description": "Estimated distance to reach next waypoint",
required=False, "example": 124.3,
description="Estimated distance to reach next waypoint", },
example=124.3, "speed_over_ground": {
), "type": "number",
"speed_over_ground": fields.Float( "description": "TODO: add description",
required=False, "example": 124.3,
description="", },
example=124.3, "water_current_velocity": {
), "type": "number",
"water_current_velocity": fields.Float( "description": "TODO: add description",
required=False, "example": 124.3,
description="", },
example=124.3, "thrust_applied": {
), "type": "number",
"thrust_applied": fields.Float( "description": "TODO: Needs further consideration",
required=False, "example": 124.3,
description="TODO: Needs further consideration", },
example=124.3, "health_status": {
), "type": "string",
"health_status": fields.String( "description": "Health status extracted by respective platform "
required=False, + "if any diagnosis is available to check sensors",
description="Health status extracted by respective platform " "example": "Warning",
+ "if any diagnosis available checks on sensors", },
example="Warning", "gps_data": {
), "type": "array",
"gps_data": fields.List( "description": "position of platform",
fields.Nested(gps_schema), # TODO: TBD Do we want a list of "items": gps_schema,
# gps readings to allow > 1 reading i.e. platform + usbl },
required=True, "localisation_error": {
description="Metadata pf each platform", "type": "number",
), "description": "Localisation error at last USBL update.",
"localisation_error": fields.Float( "example": 0.000129,
required=False, },
description="Localisation error at last USBL update.", "usbl_fix_seconds_ago": {
example="", "type": "number",
), "description": "USBL Fix received x second ago.",
"usbl_fix_seconds_ago": fields.Float( "example": 10.0,
required=False, },
description="", "battery_remaining_capacity": {
example="", "type": "number",
), "description": "Battery remaining capacity % provided by respective",
"battery_remaining_capacity": fields.Float( "example": 80.2,
required=True, },
description="Battery remaining capacity % provided by respective" "sensor_config": sensor_schema,
+ " platform/C2.", },
example=80.0, "required": [
), "platform_ID",
"sensor_config": fields.Nested( "platform_timestamp",
sensor_schema "gps_data",
), # TODO: TBD Do we want a list of sensors to allow > 1 sensor "battery_remaining_capacity",
}, ],
) }
# gps_schema = api.model(
# "GPS",
# {
# "gps_source": fields.Float( # TODO: TBD with partners
# required=False,
# description=(
# "Source of gps position. E.g. USBL (external),"
# + "platform itself (internal)"
# ),
# 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 <DEFINE UNITS>",
# example="",
# ),
# "longitude": fields.Float(
# required=False,
# description="Longitude in <DEFINE UNITS>",
# example="",
# ),
# "depth": fields.Float(
# required=False,
# description="Depth in <DEFINE UNITS>",
# example="",
# ),
# "altitude": fields.Float(
# required=False,
# description="Altitude in <DEFINE UNITS>",
# example="",
# ),
# # "gps_fix_seconds_ago"
# },
# )
# sensor_schema = api.model(
# "SensorSchema",
# {
# "sensor_ID": fields.Integer(
# required=True,
# description="unique identifier for platform",
# example=2,
# ),
# "serial": fields.String(
# required=False,
# description="serial number of sensor",
# example="mbes-001",
# ),
# "sensor_status": fields.Boolean(
# required=False,
# description="Sensor switched on (True) or off (False)",
# example=True,
# ),
# "additional_data": fields.Raw(
# required=False,
# description="Any addition fields/data to be added here",
# ),
# },
# )
# platform_status_message_schema = api.model(
# "platformStatusMessage",
# {
# "message": fields.Nested(
# full_message_schema,
# required=True,
# description="Message header",
# ),
# "platform_ID": fields.Integer(
# required=True,
# description="unique identifier for platform",
# example=1,
# ),
# "active": fields.Boolean(
# required=False,
# description="When a platform is in deployment (executing a"
# + " mission plan) this should be True",
# example=True,
# ),
# "platform_state": fields.String(
# # TODO: Define dictionary with potential STATES of each platform
# required=False,
# description="Current state executed by platform. E.g. "
# + "STOP, IDLE, ABORT.",
# example="IDLE",
# ),
# "autonomy_plan_ID": fields.Integer(
# required=False,
# description="Last mission plan ID (according to Autonomy Engine's"
# + " mission plan number) executed by platform",
# example=1,
# ),
# "mission_track_ID": fields.Integer(
# required=False,
# description=(
# "Track number - stage in mission (e.g. "
# + "4 --> Waypoint 3 to Waypoint 4)"
# ),
# example=4,
# ),
# "mission_action_ID": fields.Integer(
# required=False,
# description="to add description",
# example=1,
# ),
# "range_to_go": fields.Float(
# required=False,
# description="Estimated distance to reach next waypoint",
# example=124.3,
# ),
# "speed_over_ground": fields.Float(
# required=False,
# description="",
# example=124.3,
# ),
# "water_current_velocity": fields.Float(
# required=False,
# description="",
# example=124.3,
# ),
# "thrust_applied": fields.Float(
# required=False,
# description="TODO: Needs further consideration",
# example=124.3,
# ),
# "health_status": fields.String(
# required=False,
# description="Health status extracted by respective platform "
# + "if any diagnosis available checks on sensors",
# example="Warning",
# ),
# "gps_data": fields.List(
# fields.Nested(gps_schema), # TODO: TBD Do we want a list of
# # gps readings to allow > 1 reading i.e. platform + usbl
# required=True,
# description="Metadata pf each platform",
# ),
# "localisation_error": fields.Float(
# required=False,
# description="Localisation error at last USBL update.",
# example="",
# ),
# "usbl_fix_seconds_ago": fields.Float(
# required=False,
# description="",
# example="",
# ),
# "battery_remaining_capacity": fields.Float(
# required=True,
# description="Battery remaining capacity % provided by respective"
# + " platform/C2.",
# example=80.0,
# ),
# "sensor_config": fields.Nested(
# sensor_schema
# ), # TODO: TBD Do we want a list of sensors to allow > 1 sensor
# },
# )
# TBD: Do we append beacon positions with platform positions? # # TBD: Do we append beacon positions with platform positions?
from flask import Flask from flask import Flask
from flasgger import Swagger from flasgger import Swagger
# from . import properties
from formats.message_wrapper import message_wrapper_schema
from formats.mission_plan import mission_plan_schema
from formats.observation import observation_schema
from formats.planning_configuration import planning_configuration_schema
from formats.platform_status import platform_status_message_schema
app = Flask(__name__) app = Flask(__name__)
swagger_config = { swagger_config = {
...@@ -23,96 +30,15 @@ swagger_config = { ...@@ -23,96 +30,15 @@ swagger_config = {
], ],
"components": { "components": {
"schemas": { "schemas": {
"Message": { "Message": message_wrapper_schema,
"type": "object", "MissionPlan": mission_plan_schema,
"required": ["message_type"], "Observation": observation_schema,
"properties": { "PlanningConfiguration": planning_configuration_schema,
"message_type": { "PlatformStatus": platform_status_message_schema,
"type": "string",
},
"source": {
"type": "string",
"description": "The sender.",
"example": "autonomy-engine",
},
"destination": {
"type": "string",
"description": "Publisher topic.",
"example": "soar.noc.autosub.ah1.status",
},
"delivery_type": {
"type": "string",
"description": "Published or broadcast",
"enum": ["broadcast", "publish"],
"example": "2.0.0",
},
"message_ID": {
"type": "string",
"description": "An identifier for the type of "
+ "message received.",
"example": "PlatformStatus",
},
},
"discriminator": {
"propertyName": "message_type",
},
},
"Coordinate": {
"allOf": [{"$ref": "#/components/schemas/Message"}],
"type": "object",
"properties": {
"latitude": {
"type": "integer",
"description": "Latitude in decimal degrees.",
"example": 54.234,
},
"longitude": {
"type": "integer",
"description": "Longitude in decimal degrees.",
"example": -1.432,
},
"depth": {
"type": "integer",
"description": "Target depth",
"default": 0,
"example": 50,
},
"projection": {
"type": "integer",
"description": "EPSG Projection Code",
"example": 4326,
"default": 4326,
},
},
},
"PlatformStatus": {
"allOf": [
{"$ref": "#/components/schemas/Message"},
],
"type": "object",
"properties": {
"partner_ID": {
"type": "string",
"description": "An identifier for the partner "
+ "owning/operating the platform.",
"example": "noc",
},
"platform_ID": {
"type": "string",
"description": "An identifier for the platform.",
"example": "noc_ah1",
},
"state": {
"type": "string",
"description": "Status of platform.",
"example": "idle",
},
},
},
}, },
}, },
"paths": { "paths": {
"/messages": { "/all_messages": {
"get": { "get": {
"description": "Returns all messages from the system.", "description": "Returns all messages from the system.",
"responses": { "responses": {
...@@ -130,6 +56,22 @@ swagger_config = { ...@@ -130,6 +56,22 @@ swagger_config = {
"$ref": "#/components/" "$ref": "#/components/"
+ "schemas/PlatformStatus" + "schemas/PlatformStatus"
}, },
{
"$ref": "#/components/"
+ "schemas/MissionPlan"
},
{
"$ref": "#/components/"
+ "schemas/Observation"
},
{
"$ref": "#/components/"
+ "schemas/PlanningConfiguration"
},
{
"$ref": "#/components/"
+ "schemas/PlatformStatus"
},
], ],
"discriminator": { "discriminator": {
"propertyName": "message_type", "propertyName": "message_type",
...@@ -141,19 +83,32 @@ swagger_config = { ...@@ -141,19 +83,32 @@ swagger_config = {
}, },
} }
}, },
"/platformstatus": { },
"produces": ["application/json"],
"consumes": ["application/json"],
}
message_types = [
"Message",
"Acknowledgement",
"MissionPlan",
"Observation",
"PlanningConfiguration",
"PlatformStatus",
]
for item in message_types:
swagger_config["paths"]["/" + str(item.lower())] = {
"get": { "get": {
"description": "Returns platform status message", "description": "Returns message for " + item,
"responses": { "responses": {
"200": { "200": {
"description": "Platform status message.", "description": item + " message.",
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"allOf": [ "allOf": [
{ {
"$ref": "#/components/schemas" "$ref": "#/components/schemas"
+ "/PlatformStatus" + "/" + item,
}, },
], ],
"discriminator": { "discriminator": {
...@@ -165,11 +120,7 @@ swagger_config = { ...@@ -165,11 +120,7 @@ swagger_config = {
} }
}, },
} }
}, }
},
"produces": ["application/json"],
"consumes": ["application/json"],
}
swag = Swagger(app, config=swagger_config, merge=True) swag = Swagger(app, config=swagger_config, merge=True)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment