Commit 1b804987 authored by Trishna Saeharaseelan's avatar Trishna Saeharaseelan
Browse files

refactor: docs and schemas

parent 4ba55ad5
# 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.
# Overview
This project repository is a collaborative workspace. It consists of all messages transferred into and out of the Communications Backbone. Message type schemas will be developed once reviewing each vehicle's data and statuses defined by each partner.
**[Reference Slides](https://planetocean15.sharepoint.com/sites/IUKSoAR/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2FIUKSoAR%2FShared%20Documents%2FTechnical%2FTechnical%20meetings%2FDiscussions%E2%80%94%20Backbone%20Message%20Formats%2Epdf&viewid=1d649f5f%2Dd30e%2D482f%2Dbd77%2D9316a0023bf9&parent=%2Fsites%2FIUKSoAR%2FShared%20Documents%2FTechnical%2FTechnical%20meetings)**
## Message Types
# Guidelines on Collaborating in this Workspace
| Type | Summary of File | Human-Readable |
|--------------------------- | ------------------------------------------------------------------------------------------------ | ----------------|
| `autonomy_configuration` |  Autonomy Engine's Configuration file (sent by each C2 if there are any changes) | Yes |
| `autonomy_mission_plan` |  Mission plan generated by Autonomy Engine per platform | Yes |
| `mission_plan` |  Encoded/serialized platform-specific mission plan **(shared filepath)** | No |
| `platform_status` |  Encoded/serialized platform-specific platform status **(shared filepath)** | No |
| `platform_status` | Decoded/de-serialized generic platform status | Yes |
| `observation` |  Encoded/serialized observation data from platform for Autonomy Engine **(shared filepath)** | No |
| `observation` |  Decoded/de-serialized observation data from platform for Autonomy Engine | Yes |
### Quick Links
1. Schema Fields Definitions
2. JSON Schema Examples
3. To view schema docs via Swagger UI, run the command below and go to `http://127.0.0.1:5000/soardocs`
```
python3 docs/generate_swagger.py
```
*Disclaimer: These are *not* endpoints. Purely for schema representation.
# Message Types
* Each message below will be treated as the `payload` that are wrapped in a `full_message_format` that includes a `message_header`.
| Type | Summary of File | Serialized/Compiled in Vehicle-Specific Format? |
| --------------------------| ---------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `planning_configuration` | Autonomy Engine's Configuration file (if c2 needs to update its respective vehicle directly) | No |
| `mission_plan` | Mission plan generated by Autonomy Engine per vehicle | No |
| `encoded_mission_plan` | Encoded/serialized vehicle-specific mission plan | Yes |
| `encoded_vehicle_status` | Encoded/serialized vehicle-specific vehicle status | Yes |
| `vehicle_status` | Decoded/de-serialized generic vehicle status | No |
| `encoded_observation` | Encoded/serialized observation data from vehicle for Autonomy Engine | Yes |
| `observation` | Decoded/de-serialized observation data from vehicle for Autonomy Engine | No |
| `acknowledgement` | Decoded/de-serialized acknowledgement message from the Hydrosurv Adapater | No |
-----------------------------------
## Breakdown of Message Sources & Types
### 1/ Autonomy Engine
#### Transmit
* mission plan _(sent per platform)_
* mission plan _(sent per vehicle??)_
* TBD:
* Will emergency commands be sent via the autonomy engine (from the GUI) or directly to the C2s?
......@@ -27,7 +49,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 observation data _(from squad 1 platforms)_
* decoded ecosub observation data _(from squad 1 vehicles)_
----------------------------------
......@@ -35,7 +57,7 @@ Consists of all messages transferred into and out of the Communications Backbone
#### Transmit
* compiled ecosub mission plan
* decoded ecosub status message
* configuration file
* autonomy configuration file
* decoded ecosub observation data
......@@ -50,7 +72,7 @@ Consists of all messages transferred into and out of the Communications Backbone
#### Transmit
* compiled autosub-hover mission plan
* decoded autosub-hover status message
* configuration file
* autonomy configuration file
#### Receive
......@@ -78,8 +100,8 @@ Consists of all messages transferred into and out of the Communications Backbone
* encoded autosub status message
* encoded ecosub observation data
* TBD:
* are there acknowledgment messages from platforms?
* how are we receiving beacon messages per platform?
* are there acknowledgment messages from vehicles?
* how are we receiving beacon messages per vehicle?
#### Receive
* compiled ecosub mission plan
......@@ -88,3 +110,39 @@ Consists of all messages transferred into and out of the Communications Backbone
* compiled ecosub emergency command
* compiled reav emergency command
* compiled autosub emergency command
------------------------------------
## Message Data Flow Summary
| Partner | Message Type | Source | Destination | Via Comms Backbone? | Contains Serialized Vehicle-Specific File? | Comment |
| --------------- | ---------------------- | ----------------- | ---------------------- | ------------------- | ------------------------------------------ | ---------------------------------------------------------------------------------------- |
| Autonomy Engine | vehicle status | C2’s | Autonomy Engine | Yes | No | |
| Autonomy Engine | Hydrosurv/Ecosub/Ah1 |
| Autonomy Engine | mission plan | Autonomy Engine | C2’s | Yes | No | |
| Autonomy Engine | Hydrosurv/Ecosub/Ah1 |
| Autonomy Engine | autonomy configuration | GUI | ALL | Yes | No | Common file sent to all partners – to store |
| Autonomy Engine | autonomy configuration | C2’s | Autonomy Engine | Yes | No | IF REQUIRED ONLY, UPDATE STORED FILE. |
| Autonomy Engine | Hydrosurv/Ecosub/Ah1 |
| Hydrosurv | vehicle status | Hydrosurv C2 | Autonomy Engine | Yes | No | |
| Hydrosurv | mission plan | Autonomy Engine | Hydrosurv C2 | Yes | No | |
| Hydrosurv | mission plan | Hydrosurv C2 | Reav-60 | No | Yes | |
| Hydrosurv | autonomy configuration | GUI | Hydrosurv C2 (ALL) | Yes | No | Common file sent to all partners – to store |
| Hydrosurv | autonomy configuration | Hydrosurv C2 | GUI | Yes | No | IF REQUIRED ONLY, UPDATE STORED FILE. |
| Hydrosurv | acknowledgment | Hydrosurv C2 | Comms Backbone (Audit) | Yes | No | When hydrosurv adapter has (a) Received, (b) Sent Plan to Reav, and (c) Executed by Reav |
| Planet Ocean | vehicle status | Ecosub | Hydrosurv Adapter | No | Yes | |
| Planet Ocean | vehicle status | Hydrosurv Adapter | Ecosub C2 | Yes | Yes | |
| Planet Ocean | vehicle status | Ecosub C2 | Autonomy Engine | Yes | No | |
| Planet Ocean | mission plan | Autonomy Engine | Ecosub C2 | Yes | No | |
| Planet Ocean | mission plan | Ecosub C2 | Hydrosurv Adapter | Yes | Yes | |
| Planet Ocean | mission plan | Hydrosurv Adapter | Ecosub | No | Yes | Via Hermes + Router |
| Planet Ocean | autonomy configuration | GUI | Ecosub C2 (ALL) | Yes | No | Common file sent to all partners – to store |
| Planet Ocean | autonomy configuration | Hydrosurv C2 | GUI | Yes | No | IF REQUIRED ONLY, UPDATE STORED FILE. |
| Planet Ocean | observation | Ecosub | Hydrosurv Adapter | No | Yes | Via Hermes + Router |
| Planet Ocean | observation | Hydrosurv Adapter | Ecosub C2 | Yes | Yes | |
| Planet Ocean | observation | Ecosub C2 | Autonomy Engine | Yes | No | |
| Planet Ocean | vehicle status | AH1 | Hydrosurv Adapter | No | Yes | Via Hermes + Router |
| NOC | vehicle status | Hydrosurv Adapter | NOC C2 | Yes | Yes | |
| NOC | vehicle status | NOC C2 | Autonomy Engine | Yes | No | |
| NOC | mission plan | Autonomy Engine | NOC C2 | Yes | No | |
| NOC | mission plan | NOC C2 | Hydrosurv Adapter | Yes | Yes | |
| NOC | mission plan | Hydrosurv Adapter | AH1 | No | Yes | Via Hermes + Router |
| NOC | autonomy configuration | GUI | NOC C2 (ALL) | Yes | No | Common file sent to all partners – to store |
| NOC | autonomy configuration | Hydrosurv C2 | GUI | Yes | No | IF REQUIRED ONLY, UPDATE STORED FILE. |
\ No newline at end of file
from flask_restx import Api, fields
from flask_restx import Api
from flask import Flask
import os
# import os
__all__ = [
"docs",
"formats",
# "parsers",
]
app = Flask(__name__)
api = Api(app)
from . import api
import os
......
......@@ -9,9 +9,10 @@ from flask import Flask, request
from flasgger import Swagger, LazyString
from flask_restx import Api, fields, Resource
app = Flask(__name__)
app = Flask(__name__)
api = Api(app)
swagger_template = dict(
info={
"title": LazyString(lambda: "SoAR Backbone Message Formats"),
......@@ -49,73 +50,57 @@ message_header_schema = api.model(
"type": fields.String(
required=True,
description="Type of message",
example="platform_status",
example="vehicle_status",
),
# "payload":# TODO: schema applicable changes according to "type"
"payload": fields.Raw(required=True) # TODO: schema applicable changes according to "type"
},
)
platform_schema = api.model(
"Platform",
full_message_schema = api.model(
"FullMessageSchema",
{
"platform_ID": fields.Integer(
required=True,
description="unique identifier for platform",
example="ah-1",
),
"serial": fields.Integer(
"uuid": fields.DateTime( # add this
required=True,
description="platform serial number",
example="ah-1",
description="Timestamp of message",
example="2022-11-16T00:00:00Z",
),
"model": fields.Integer(
"timestamp": fields.DateTime(
required=True,
description="platform serial number",
example="ah-1",
),
"active": fields.Boolean(
required=False,
description="platform in mission",
example=True,
description="Timestamp of message",
example="2022-11-16T00:00:00Z",
),
},
)
squad_metadata_schema = api.model(
"SquadMetadata",
{
"squad_ID": fields.Integer(
"source": fields.String(
required=True,
description="platform serial number",
example="ah-1",
description="Where is this message from",
example="autonomy_engine",
),
"no_of_platforms": fields.Integer(
"destination": fields.String(
required=True,
description="number of platform serial number",
description="What is the destination of this message",
example="ah-1",
),
"platforms": fields.List(
fields.Nested(platform_schema),
"encoded": fields.Boolean(
required=True,
description="Metadata pf each platform",
description="Indicate that message raw (encoded) or decoded. "
+ "Options: encoded=True, decoded=False",
example=False,
),
"squad_mission_type": fields.String(
"type": fields.String(
required=True,
description="Survey or Detail",
example="survey",
description="Type of message",
example="vehicle_status",
),
"squad_state": fields.Boolean(
"payload": fields.Raw(
required=True,
description="True if given Squad is executing mission type "
+ "according to the Autonomy Engine plan",
example=False,
description="Content of Message",
# example="{}",
),
},
}
)
constraints_schema = api.model(
"Constraints",
# TODO: Should this be per platform instead of squad?
"ConstraintsSchema",
{
"min_altitude": fields.Float(
required=True,
......@@ -135,42 +120,60 @@ constraints_schema = api.model(
},
)
environment_config_schema = api.model( # TODO: Discuss how regions are defined
"EnvironmentConfig",
vehicle_schema = api.model(
"VehicleSchema",
{
"region_of_interest": fields.String(),
"exclusion_zone": fields.String(),
"vehicle_ID": fields.Integer(
required=True,
description="unique identifier for vehicle",
example="ah-1",
),
"serial": fields.Integer(
required=True,
description="vehicle serial number",
example="ah-1",
),
"model": fields.Integer(
required=True,
description="vehicle serial number",
example="ah-1",
),
"constraints": fields.Nested(constraints_schema),
"active": fields.Boolean(
required=False,
description="When a vehicle is in deployment (executing a mission plan) this should be True",
example=True,
),
},
)
# Main Autonomy Configuration Schema
autonomy_configuration_schema = api.model(
"Configuration",
squad_metadata_schema = api.model(
"SquadMetadataSchema",
{
"message": fields.Nested(
message_header_schema,
"squad_ID": fields.Integer(
required=True,
description="Message header",
description="Identifier of given squad",
example="ah-1",
),
"ID": fields.Integer(
"no_of_vehicles": fields.Integer(
required=True,
description="Unique identifier tagged to version of this"
+ " configuration plan",
example=3,
description="number of vehicles",
example="ah-1",
),
"time": fields.String(
"vehicles": fields.List(
fields.Nested(vehicle_schema),
required=True,
description="",
example="",
description="Squad consists of these vehicles",
),
"squads": fields.Nested(
squad_metadata_schema,
required=False,
description="Details of each squad",
"squad_mission_type": fields.String(
required=True,
description="Mission of given squad: `tracking`, `survey`, `inspection`",
example="survey",
),
"environment": fields.Nested(
environment_config_schema,
required=False,
description="Region of interest and exclusion zone",
"squad_state": fields.Boolean(
required=True,
description="In execution, Waiting.. <define further>",
example=False,
),
},
)
......@@ -180,7 +183,7 @@ gps_schema = api.model(
{
"gps_source": fields.Float( # TODO: TBD with partners
required=False,
description="Source of gps position. E.g. Beacon",
description="Source of gps position. E.g. USBL (external), vehicle itself (internal)",
example="internal",
),
"latitude_type": fields.String(
......@@ -195,26 +198,32 @@ gps_schema = api.model(
),
"latitude": fields.Float(
required=False,
description="Latitude in <insert units>",
description="Latitude in <DEFINE UNITS>",
example="",
),
"longitude": fields.Float(
required=False,
description="Longitude in <insert units>",
description="Longitude in <DEFINE UNITS>",
example="",
),
"depth": fields.Float(
required=False,
description="",
description="Depth in <DEFINE UNITS>",
example="",
),
"gps_fix_seconds_ago": fields.Float(
"altitude": fields.Float(
required=False,
description="",
description="Altitude in <DEFINE UNITS>",
example="",
),
# "gps_fix_seconds_ago": fields.Float(
# required=False,
# description="",
# example="",
# ),
},
)
observation_schema = api.model(
"Observation",
{
......@@ -223,45 +232,77 @@ observation_schema = api.model(
required=True,
description="Message header",
),
"platform": fields.Nested(platform_schema),
"vehicle": fields.Nested(vehicle_schema),
"time": fields.String(
required=True,
description="Timestamp of message",
example="2022-11-16T00:00:00Z",
),
"discuss_other_fields": fields.String(
# "observation_type" ==> payloads tied to different types maybe?
# properties of each observation?
"points_of_interest": fields.Float(
required=False,
description="", # we can track the version of the AE plan?
description="Points from features of interest identified by vehicle if any found. DEFINE FORMAT.",
example="",
),
"region_surveyed": fields.Float(
required=False,
description="Region surveyed by given vehicle. DEFINE FORMAT. GEOJSON?",
example="",
),
"quality_of_points": fields.Float(
required=False,
description="Quality/strength of points from features of interest identified by vehicle. DEFINE FORMAT.",
example=0.98,
),
"additional_data": fields.Raw(
required=False,
description="Placeholder field for any additional data",
example={"sensor_payload": False},
),
}
)
platform_status_message_schema = api.model(
"PlatformStatusMessage",
sensor_schema = api.model(
"Sensor",
{
"sensor_name": fields.String(
required=False,
description="Name of sensor (e.g. MBES for AH1 and SideScan for Ecosub",
example="MBES",
),
"sensor_status": fields.Boolean(
required=False,
description="Sensor switched on (True) or off (False)",
example=True,
),
"additional_data": fields.Raw(
required=False,
description="Add any additional sensor-related data here.",
example={"sensor_loadtime_seconds": 30.0},
),
}
)
vehicle_status_message_schema = api.model(
"VehicleStatusMessage",
{
"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(
"vehicle": fields.Nested(vehicle_schema),
"vehicle_state": fields.String(
# TODO: Define dictionary with potential STATES of each vehicle
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. "
description="Current state executed by vehicle. E.g. "
+ "STOP, IDLE, ABORT.",
example="IDLE",
),
"autonomy_mission_ID": fields.Integer(
required=False,
description="Last mission plan ID (according to Autonomy Engine's mission plan number) executed by vehicle",
example=1,
),
"mission_track_number": fields.Integer(
required=False,
description=(
......@@ -275,41 +316,81 @@ platform_status_message_schema = api.model(
description="Estimated distance to reach next waypoint",
example=124.3,
),
"c2_health_status": fields.String(
"speed_over_ground": fields.Float(
required=False,
description="Health status extracted by respective platform's C2 "
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 vehicle "
+ "if any diagnosis available checks on sensors",
example="Warning",
),
"gps_data": fields.Nested(gps_schema),
"battery_voltage": fields.Float(
"gps_data": fields.List(
fields.Nested(gps_schema), # TODO: TBD Do we want a list of gps readings to allow > 1 reading i.e. vehicle + usbl
required=True,
description="Volts",
example=23.0,
description="Metadata pf each vehicle",
),
"battery_current": fields.Float(
"localisation_error": fields.Float(
required=False,
description="Amps",
example=1.2,
description="Localisation error at last USBL update.",
example="",
),
"battery_current_per_hour": fields.Float(
"usbl_fix_seconds_ago": fields.Float(
required=False,
description="Amp-Hours",
example=1.2,
description="",
example="",
),
"battery_wattage": fields.Float(
"battery_remaining_capacity": fields.Float(
required=False,
description="Watts",
example=23.0,
description="Battery remaining capacity % provided by respective C2 if any.",
example=80.0,
),
"sensor_config": fields.Nested(sensor_schema), # TODO: TBD Do we want a list of sensors to allow > 1 sensor
},
)
mission_plan_schema = api.model(
"MissionPlan",
{
"message": fields.Nested(
message_header_schema,
required=True,
description="Message header",
),
"battery_wattage_per_hour": fields.Float(
"ID": fields.Integer(
required=True,
description="Unique identifier tagged to version of this"
+ " configuration plan",
example=3,
),
"time": fields.String(
required=True,
description="",
example="",
),
"vehicle_ID": fields.Integer(
required=False,
description="Watt-Hours",
example=23.0,
description="Details of each squad",
),
"payload": fields.Raw(
required=True,
)
},
)
swagger_config = {
"headers": [],
"specs": [
......@@ -334,26 +415,115 @@ swagger_config = {
"consumes": ["application/json"],
}
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",
),
"region_of_operation": fields.Raw(
required=True,
description="Using GEOJSON, exact region of interest EXCLUDING exclusion zone",
example={
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
[
-4.187143188645706,
50.37072283932642
],
[
-4.202697005964865,
50.368816892405874
],
[
-4.203156724702808,
50.365640144076906
],
[
-4.19449868846155,
50.362267670845654
],
[
-4.187373048015246,
50.35933489062816
],
[
-4.1859938917992565,
50.36207215780212
],
[
-4.188598964651192,
50.36471151593676
],
[
-4.192506573927574,
50.367350727294706
],
[
-4.188675584440716,
50.36837704762925
],
[
-4.187143188645706,
50.37072283932642
]
]
],
"type": "Polygon"
# "projection": coordinate system used
}
}
]
}
),
},
)
swagger = Swagger(app, template=swagger_template, config=swagger_config)
ns1 = api.namespace("message", description="Message Header Format")
@ns1.route("/header")
class MessageHeader(Resource):
@ns1.response(200, "Success", message_header_schema)
ns1 = api.namespace("message", description="Message Wrapper (Full Message Schema) Format")
@ns1.route("/wrapper")
class MessageWrapper(Resource):
@ns1.response(200, "Success", full_message_schema)
def get(self):
pass
ns2 = api.namespace("platform_status", description="Platform Status Message Format")
ns2 = api.namespace("vehicle_status", description="vehicle Status Message Format")
@ns2.route("")
class PlatformStatus(Resource):
@ns2.response(200, "Success", platform_status_message_schema)
class VehicleStatus(Resource):
@ns2.response(200, "Success", vehicle_status_message_schema)
def get(self):
pass
ns3 = api.namespace(
"autonomy_configuration", description="Autonomy Configuration Format"
"autonomy_configuration", description="Autonomy Configuration Format. Do we want region of interest to be per squad, per vehicle or in the main schema?"
)
@ns3.route("")
......@@ -363,27 +533,33 @@ class AutonomyConfiguration(Resource):
pass
# @api.route('/mission-plan/<str:platform_type')
# @api.doc(params={"platform_type": "The type of platform of the mission plan to target."})
ns4 = api.namespace("mission_plan", description="Mission Plan Format Per Platform")
# @api.route('/mission-plan/<str:vehicle_type')
# @api.doc(params={"vehicle_type": "The type of vehicle of the mission plan to target."})
ns4 = api.namespace("mission_plan", description="Mission Plan Format Per vehicle")
@ns4.route("")
class MissionPlan(Resource):
@ns4.response(200, "Success", mission_plan_schema)
def get(self):
pass
@ns4.route("/ecosub")
class MissionPlanEcosub(Resource):
@ns4.response(200, "Success", {})
@ns4.response(200, "Success", mission_plan_schema)
def get(self):
pass
@ns4.route("/reav")
class MissionPlanReav(Resource):
@ns4.response(200, "Success", {})
@ns4.response(200, "Success", mission_plan_schema)
def get(self):
pass
@ns4.route("/autosubhover")
class MissionPlanAutosubHover(Resource):
@ns4.response(200, "Success", {})
@ns4.response(200, "Success", mission_plan_schema)
def get(self):
pass
ns5 = api.namespace("observation", description="Observation Format --> Per Platform or generic?")
ns5 = api.namespace("observation", description="Observation Format --> Per vehicle or generic?")
@ns5.route("")
class Observation(Resource):
@ns5.response(200, "Success", observation_schema)
......
//****************************************************************
// File: config.json (THIS FILE WILL BE IN JSON FORMAT, NOT JSONC)
// Description: This file is configured by the respective
// platform's C2 if a platform is no longer active or change
// vehicle's C2 if a vehicle is no longer active or change
// a configured value.
//
// Notes: All comments in this document will be stripped out
......@@ -24,24 +24,24 @@
}
},
"survey_team": {
"no_of_platforms": 3, // int
"platforms": [
"no_of_vehicles": 3, // int
"vehicles": [
{
"id": 1, // int
"platform": "ecosub", // str
"platform_serial": "eco101", // str
"vehicle": "ecosub", // str
"vehicle_serial": "eco101", // str
"active": true // bool
},
{
"id": 2,
"platform": "ecosub", // str
"platform_serial": "eco-102", // str
"vehicle": "ecosub", // str
"vehicle_serial": "eco-102", // str
"active": true // bool
},
{
"id": 3, // int
"platform": "ecosub", // str
"platform_serial": "ecosub3", // str
"vehicle": "ecosub", // str
"vehicle_serial": "ecosub3", // str
"active": true // bool
}
],
......@@ -51,12 +51,12 @@
},
"detail_team": {
"execute_stage": true, // bool
"no_of_platforms": 1, // int
"platforms": [
"no_of_vehicles": 1, // int
"vehicles": [
{
"id": 1, // int
"platform": "autosub-hover", // str
"platform_serial": "ah1", // str
"vehicle": "autosub-hover", // str
"vehicle_serial": "ah1", // str
"active": true // bool
}
],
......
......@@ -10,14 +10,14 @@ __all__ = [
]
message_types = [
"platform_status",
"vehicle_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",
full_message_schema = api.model(
"FullMessageSchema",
{
"timestamp": fields.DateTime(
required=True,
......@@ -43,33 +43,59 @@ message_header_schema = api.model(
"type": fields.String(
required=True,
description="Type of message",
example="platform_status",
example="vehicle_status",
),
"payload": fields.Raw(
required=True,
description="Content of Message",
# example="{}",
),
}
)
constraints_schema = api.model(
"ConstraintsSchema",
{
"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,
),
# "payload":# TODO: schema applicable changes according to "type"
},
)
platform_schema = api.model(
"PlatformSchema",
vehicle_schema = api.model(
"vehicleSchema",
{
"platform_ID": fields.Integer(
"vehicle_ID": fields.Integer(
required=True,
description="unique identifier for platform",
description="unique identifier for vehicle",
example="ah-1",
),
"serial": fields.Integer(
required=True,
description="platform serial number",
description="vehicle serial number",
example="ah-1",
),
"model": fields.Integer(
required=True,
description="platform serial number",
description="vehicle serial number",
example="ah-1",
),
"constraints": fields.Nested(constraints_schema),
"active": fields.Boolean(
required=False,
description="platform in mission",
description="When a vehicle is in deployment (executing a mission plan) this should be True",
example=True,
),
},
......
"""
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",
),
},
)
"""
schemas: configuration sent to Autonomy Engine (i.e. during an emergency,
if a vehicle needs to be removed from the mission planning)
"""
from . import api, message_header_schema
from flask_restx import fields
mission_plan_schema = api.model(
"MissionPlan",
{
"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="",
),
"vehicle_ID": fields.Integer(
required=False,
description="Details of each squad",
),
"payload": fields.Raw(
required=True,
)
},
)
"""
schema: platform-specific decoded status message (DRAFT)
schema: vehicle-specific decoded status message (DRAFT)
"""
from . import message_header_schema, platform_schema, api
from . import message_header_schema, vehicle_schema, api
from flask_restx import fields
......@@ -13,16 +13,33 @@ observation_schema = api.model(
required=True,
description="Message header",
),
"platform": fields.Nested(platform_schema),
"vehicle": fields.Nested(vehicle_schema),
"time": fields.String(
required=True,
description="Timestamp of message",
example="2022-11-16T00:00:00Z",
),
"discuss_other_fields": fields.String(
# "observation_type" ==> payloads tied to different types maybe?
# properties of each observation?
"points_of_interest": fields.Float(
required=False,
description="", # we can track the version of the AE plan?
description="Points from features of interest identified by vehicle if any found. DEFINE FORMAT.",
example="",
),
"region_surveyed": fields.Float(
required=False,
description="Region surveyed by given vehicle. DEFINE FORMAT. GEOJSON?",
example="",
),
"quality_of_points": fields.Float(
required=False,
description="Quality/strength of points from features of interest identified by vehicle. DEFINE FORMAT.",
example=0.98,
),
"additional_data": fields.Raw(
required=False,
description="Placeholder field for any additional data",
example={"sensor_payload": False},
),
}
)
\ No newline at end of file
"""
schemas: configuration sent to Autonomy Engine (i.e. during an emergency,
if a platform needs to be removed from the mission planning)
if a vehicle needs to be removed from the mission planning)
"""
from . import api, message_header_schema, platform_schema
from . import api, message_header_schema, vehicle_schema
from flask_restx import fields
......@@ -11,62 +11,32 @@ squad_metadata_schema = api.model(
{
"squad_ID": fields.Integer(
required=True,
description="platform serial number",
description="Identifier of given squad",
example="ah-1",
),
"no_of_platforms": fields.Integer(
"no_of_vehicles": fields.Integer(
required=True,
description="number of platform serial number",
description="number of vehicles",
example="ah-1",
),
"platforms": fields.List(
fields.Nested(platform_schema),
"vehicles": fields.List(
fields.Nested(vehicle_schema),
required=True,
description="Metadata pf each platform",
description="Squad consists of these vehicles",
),
"squad_mission_type": fields.String(
required=True,
description="Survey or Detail",
description="Mission of given squad: `tracking`, `survey`, `inspection`",
example="survey",
),
"squad_state": fields.Boolean(
required=True,
description="True if given Squad is executing mission type "
+ "according to the Autonomy Engine plan",
description="In execution, Waiting.. <define further>",
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",
......@@ -92,10 +62,66 @@ autonomy_configuration_schema = api.model(
required=False,
description="Details of each squad",
),
"environment": fields.Nested(
environment_config_schema,
required=False,
description="Region of interest and exclusion zone",
"region_of_operation": fields.Raw(
required=True,
description="Using GEOJSON, exact region of interest EXCLUDING exclusion zone",
example={
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
[
-4.187143188645706,
50.37072283932642
],
[
-4.202697005964865,
50.368816892405874
],
[
-4.203156724702808,
50.365640144076906
],
[
-4.19449868846155,
50.362267670845654
],
[
-4.187373048015246,
50.35933489062816
],
[
-4.1859938917992565,
50.36207215780212
],
[
-4.188598964651192,
50.36471151593676
],
[
-4.192506573927574,
50.367350727294706
],
[
-4.188675584440716,
50.36837704762925
],
[
-4.187143188645706,
50.37072283932642
]
]
],
"type": "Polygon"
# "projection": coordinate system used
}
}
]
}
),
},
)
"""
schema: platform-specific decoded status message (DRAFT)
schema: vehicle-specific decoded status message (DRAFT)
"""
from . import message_header_schema, platform_schema, api
from . import message_header_schema, vehicle_schema, api
from flask_restx import fields
# TODO: Discuss nomenclature > platform or vehicle?
# TODO: Discuss nomenclature > vehicle 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",
description="Source of gps position. E.g. USBL (external), vehicle itself (internal)",
example="internal",
),
"latitude_type": fields.String(
......@@ -26,53 +26,109 @@ gps_schema = api.model(
),
"latitude": fields.Float(
required=False,
description="Latitude in <insert units>",
description="Latitude in <DEFINE UNITS>",
example="",
),
"longitude": fields.Float(
required=False,
description="Longitude in <insert units>",
description="Longitude in <DEFINE UNITS>",
example="",
),
"depth": fields.Float(
required=False,
description="",
description="Depth in <DEFINE UNITS>",
example="",
),
"gps_fix_seconds_ago": fields.Float(
"altitude": fields.Float(
required=False,
description="",
description="Altitude in <DEFINE UNITS>",
example="",
),
# "gps_fix_seconds_ago": fields.Float(
# required=False,
# description="",
# example="",
# ),
},
)
platform_status_message_schema = api.model(
"PlatformStatusMessage",
# battery_schema = api.model(
# "Battery",
# {
# "voltage_v": fields.Float(
# required=True,
# description="Volts",
# example=23.0,
# ),
# "current_amps": fields.Float(
# required=False,
# description="Amps",
# example=1.2,
# ),
# "current_usage_amphours": fields.Float(
# required=False,
# description="Amp-Hours",
# example=1.2,
# ),
# "wattage_w": fields.Float(
# required=False,
# description="Watts",
# example=23.0,
# ),
# "wattage_usage_watthours": fields.Float(
# required=False,
# description="Watt-Hours",
# example=23.0,
# ),
# "remaining_capacity": fields.Float(
# required=False,
# description="Battery remaining capacity % provided by respective C2 if any.",
# example=80.0,
# ),
# }
# )
sensor_schema = api.model(
"Sensor",
{
"sensor_name": fields.String(
required=False,
description="Name of sensor (e.g. MBES for AH1 and SideScan for Ecosub",
example="MBES",
),
"sensor_status": fields.Boolean(
required=False,
description="Sensor switched on (True) or off (False)",
example=True,
),
"additional_data": fields.Raw(
required=False,
description="Add any additional sensor-related data here.",
example={"sensor_loadtime_seconds": 30.0},
),
}
)
vehicle_status_message_schema = api.model(
"vehicleStatusMessage",
{
"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(
"vehicle": fields.Nested(vehicle_schema),
"vehicle_state": fields.String(
# TODO: Define dictionary with potential STATES of each vehicle
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. "
description="Current state executed by vehicle. E.g. "
+ "STOP, IDLE, ABORT.",
example="IDLE",
),
"autonomy_mission_ID": fields.Integer(
required=False,
description="Last mission plan ID (according to Autonomy Engine's mission plan number) executed by vehicle",
example=1,
),
"mission_track_number": fields.Integer(
required=False,
description=(
......@@ -86,39 +142,49 @@ platform_status_message_schema = api.model(
description="Estimated distance to reach next waypoint",
example=124.3,
),
"c2_health_status": fields.String(
"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="Health status extracted by respective platform's C2 "
description="TODO: Needs further consideration",
example=124.3,
),
"health_status": fields.String(
required=False,
description="Health status extracted by respective vehicle "
+ "if any diagnosis available checks on sensors",
example="Warning",
),
"gps_data": fields.Nested(gps_schema),
"battery_voltage": fields.Float(
"gps_data": fields.List(
fields.Nested(gps_schema), # TODO: TBD Do we want a list of gps readings to allow > 1 reading i.e. vehicle + usbl
required=True,
description="Volts",
example=23.0,
),
"battery_current": fields.Float(
required=False,
description="Amps",
example=1.2,
description="Metadata pf each vehicle",
),
"battery_current_per_hour": fields.Float(
"localisation_error": fields.Float(
required=False,
description="Amp-Hours",
example=1.2,
description="Localisation error at last USBL update.",
example="",
),
"battery_wattage": fields.Float(
"usbl_fix_seconds_ago": fields.Float(
required=False,
description="Watts",
example=23.0,
description="",
example="",
),
"battery_wattage_per_hour": fields.Float(
"battery_remaining_capacity": fields.Float(
required=False,
description="Watt-Hours",
example=23.0,
description="Battery remaining capacity % provided by respective C2 if any.",
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 vehicle positions?
"""
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?
Flask
flask-restx
flasgger
\ No newline at end of file
#!/bin/bash
firefox "$1"
python3 docs/generate_swagger.py
./urlopener "https://google.com"
\ No newline at end of file
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