Commit 714a573a authored by Dan Jones's avatar Dan Jones
Browse files

Merge branch 'release-v1-0-0' into 'master'

Release v1 0 0

See merge request !35
__pycache__/ __pycache__/
*.pyc *.pyc
tests/test_swagger.json
\ No newline at end of file
...@@ -2,6 +2,7 @@ include: ...@@ -2,6 +2,7 @@ include:
- project: communications-backbone-system/backbone-infrastructure-config - project: communications-backbone-system/backbone-infrastructure-config
ref: master ref: master
file: gitlab/all.yml file: gitlab/all.yml
- local: /gitlab/test-js.yml
variables: variables:
DOCKER_IMAGE_NAME: backbone-message-format DOCKER_IMAGE_NAME: backbone-message-format
......
...@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ...@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased] ## [Unreleased]
## [v1.0.0] - 2024-08-28
### Changed
- Use discover to find all unit tests
- Run tests against current format definitions
- Test that formats match saved schema
- Run python and javascript tests in CI
- Refactor schema script to remove invalid definitions object automatically
- Refactor generate_schema_config script to output file on -f flag
## [v0.2.0] - 2024-02-06 ## [v0.2.0] - 2024-02-06
### Added ### Added
...@@ -35,6 +46,7 @@ JSON schema definitions for the SoAR project ...@@ -35,6 +46,7 @@ JSON schema definitions for the SoAR project
Example messages matching the schema for each partner Example messages matching the schema for each partner
[unreleased]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.2.0...dev [unreleased]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v1.0.0...dev
[v1.0.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.2.0...v1.0.0
[v0.2.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.1.0...v0.2.0 [v0.2.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/v0.1.0...v0.2.0
[v0.1.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/9e6ce245...v0.1.0 [v0.1.0]: https://git.noc.ac.uk/communications-backbone-system/backbone-message-format/compare/9e6ce245...v0.1.0
...@@ -72,22 +72,28 @@ eg ...@@ -72,22 +72,28 @@ eg
## Run Docs & Save Schema When Updating Message Formats ## Run Docs & Save Schema When Updating Message Formats
1. Run the command below and go to `http://127.0.0.1:5000` 1. Run the command below and go to `http://127.0.0.1:5000`
``` ```
python3 generate_schema_config.py python3 generate_schema_config.py -f
``` ```
2. Copy the schema generated `http://127.0.0.1:5000/soar_protocol.json` into the `backbone-message-format/project/<project_name>/swagger.json` 2. Commit any changes to the `backbone-message-format/project/<project_name>/swagger.json`
3. In the json copied, remove the key `"definitions"` from the schema and save this.
## Run Tests ## Run Tests
We have found slightly different behaviour between the OpenAPI validators in python and javascript.
By running tests in both languages we should protect against messages that pass validation in
one client but fail in another.
Run both tests below: Run both tests below:
1. Test 1
1. Test 1 - Use python validators
``` ```
python3 -m unittest tests/test_schemas.py python3 -m unittest discover
``` ```
2. Test 2 2. Test 2 - Use javascript validators
``` ```
cd tests-js/docker; docker compose up --build # Compile schema and run javascript validation tests in docker
python3 test-js.py
``` ```
## Quick Links ## Quick Links
......
...@@ -12,4 +12,4 @@ services: ...@@ -12,4 +12,4 @@ services:
- PYTHONDONTWRITEBYTECODE=1 - PYTHONDONTWRITEBYTECODE=1
volumes: volumes:
- ../:/app - ../:/app
command: "python3 -m unittest tests/test_schemas.py" command: "python3 -m unittest discover"
\ No newline at end of file
...@@ -14,11 +14,19 @@ from formats.alert import alert_schema ...@@ -14,11 +14,19 @@ from formats.alert import alert_schema
from flasgger import Swagger from flasgger import Swagger
from flask import Flask from flask import Flask
import argparse
import json
import os import os
app = Flask(__name__)
url_prefix = os.getenv("URL_PREFIX", "") # Enable running on domain sub-path
URL_PREFIX = os.getenv("URL_PREFIX", "")
# Allow env override of default host
FLASK_HOST = os.getenv("FLASK_HOST", "localhost")
# Allow env override of default port
FLASK_PORT = os.getenv("FLASK_PORT", 5000)
# Switch on debug mode if env var is truthy
FLASK_DEBUG = os.getenv("FLASK_DEBUG", "False").lower() in ("true", "1", "t")
swagger_config = { swagger_config = {
"openapi": "3.0.2", "openapi": "3.0.2",
...@@ -35,7 +43,7 @@ swagger_config = { ...@@ -35,7 +43,7 @@ swagger_config = {
"route": "/soar_protocol.json", "route": "/soar_protocol.json",
} }
], ],
"url_prefix": url_prefix, "url_prefix": URL_PREFIX,
"paths": {}, "paths": {},
"components": { "components": {
"schemas": { "schemas": {
...@@ -103,11 +111,125 @@ swagger_config = { ...@@ -103,11 +111,125 @@ swagger_config = {
}, },
} }
swag = Swagger(app, config=swagger_config, merge=True)
flask_host = os.getenv( def configure_flask(swagger_config):
"FLASK_HOST", "localhost" """
) # Sets to whatever FLASK_HOST is, or defaults to localhost Setup a flask app, load flasgger
and then patch to remove invalid
definitions:{} object
"""
app = Flask(__name__)
Swagger(app, config=swagger_config, merge=True)
# Replace schema route to remove invalid
# definitions: {}
# Should be fixed if Flassger 0.9.7 is released
#
# The last release of flasgger was Aug 2020
# This bug was fixed in Nov 2021
# There is a pre-release from May 2023
# Until the fix gets released we have to
# remove the invalid definitions object
# from the spec
@app.after_request
def after_request_decorator(response):
"""
I didn't want to mess with flasgger so
this blunt workaround that runs on every
route and then checks whether it's required
"""
is_response = type(response).__name__ == "Response"
is_json = is_response and response.content_type == "application/json"
if is_json:
parsed = response.json
if "definitions" in parsed:
del parsed["definitions"]
response.data = json.dumps(parsed)
return response
return app
def serve(swagger_config):
"""
Run as local flask app on FLASK_PORT|5000
"""
app = configure_flask(swagger_config)
app.run(debug=FLASK_DEBUG, host=FLASK_HOST, port=FLASK_PORT)
def compile_schema(swagger_config):
"""Extract the output schema from flasgger
The only way I have found to do this is to
use a test client to make the GET request
for the page
The function that returns the definition
can't be called outside the flask app context
"""
app = configure_flask(swagger_config)
route = swagger_config["specs"][0]["route"]
client = app.test_client()
response = client.get(route)
spec = response.json
return spec
def write_schema(swagger_config, file_path):
"""
Dump schema to specified file
"""
spec = compile_schema(swagger_config)
json_schema = json.dumps(spec, indent=2)
with open(file_path, "w") as f:
f.write(json_schema)
def get_options():
"""
Parse script arguments
"""
parser = argparse.ArgumentParser(
description="Generate the schema",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"-s",
"--serve",
dest="run_flask",
action="store_true",
help="Run flask app",
default=False,
)
parser.add_argument(
"-f",
"--file",
dest="output_file",
action="store_true",
help="Save output to schema file",
default=False,
)
parser.add_argument("filename", nargs="?", default="project/soar/swagger.json")
args = parser.parse_args()
config = vars(args)
# If no flag is specified default to running the flask server
if all(v is False for v in config.values()):
config["run_flask"] = True
return config
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=False, host=flask_host) # Parse script args
config = get_options()
# Output compiled schema
if config.get("output_file"):
write_schema(swagger_config, config.get("filename"))
# Run flask app
if config.get("run_flask"):
serve(swagger_config)
test-js:
stage: test
script:
- cd tests-js/docker
- docker compose up --build
tags:
- shell
rules:
- if: '$CI_SKIP_TESTS == "1"'
when: never
- if: '$CI_IGNORE_TESTS == "1"'
allow_failure: true
- when: on_success
{ {
"components": { "components": {
"schemas": { "schemas": {
"MESSAGE": { "MESSAGE": {
"description": "Full message definition with message-metadata in `header` and different message type schemas under `payload`", "description": "Full message definition with message-metadata in `header` and different message type schemas under `payload`",
"properties": { "properties": {
"header": { "header": {
"$ref": "#/components/schemas/header" "$ref": "#/components/schemas/header"
}, },
"payload": { "payload": {
"$ref": "#/components/schemas/payload" "$ref": "#/components/schemas/payload"
} }
}, },
"required": [ "required": [
"header", "header",
"payload" "payload"
],
"type": "object"
},
"acknowledgement": {
"properties": {
"approved": {
"description": "Human-in-the-loop approval.1 - Plan approved; 0 - Plan Rejected",
"type": "boolean"
},
"autonomy_engine_plan_ID": {
"description": "Mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform",
"example": 1,
"type": "integer"
},
"message_type": {
"description": "Type of message",
"enum": [
"acknowledgement"
], ],
"type": "object" "example": "acknowledgement",
}, "type": "string"
"acknowledgement": { },
"properties": { "platform_ID": {
"approved": { "description": "Unique identifier for this platform",
"description": "Human-in-the-loop approval.1 - Plan approved; 0 - Plan Rejected", "example": "reav-x-1",
"type": "boolean" "type": "string"
}, }
"autonomy_engine_plan_ID": { },
"description": "Mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform", "required": [
"example": 1, "message_type",
"type": "integer" "autonomy_engine_plan_ID",
}, "platform_ID",
"message_type": { "approved"
"description": "Type of message", ],
"enum": [ "type": "object"
"acknowledgement" },
], "alert": {
"example": "acknowledgement", "properties": {
"type": "string" "code": {
}, "description": "Alert code",
"platform_ID": { "example": 345,
"description": "Unique identifier for this platform", "type": "integer"
"example": "reav-x-1", },
"type": "string" "details": {
} "description": "Detailed reason for the alert ",
}, "example": "Discrepancy between rudder and actuator positions : Rudder Pos=10.31\u00b0, Steering Actuator Pos=28.65\u00b0, Discrepancy=18.33\u00b0",
"required": [ "type": "string"
"message_type", },
"autonomy_engine_plan_ID", "message_type": {
"platform_ID", "description": "Type of message",
"approved" "enum": [
"alert"
], ],
"type": "object" "example": "alert",
}, "type": "string"
"alert": { },
"properties": { "platform_ID": {
"code": { "description": "Unique identifier for this platform",
"description": "Alert code", "example": "usvdecibel",
"example": 345, "type": "string"
"type": "integer" },
}, "platform_timestamp": {
"details": { "description": "Timestamp for onboard platform status message",
"description": "Detailed reason for the alert ", "example": "2022-12-21T00:00:00Z",
"example": "Discrepancy between rudder and actuator positions : Rudder Pos=10.31\u00b0, Steering Actuator Pos=28.65\u00b0, Discrepancy=18.33\u00b0", "format": "date-time",
"type": "string" "type": "string"
}, },
"message_type": { "severity": {
"description": "Type of message", "description": "Severity level of alert",
"enum": [ "enum": [
"alert" "Emergency",
], "Alarm",
"example": "alert", "Warning",
"type": "string" "Caution"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "usvdecibel",
"type": "string"
},
"platform_timestamp": {
"description": "Timestamp for onboard platform status message",
"example": "2022-12-21T00:00:00Z",
"format": "date-time",
"type": "string"
},
"severity": {
"description": "Severity level of alert",
"enum": [
"Emergency",
"Alarm",
"Warning",
"Caution"
],
"example": "Alarm",
"type": "string"
},
"subsystem": {
"description": "System that generated the alert",
"example": "Onboard Fault Monitor",
"type": "string"
},
"summary": {
"description": "High level description of the alert",
"example": "Steering Damage - Port Side",
"type": "string"
}
},
"required": [
"message_type",
"platform_ID",
"code",
"severity"
], ],
"type": "object" "example": "Alarm",
}, "type": "string"
"header": { },
"discriminator": { "subsystem": {
"propertyName": "message_type" "description": "System that generated the alert",
}, "example": "Onboard Fault Monitor",
"properties": { "type": "string"
"delivery_type": { },
"default": "publish", "summary": {
"description": "To publish or broadcast this message.", "description": "High level description of the alert",
"enum": [ "example": "Steering Damage - Port Side",
"broadcast", "type": "string"
"publish" }
], },
"example": "publish", "required": [
"type": "string" "message_type",
}, "platform_ID",
"destination": { "code",
"description": "Publisher topic; What is the destination of this message", "severity"
"example": "ah1", ],
"type": "string" "type": "object"
}, },
"encoded": { "header": {
"description": "Indicate that message raw (encoded) or decoded. Options: encoded=true, decoded=false", "discriminator": {
"example": false, "propertyName": "message_type"
"type": "boolean" },
}, "properties": {
"message_ID": { "delivery_type": {
"description": "An identifier for the type of message received.", "default": "publish",
"example": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd", "description": "To publish or broadcast this message.",
"type": "string" "enum": [
}, "broadcast",
"source": { "publish"
"description": "The sender; Where is this message from",
"example": "autonomy_engine",
"type": "string"
},
"timestamp": {
"description": "Timestamp of message",
"example": "2022-11-16T00:00:00Z",
"format": "date-time",
"type": "string"
},
"version": {
"description": "Version of comms backbone message format protocol",
"example": 2.0,
"format": "float",
"type": "number"
}
},
"type": "object"
},
"mission_plan": {
"properties": {
"autonomy_engine_plan_ID": {
"description": "Unique identifier for this plangenerated by the Autonomy Engine",
"example": 3,
"type": "integer"
},
"emergency": {
"default": false,
"description": "To indicate if this is an emergency. true = emergency and false = no emergency",
"example": false,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"mission_plan"
],
"example": "mission_plan",
"type": "string"
},
"plan": {
"items": {
"properties": {
"action": {
"description": "Autonomy Engine's action from `move`, `payload`, `dive`, `send_hits`, `scanline`, `scanpoint`.",
"enum": [
"move",
"payload",
"dive",
"send_hits",
"scanline",
"scanpoint",
"go_home",
"surface_now",
"stop_mission",
"abort_now"
],
"example": "move",
"type": "string"
},
"activate_payload": {
"description": "To activate/deactivate sensor for Autosub Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`",
"example": true,
"type": "boolean"
},
"altitude": {
"description": "Altitude of next action",
"example": 15.0,
"format": "float",
"type": "number"
},
"depth": {
"description": "Depth of next action",
"example": 15.0,
"format": "float",
"type": "number"
},
"start_point_latitude": {
"description": "Start point, y-coordinate",
"example": 50.37072283932642,
"format": "float",
"type": "number"
},
"start_point_longitude": {
"description": "Start point, x-coordinate",
"example": -4.187143188645706,
"format": "float",
"type": "number"
},
"target_waypoint_latitude": {
"description": "Target waypoint, y-coordinate",
"example": 50.37072283932642,
"format": "float",
"type": "number"
},
"target_waypoint_longitude": {
"description": "Target waypoint, x-coordinate",
"example": -4.187143188645706,
"format": "float",
"type": "number"
},
"timeout": {
"description": "Timeout set to perform action",
"example": 1800.0,
"format": "float",
"type": "number"
}
},
"required": [
"target_waypoint_latitude",
"target_waypoint_longitude"
],
"type": "object"
},
"type": "array"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string"
}
},
"required": [
"message_type",
"autonomy_engine_plan_ID",
"platform_ID",
"plan"
], ],
"type": "object" "example": "publish",
}, "type": "string"
"mission_plan_encoded": { },
"properties": { "destination": {
"data": { "description": "Publisher topic; What is the destination of this message",
"description": "encoded string. E.g. Base64 encoded", "example": "ah1",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", "type": "string"
"type": "string" },
}, "encoded": {
"file_name": { "description": "Indicate that message raw (encoded) or decoded. Options: encoded=true, decoded=false",
"description": "Name of file", "example": false,
"example": "ah1-0238126349247372.bin", "type": "boolean"
"type": "string" },
}, "message_ID": {
"is_binary": { "description": "An identifier for the type of message received.",
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", "example": "b427003c-0000-11aa-a1eb-bvcdfghjgfdd",
"example": true, "type": "string"
"type": "boolean" },
}, "source": {
"message_type": { "description": "The sender; Where is this message from",
"description": "Type of message", "example": "autonomy_engine",
"enum": [ "type": "string"
"mission_plan_encoded" },
], "timestamp": {
"example": "mission_plan_encoded", "description": "Timestamp of message",
"type": "string" "example": "2022-11-16T00:00:00Z",
}, "format": "date-time",
"mime_type": { "type": "string"
"description": "MIME type", },
"example": "application/gzip", "version": {
"type": "string" "description": "Version of comms backbone message format protocol",
} "example": 2.0,
}, "format": "float",
"required": [ "type": "number"
"data", }
"is_binary" },
"type": "object"
},
"mission_plan": {
"properties": {
"autonomy_engine_plan_ID": {
"description": "Unique identifier for this plangenerated by the Autonomy Engine",
"example": 3,
"type": "integer"
},
"emergency": {
"default": false,
"description": "To indicate if this is an emergency. true = emergency and false = no emergency",
"example": false,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"mission_plan"
], ],
"type": "object" "example": "mission_plan",
}, "type": "string"
"observation": { },
"properties": { "plan": {
"additional_data": { "items": {
"description": "Placeholder field for any additional data", "properties": {
"example": { "action": {
"sensor_payload": false "description": "Autonomy Engine's action from `move`, `payload`, `dive`, `send_hits`, `scanline`, `scanpoint`.",
}
},
"message_type": {
"description": "Type of message",
"enum": [ "enum": [
"observation" "move",
"payload",
"dive",
"send_hits",
"scanline",
"scanpoint",
"go_home",
"surface_now",
"stop_mission",
"abort_now"
], ],
"example": "observation", "example": "move",
"type": "string"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string" "type": "string"
}, },
"points_of_interest": { "activate_payload": {
"description": "Points from features of interest identified by platform if any found.", "description": "To activate/deactivate sensor for Autosub Hover-1 --> `MBES` sensor and for EcoSUB --> `Sidescan`",
"items": {
"properties": {
"latitude": {
"description": "Identified y-coordinate of point of interest",
"example": 178.2,
"format": "float",
"type": "number"
},
"longitude": {
"description": "Identified x-coordinate of point of interest",
"example": -10.122,
"format": "float",
"type": "number"
},
"quality_of_point": {
"description": "Quality/strength of points from features of interest identified by platform.",
"example": 0.98,
"format": "float",
"type": "number"
}
},
"required": [
"latitude",
"longitude"
],
"type": "object"
},
"type": "array"
},
"region_surveyed": {
"description": "Region surveyed by given platform. GEOJSON",
"example": "",
"nullable": true
}
},
"required": [
"message_type",
"platform_ID"
],
"type": "object"
},
"observation_encoded": {
"properties": {
"data": {
"description": "encoded string. E.g. Base64 encoded",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==",
"type": "string"
},
"file_name": {
"description": "Name of file",
"example": "ah1-0238126349247372.bin",
"type": "string"
},
"is_binary": {
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.",
"example": true, "example": true,
"type": "boolean" "type": "boolean"
}, },
"message_type": { "altitude": {
"description": "Type of message", "description": "Altitude of next action",
"enum": [ "example": 15.0,
"observation_encoded"
],
"example": "observation_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
},
"required": [
"data",
"is_binary"
],
"type": "object"
},
"payload": {
"discriminator": {
"mapping": {
"acknowledgement": "#/components/schemas/acknowledgement",
"alert": "#/components/schemas/alert",
"mission_plan": "#/components/schemas/mission_plan",
"mission_plan_encoded": "#/components/schemas/mission_plan_encoded",
"observation": "#/components/schemas/observation",
"observation_encoded": "#/components/schemas/observation_encoded",
"planning_configuration": "#/components/schemas/planning_configuration",
"platform_status": "#/components/schemas/platform_status",
"platform_status_encoded": "#/components/schemas/platform_status_encoded",
"survey": "#/components/schemas/survey",
"survey_encoded": "#/components/schemas/survey_encoded"
},
"propertyName": "message_type"
},
"oneOf": [
{
"$ref": "#/components/schemas/alert"
},
{
"$ref": "#/components/schemas/acknowledgement"
},
{
"$ref": "#/components/schemas/mission_plan"
},
{
"$ref": "#/components/schemas/mission_plan_encoded"
},
{
"$ref": "#/components/schemas/observation"
},
{
"$ref": "#/components/schemas/observation_encoded"
},
{
"$ref": "#/components/schemas/planning_configuration"
},
{
"$ref": "#/components/schemas/platform_status"
},
{
"$ref": "#/components/schemas/platform_status_encoded"
},
{
"$ref": "#/components/schemas/survey"
},
{
"$ref": "#/components/schemas/survey_encoded"
}
]
},
"planning_configuration": {
"properties": {
"exclusion_zones": {
"description": "Exclusion zones for all platforms",
"items": {
"description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)",
"properties": {
"geometry_coordinates": {
"example": [
[
[
-4.1777839187560915,
50.34173405662855
],
[
-4.1777839187560915,
50.33820949229701
],
[
-4.143667777943875,
50.33820949229701
],
[
-4.143667777943875,
50.34173405662855
],
[
-4.1777839187560915,
50.34173405662855
]
]
],
"type": "array"
}
},
"type": "object"
},
"type": "array"
},
"message_type": {
"description": "Type of message",
"enum": [
"planning_configuration"
],
"example": "planning_configuration",
"type": "string"
},
"planning_config_ID": {
"description": "Unique identifier tagged to version of this configuration plan",
"example": 3,
"type": "integer"
},
"region_of_interest": {
"description": "Region of interest for the entire operation",
"items": {
"description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)",
"properties": {
"geometry_coordinates": {
"example": [
[
[
-4.1777839187560915,
50.34173405662855
],
[
-4.1777839187560915,
50.33820949229701
],
[
-4.143667777943875,
50.33820949229701
],
[
-4.143667777943875,
50.34173405662855
],
[
-4.1777839187560915,
50.34173405662855
]
]
],
"type": "array"
}
},
"type": "object"
},
"type": "array"
},
"squads": {
"items": {
"properties": {
"no_of_platforms": {
"description": "Number of platforms",
"example": 3,
"type": "integer"
},
"platforms": {
"description": "Squad consists of these platforms",
"items": {
"properties": {
"active": {
"description": "If platform is active = True, and inactive = False",
"example": true,
"type": "boolean"
},
"additional_data": {
"description": "Any addition fields/data to be added here",
"example": {
"new_sensor_a": "test_sensor",
"range": 10.0
},
"type": "object"
},
"beacon_ID": {
"description": "Unique identifier (number) for the beacon associated to this platform",
"example": 2407,
"type": "number"
},
"emergency": {
"properties": {
"safe_command": {
"description": "Command/Action that is native to respective partner's platform/C2",
"enum": [
"go_home",
"abort_now",
"stop_now",
"surface_now"
],
"example": "go_home",
"type": "string"
},
"target_depth": {
"description": "Z-coordinate safe place for respective platform . If platform to NOT stay at depth, key in `0.0`",
"example": 10.0,
"format": "float",
"type": "number"
},
"target_waypoint_latitude": {
"description": "Y-coordinate safe place for respective platform",
"example": 50.365,
"format": "float",
"type": "number"
},
"target_waypoint_longitude": {
"description": "X-coordinate safe place for respective platform",
"example": -7.432,
"format": "float",
"type": "number"
}
},
"required": [
"target_waypoint_latitude",
"target_waypoint_longitude",
"target_depth"
],
"type": "object"
},
"endurance_relative_to_water_speed": {
"properties": {
"avg_battery_rating": {
"description": "Battery endurance rating during standard operational speed usage (m/s)",
"example": 1.9,
"format": "float",
"type": "number"
},
"max_battery_rating": {
"description": "Battery endurance rating during maximum speed usage (m/s)",
"example": 1.23,
"format": "float",
"type": "number"
},
"min_battery_rating": {
"description": "Battery endurance rating during maximum speed usage (m/s)",
"example": 3.32,
"format": "float",
"type": "number"
}
},
"type": "object"
},
"max_velocity": {
"description": "Maximum velocity set for platform",
"example": 0.9,
"format": "float",
"type": "number"
},
"min_altitude": {
"description": "Minimum altitude set for platform",
"example": 15.2,
"format": "float",
"type": "number"
},
"min_velocity": {
"description": "Minimum velocity set for platform",
"example": 0.1,
"format": "float",
"type": "number"
},
"model": {
"example": "reav",
"type": "string"
},
"operator": {
"description": "Operator of platform",
"example": "noc",
"type": "string"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string"
},
"scan_sensor": {
"properties": {
"angle": {
"description": "Angle of range of swath width (in degrees)",
"example": 140.0,
"format": "float",
"type": "number"
},
"frequency": {
"description": "Frequency of scanning sensor (in kHz)",
"example": 700.0,
"format": "float",
"type": "number"
},
"sensor_type": {
"description": "Unique identifier for this platform",
"enum": [
"SIDESCAN",
"MBES"
],
"example": "MBES",
"type": "string"
},
"swath_width": {
"description": "Function of `target_altitude` for the platform's swath width (in metres)",
"example": 38.0,
"format": "float",
"type": "number"
},
"warmup_time": {
"description": "Warmup time (seconds) for sensor to start up.",
"example": 180.0,
"format": "float",
"type": "number"
}
},
"type": "object"
},
"target_altitude": {
"description": "Target altitude set for platform. This affects swath width",
"example": 15.0,
"format": "float",
"type": "number"
},
"turning_radius": {
"description": "Turning radius of platform (in metres)",
"example": 1.0,
"format": "float",
"type": "number"
}
},
"required": [
"operator",
"platform_ID",
"active",
"model"
],
"type": "object"
},
"type": "array"
},
"squad_ID": {
"description": "Identifier of given squad",
"example": 23,
"type": "integer"
},
"squad_mission_type": {
"description": "Mission of given squad: `tracking`, `survey`, `inspection`",
"enum": [
"tracking",
"survey",
"inspection"
],
"example": "survey",
"type": "string"
}
},
"required": [
"squad_ID",
"no_of_platforms",
"platforms",
"squad_mission_type"
],
"type": "object"
},
"type": "array"
}
},
"required": [
"message_type",
"planning_config_ID",
"squads",
"exclusion_zones",
"region_of_interest"
],
"type": "object"
},
"platform_status": {
"properties": {
"altitude": {
"description": "Target altitude in metres",
"example": 20.0,
"format": "float",
"type": "number"
},
"autonomy_engine_plan_ID": {
"description": "Last mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform",
"example": 1,
"type": "integer"
},
"battery_output": {
"description": "Battery output in kW",
"example": 80.2,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"battery_remaining_capacity": { "depth": {
"description": "Battery remaining % provided by respective C2", "description": "Depth of next action",
"example": 80.2, "example": 15.0,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"depth": { "start_point_latitude": {
"default": 0.0, "description": "Start point, y-coordinate",
"description": "Target depth in metres", "example": 50.37072283932642,
"example": 50.0,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"endurance": { "start_point_longitude": {
"description": "Estimate of hours of operation remaining based on present output or performance", "description": "Start point, x-coordinate",
"example": 7.4, "example": -4.187143188645706,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"fuel_remaining_capacity": { "target_waypoint_latitude": {
"description": "Percentage remaining capacity", "description": "Target waypoint, y-coordinate",
"example": 80.2, "example": 50.37072283932642,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"fuel_volume": { "target_waypoint_longitude": {
"description": "Litres of liquid fuel", "description": "Target waypoint, x-coordinate",
"example": 12.5, "example": -4.187143188645706,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"heading": { "timeout": {
"description": "Angular distance relative to north, usually 000\u00b0 at north, clockwise through 359\u00b0, in degrees", "description": "Timeout set to perform action",
"example": 124.3, "example": 1800.0,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, }
"health_status": { },
"description": "Health status where 0 is OK, 1 is platform has an ERROR", "required": [
"example": false, "target_waypoint_latitude",
"type": "boolean" "target_waypoint_longitude"
}, ],
"latitude": { "type": "object"
"description": "Latitude (Y-coordinate) in decimal degrees.", },
"type": "array"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string"
}
},
"required": [
"message_type",
"autonomy_engine_plan_ID",
"platform_ID",
"plan"
],
"type": "object"
},
"mission_plan_encoded": {
"properties": {
"data": {
"description": "encoded string. E.g. Base64 encoded",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==",
"type": "string"
},
"file_name": {
"description": "Name of file",
"example": "ah1-0238126349247372.bin",
"type": "string"
},
"is_binary": {
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.",
"example": true,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"mission_plan_encoded"
],
"example": "mission_plan_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
},
"required": [
"data",
"is_binary"
],
"type": "object"
},
"observation": {
"properties": {
"additional_data": {
"description": "Placeholder field for any additional data",
"example": {
"sensor_payload": false
}
},
"message_type": {
"description": "Type of message",
"enum": [
"observation"
],
"example": "observation",
"type": "string"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string"
},
"points_of_interest": {
"description": "Points from features of interest identified by platform if any found.",
"items": {
"properties": {
"latitude": {
"description": "Identified y-coordinate of point of interest",
"example": 178.2, "example": 178.2,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"localisation_east_error": { "longitude": {
"description": "Difference in EAST between deadreckoningand USBL update.", "description": "Identified x-coordinate of point of interest",
"example": 0.000129, "example": -10.122,
"format": "float",
"type": "number"
},
"localisation_north_error": {
"description": "Difference in NORTH between deadreckoning and USBL update.",
"example": 0.000129,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, },
"longitude": { "quality_of_point": {
"description": "Longitude (X-coordinate) in decimal degrees.", "description": "Quality/strength of points from features of interest identified by platform.",
"example": -10.122, "example": 0.98,
"format": "float", "format": "float",
"type": "number" "type": "number"
}, }
"message_type": { },
"description": "Type of message", "required": [
"enum": [ "latitude",
"platform_status" "longitude"
],
"type": "object"
},
"type": "array"
},
"region_surveyed": {
"description": "Region surveyed by given platform. GEOJSON",
"example": "",
"nullable": true
}
},
"required": [
"message_type",
"platform_ID"
],
"type": "object"
},
"observation_encoded": {
"properties": {
"data": {
"description": "encoded string. E.g. Base64 encoded",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==",
"type": "string"
},
"file_name": {
"description": "Name of file",
"example": "ah1-0238126349247372.bin",
"type": "string"
},
"is_binary": {
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.",
"example": true,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"observation_encoded"
],
"example": "observation_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
},
"required": [
"data",
"is_binary"
],
"type": "object"
},
"payload": {
"discriminator": {
"mapping": {
"acknowledgement": "#/components/schemas/acknowledgement",
"alert": "#/components/schemas/alert",
"mission_plan": "#/components/schemas/mission_plan",
"mission_plan_encoded": "#/components/schemas/mission_plan_encoded",
"observation": "#/components/schemas/observation",
"observation_encoded": "#/components/schemas/observation_encoded",
"planning_configuration": "#/components/schemas/planning_configuration",
"platform_status": "#/components/schemas/platform_status",
"platform_status_encoded": "#/components/schemas/platform_status_encoded",
"survey": "#/components/schemas/survey",
"survey_encoded": "#/components/schemas/survey_encoded"
},
"propertyName": "message_type"
},
"oneOf": [
{
"$ref": "#/components/schemas/alert"
},
{
"$ref": "#/components/schemas/acknowledgement"
},
{
"$ref": "#/components/schemas/mission_plan"
},
{
"$ref": "#/components/schemas/mission_plan_encoded"
},
{
"$ref": "#/components/schemas/observation"
},
{
"$ref": "#/components/schemas/observation_encoded"
},
{
"$ref": "#/components/schemas/planning_configuration"
},
{
"$ref": "#/components/schemas/platform_status"
},
{
"$ref": "#/components/schemas/platform_status_encoded"
},
{
"$ref": "#/components/schemas/survey"
},
{
"$ref": "#/components/schemas/survey_encoded"
}
]
},
"planning_configuration": {
"properties": {
"exclusion_zones": {
"description": "Exclusion zones for all platforms",
"items": {
"description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)",
"properties": {
"geometry_coordinates": {
"example": [
[
[
-4.1777839187560915,
50.34173405662855
],
[
-4.1777839187560915,
50.33820949229701
],
[
-4.143667777943875,
50.33820949229701
],
[
-4.143667777943875,
50.34173405662855
],
[
-4.1777839187560915,
50.34173405662855
]
]
], ],
"example": "platform_status", "type": "array"
"type": "string" }
}, },
"mission_plan_ID": { "type": "object"
"description": "Mission plan ID according to platform-C2 system", },
"example": 1, "type": "array"
"type": "integer" },
}, "message_type": {
"mission_track_ID": { "description": "Type of message",
"description": "Track number - stage in mission (e.g. 4 --> Waypoint 3 to Waypoint 4)", "enum": [
"example": 4, "planning_configuration"
],
"example": "planning_configuration",
"type": "string"
},
"planning_config_ID": {
"description": "Unique identifier tagged to version of this configuration plan",
"example": 3,
"type": "integer"
},
"region_of_interest": {
"description": "Region of interest for the entire operation",
"items": {
"description": "Using GEOJSON, exact 4-point region (rectangle shaped - 5 points)",
"properties": {
"geometry_coordinates": {
"example": [
[
[
-4.1777839187560915,
50.34173405662855
],
[
-4.1777839187560915,
50.33820949229701
],
[
-4.143667777943875,
50.33820949229701
],
[
-4.143667777943875,
50.34173405662855
],
[
-4.1777839187560915,
50.34173405662855
]
]
],
"type": "array"
}
},
"type": "object"
},
"type": "array"
},
"squads": {
"items": {
"properties": {
"no_of_platforms": {
"description": "Number of platforms",
"example": 3,
"type": "integer" "type": "integer"
}, },
"platform_ID": { "platforms": {
"description": "Unique identifier for this platform", "description": "Squad consists of these platforms",
"example": "reav-x-1", "items": {
"type": "string" "properties": {
}, "active": {
"platform_state": { "description": "If platform is active = True, and inactive = False",
"description": "Current state executed by platform. E.g. STOP, IDLE, ABORT.", "example": true,
"example": "ABORT", "type": "boolean"
"type": "string" },
}, "additional_data": {
"platform_timestamp": {
"description": "Timestamp for onboard platform status message",
"example": "2022-12-21T00:00:00Z",
"format": "date-time",
"type": "string"
},
"range_to_go": {
"description": "Estimated distance to reach next waypoint",
"example": 124.3,
"format": "float",
"type": "number"
},
"sensor_config": {
"description": "Scanning sensor on platform available to be controlled by the Autonomy Engine",
"properties": {
"additional_data": {
"description": "Any addition fields/data to be added here", "description": "Any addition fields/data to be added here",
"example": { "example": {
"payload": [ "new_sensor_a": "test_sensor",
1.2, "range": 10.0
434
]
}, },
"type": "object" "type": "object"
}, },
"sensor_on": { "beacon_ID": {
"description": "Sensor switched on (true) or off (false)", "description": "Unique identifier (number) for the beacon associated to this platform",
"example": true, "example": 2407,
"type": "boolean" "type": "number"
}, },
"sensor_serial": { "emergency": {
"description": "serial number of sensor", "properties": {
"example": "mbes-002a", "safe_command": {
"description": "Command/Action that is native to respective partner's platform/C2",
"enum": [
"go_home",
"abort_now",
"stop_now",
"surface_now"
],
"example": "go_home",
"type": "string"
},
"target_depth": {
"description": "Z-coordinate safe place for respective platform . If platform to NOT stay at depth, key in `0.0`",
"example": 10.0,
"format": "float",
"type": "number"
},
"target_waypoint_latitude": {
"description": "Y-coordinate safe place for respective platform",
"example": 50.365,
"format": "float",
"type": "number"
},
"target_waypoint_longitude": {
"description": "X-coordinate safe place for respective platform",
"example": -7.432,
"format": "float",
"type": "number"
}
},
"required": [
"target_waypoint_latitude",
"target_waypoint_longitude",
"target_depth"
],
"type": "object"
},
"endurance_relative_to_water_speed": {
"properties": {
"avg_battery_rating": {
"description": "Battery endurance rating during standard operational speed usage (m/s)",
"example": 1.9,
"format": "float",
"type": "number"
},
"max_battery_rating": {
"description": "Battery endurance rating during maximum speed usage (m/s)",
"example": 1.23,
"format": "float",
"type": "number"
},
"min_battery_rating": {
"description": "Battery endurance rating during maximum speed usage (m/s)",
"example": 3.32,
"format": "float",
"type": "number"
}
},
"type": "object"
},
"max_velocity": {
"description": "Maximum velocity set for platform",
"example": 0.9,
"format": "float",
"type": "number"
},
"min_altitude": {
"description": "Minimum altitude set for platform",
"example": 15.2,
"format": "float",
"type": "number"
},
"min_velocity": {
"description": "Minimum velocity set for platform",
"example": 0.1,
"format": "float",
"type": "number"
},
"model": {
"example": "reav",
"type": "string"
},
"operator": {
"description": "Operator of platform",
"example": "noc",
"type": "string" "type": "string"
} },
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string"
},
"scan_sensor": {
"properties": {
"angle": {
"description": "Angle of range of swath width (in degrees)",
"example": 140.0,
"format": "float",
"type": "number"
},
"frequency": {
"description": "Frequency of scanning sensor (in kHz)",
"example": 700.0,
"format": "float",
"type": "number"
},
"sensor_type": {
"description": "Unique identifier for this platform",
"enum": [
"SIDESCAN",
"MBES"
],
"example": "MBES",
"type": "string"
},
"swath_width": {
"description": "Function of `target_altitude` for the platform's swath width (in metres)",
"example": 38.0,
"format": "float",
"type": "number"
},
"warmup_time": {
"description": "Warmup time (seconds) for sensor to start up.",
"example": 180.0,
"format": "float",
"type": "number"
}
},
"type": "object"
},
"target_altitude": {
"description": "Target altitude set for platform. This affects swath width",
"example": 15.0,
"format": "float",
"type": "number"
},
"turning_radius": {
"description": "Turning radius of platform (in metres)",
"example": 1.0,
"format": "float",
"type": "number"
}
},
"required": [
"operator",
"platform_ID",
"active",
"model"
],
"type": "object"
}, },
"type": "object" "type": "array"
}, },
"speed_over_ground": { "squad_ID": {
"description": "Speed over ground", "description": "Identifier of given squad",
"example": 124.3, "example": 23,
"format": "float", "type": "integer"
"type": "number" },
}, "squad_mission_type": {
"status_source": { "description": "Mission of given squad: `tracking`, `survey`, `inspection`",
"description": "Indicate if this status message is from the platform or USBL",
"enum": [
"usbl",
"onboard_platform"
],
"example": "usbl",
"type": "string"
},
"thrust_applied": {
"description": "Thrust applied",
"example": 124.3,
"format": "float",
"type": "number"
},
"transmission_mode": {
"description": "Mode in which status message was transmitted when on the surface (e.g. iridium/wifi) or underwater (e.g. acoustics)",
"enum": [ "enum": [
"acoustics", "tracking",
"iridium", "survey",
"wifi", "inspection"
"starlink"
], ],
"example": "wifi", "example": "survey",
"type": "string"
},
"usbl_fix_seconds_ago": {
"description": "USBL Fix received x second ago.",
"example": 10.0,
"format": "float",
"type": "number"
},
"water_current_velocity": {
"description": "Water current magnitude and direction",
"example": "124.3NE",
"type": "string" "type": "string"
} }
},
"required": [
"squad_ID",
"no_of_platforms",
"platforms",
"squad_mission_type"
],
"type": "object"
}, },
"required": [ "type": "array"
"message_type", }
"platform_ID", },
"status_source", "required": [
"platform_timestamp", "message_type",
"latitude", "planning_config_ID",
"longitude" "squads",
"exclusion_zones",
"region_of_interest"
],
"type": "object"
},
"platform_status": {
"properties": {
"altitude": {
"description": "Target altitude in metres",
"example": 20.0,
"format": "float",
"type": "number"
},
"autonomy_engine_plan_ID": {
"description": "Last mission plan ID (according to Autonomy Engine's mission plan number sent) executed by platform",
"example": 1,
"type": "integer"
},
"battery_output": {
"description": "Battery output in kW",
"example": 80.2,
"format": "float",
"type": "number"
},
"battery_remaining_capacity": {
"description": "Battery remaining % provided by respective C2",
"example": 80.2,
"format": "float",
"type": "number"
},
"depth": {
"default": 0.0,
"description": "Target depth in metres",
"example": 50.0,
"format": "float",
"type": "number"
},
"endurance": {
"description": "Estimate of hours of operation remaining based on present output or performance",
"example": 7.4,
"format": "float",
"type": "number"
},
"fuel_remaining_capacity": {
"description": "Percentage remaining capacity",
"example": 80.2,
"format": "float",
"type": "number"
},
"fuel_volume": {
"description": "Litres of liquid fuel",
"example": 12.5,
"format": "float",
"type": "number"
},
"heading": {
"description": "Angular distance relative to north, usually 000\u00b0 at north, clockwise through 359\u00b0, in degrees",
"example": 124.3,
"format": "float",
"type": "number"
},
"health_status": {
"description": "Health status where 0 is OK, 1 is platform has an ERROR",
"example": false,
"type": "boolean"
},
"latitude": {
"description": "Latitude (Y-coordinate) in decimal degrees.",
"example": 178.2,
"format": "float",
"type": "number"
},
"localisation_east_error": {
"description": "Difference in EAST between deadreckoningand USBL update.",
"example": 0.000129,
"format": "float",
"type": "number"
},
"localisation_north_error": {
"description": "Difference in NORTH between deadreckoning and USBL update.",
"example": 0.000129,
"format": "float",
"type": "number"
},
"longitude": {
"description": "Longitude (X-coordinate) in decimal degrees.",
"example": -10.122,
"format": "float",
"type": "number"
},
"message_type": {
"description": "Type of message",
"enum": [
"platform_status"
], ],
"type": "object" "example": "platform_status",
}, "type": "string"
"platform_status_encoded": { },
"mission_plan_ID": {
"description": "Mission plan ID according to platform-C2 system",
"example": 1,
"type": "integer"
},
"mission_track_ID": {
"description": "Track number - stage in mission (e.g. 4 --> Waypoint 3 to Waypoint 4)",
"example": 4,
"type": "integer"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "reav-x-1",
"type": "string"
},
"platform_state": {
"description": "Current state executed by platform. E.g. STOP, IDLE, ABORT.",
"example": "ABORT",
"type": "string"
},
"platform_timestamp": {
"description": "Timestamp for onboard platform status message",
"example": "2022-12-21T00:00:00Z",
"format": "date-time",
"type": "string"
},
"range_to_go": {
"description": "Estimated distance to reach next waypoint",
"example": 124.3,
"format": "float",
"type": "number"
},
"sensor_config": {
"description": "Scanning sensor on platform available to be controlled by the Autonomy Engine",
"properties": { "properties": {
"data": { "additional_data": {
"description": "encoded string. E.g. Base64 encoded", "description": "Any addition fields/data to be added here",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", "example": {
"type": "string" "payload": [
}, 1.2,
"file_name": { 434
"description": "Name of file", ]
"example": "ah1-0238126349247372.bin", },
"type": "string" "type": "object"
}, },
"is_binary": { "sensor_on": {
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", "description": "Sensor switched on (true) or off (false)",
"example": true, "example": true,
"type": "boolean" "type": "boolean"
}, },
"message_type": { "sensor_serial": {
"description": "Type of message", "description": "serial number of sensor",
"enum": [ "example": "mbes-002a",
"platform_status_encoded" "type": "string"
], }
"example": "platform_status_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
}, },
"required": [
"data",
"is_binary"
],
"type": "object" "type": "object"
}, },
"survey": { "speed_over_ground": {
"properties": { "description": "Speed over ground",
"latitude_A": { "example": 124.3,
"description": "Latitude of point A(intersection of normal)from waypoint A to survey line", "format": "float",
"example": 178.2, "type": "number"
"format": "float", },
"type": "number" "status_source": {
}, "description": "Indicate if this status message is from the platform or USBL",
"latitude_B": { "enum": [
"description": "Latitude of point B(intersection of normal)from waypoint B to survey line", "usbl",
"example": 178.2, "onboard_platform"
"format": "float",
"type": "number"
},
"latitude_C": {
"description": "Latitude of point C(intersection of normal)from waypoint C to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"latitude_D": {
"description": "Latitude of point D(intersection of normal)from waypoint D to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"latitude_E": {
"description": "Latitude of point E(intersection of normal)from waypoint E to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"longitude_A": {
"description": "Longitude of point A(intersection of normal)from waypoint A to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_B": {
"description": "Longitude of point B(intersection of normal)from waypoint B to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_C": {
"description": "Longitude of point C(intersection of normal)from waypoint C to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_D": {
"description": "Longitude of point D(intersection of normal)from waypoint D to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_E": {
"description": "Longitude of point E(intersection of normal)from waypoint E to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"message_type": {
"description": "Type of message",
"enum": [
"survey"
],
"example": "survey",
"type": "string"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "ecosub-2",
"type": "string"
},
"timestamp": {
"description": "Timestamp for onboard message",
"example": "2022-12-21T00:00:00Z",
"format": "date-time",
"type": "string"
},
"track_ID": {
"description": "Track number of action(s) currently executed by platform",
"example": 1,
"type": "integer"
}
},
"required": [
"latitude_A",
"longitude_A",
"latitude_B",
"longitude_B",
"platform_ID"
], ],
"type": "object" "example": "usbl",
}, "type": "string"
"survey_encoded": { },
"properties": { "thrust_applied": {
"data": { "description": "Thrust applied",
"description": "encoded string. E.g. Base64 encoded", "example": 124.3,
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==", "format": "float",
"type": "string" "type": "number"
}, },
"file_name": { "transmission_mode": {
"description": "Name of file", "description": "Mode in which status message was transmitted when on the surface (e.g. iridium/wifi) or underwater (e.g. acoustics)",
"example": "ah1-0238126349247372.bin", "enum": [
"type": "string" "acoustics",
}, "iridium",
"is_binary": { "wifi",
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.", "starlink"
"example": true,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"survey_encoded"
],
"example": "survey_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
},
"required": [
"data",
"is_binary"
], ],
"type": "object" "example": "wifi",
} "type": "string"
},
"usbl_fix_seconds_ago": {
"description": "USBL Fix received x second ago.",
"example": 10.0,
"format": "float",
"type": "number"
},
"water_current_velocity": {
"description": "Water current magnitude and direction",
"example": "124.3NE",
"type": "string"
}
},
"required": [
"message_type",
"platform_ID",
"status_source",
"platform_timestamp",
"latitude",
"longitude"
],
"type": "object"
},
"platform_status_encoded": {
"properties": {
"data": {
"description": "encoded string. E.g. Base64 encoded",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==",
"type": "string"
},
"file_name": {
"description": "Name of file",
"example": "ah1-0238126349247372.bin",
"type": "string"
},
"is_binary": {
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.",
"example": true,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"platform_status_encoded"
],
"example": "platform_status_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
},
"required": [
"data",
"is_binary"
],
"type": "object"
},
"survey": {
"properties": {
"latitude_A": {
"description": "Latitude of point A(intersection of normal)from waypoint A to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"latitude_B": {
"description": "Latitude of point B(intersection of normal)from waypoint B to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"latitude_C": {
"description": "Latitude of point C(intersection of normal)from waypoint C to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"latitude_D": {
"description": "Latitude of point D(intersection of normal)from waypoint D to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"latitude_E": {
"description": "Latitude of point E(intersection of normal)from waypoint E to survey line",
"example": 178.2,
"format": "float",
"type": "number"
},
"longitude_A": {
"description": "Longitude of point A(intersection of normal)from waypoint A to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_B": {
"description": "Longitude of point B(intersection of normal)from waypoint B to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_C": {
"description": "Longitude of point C(intersection of normal)from waypoint C to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_D": {
"description": "Longitude of point D(intersection of normal)from waypoint D to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"longitude_E": {
"description": "Longitude of point E(intersection of normal)from waypoint E to survey line",
"example": -10.122,
"format": "float",
"type": "number"
},
"message_type": {
"description": "Type of message",
"enum": [
"survey"
],
"example": "survey",
"type": "string"
},
"platform_ID": {
"description": "Unique identifier for this platform",
"example": "ecosub-2",
"type": "string"
},
"timestamp": {
"description": "Timestamp for onboard message",
"example": "2022-12-21T00:00:00Z",
"format": "date-time",
"type": "string"
},
"track_ID": {
"description": "Track number of action(s) currently executed by platform",
"example": 1,
"type": "integer"
}
},
"required": [
"latitude_A",
"longitude_A",
"latitude_B",
"longitude_B",
"platform_ID"
],
"type": "object"
},
"survey_encoded": {
"properties": {
"data": {
"description": "encoded string. E.g. Base64 encoded",
"example": "SDQke4uwyP/YQQAgAhA2AND/nu8nvQAAAAAAAAAACtejPa5HHUGkcBAAAAIAAAAQAAAAAAAAAA9P2cP166ab+9cg==",
"type": "string"
},
"file_name": {
"description": "Name of file",
"example": "ah1-0238126349247372.bin",
"type": "string"
},
"is_binary": {
"description": "true if the data field contains binary format data encoded as base64. false if the data field contains ascii content such as NMEA.",
"example": true,
"type": "boolean"
},
"message_type": {
"description": "Type of message",
"enum": [
"survey_encoded"
],
"example": "survey_encoded",
"type": "string"
},
"mime_type": {
"description": "MIME type",
"example": "application/gzip",
"type": "string"
}
},
"required": [
"data",
"is_binary"
],
"type": "object"
} }
}, }
"info":{ },
"description":"SoAR message protocol in schemas", "info": {
"title":"SoAR Backbone Message Formats", "description": "SoAR message protocol in schemas",
"version":"0.2" "title": "SoAR Backbone Message Formats",
}, "version": "1.0"
"openapi": "3.0.2", },
"paths": {} "openapi": "3.0.2",
"paths": {}
} }
\ No newline at end of file
...@@ -2,7 +2,7 @@ from setuptools import setup ...@@ -2,7 +2,7 @@ from setuptools import setup
setup( setup(
version="0.2.0", version="1.0.0",
name="backbone_message_format", name="backbone_message_format",
python_requires=">=3.8", python_requires=">=3.8",
packages=["backbone_formats", "soar_schema"], packages=["backbone_formats", "soar_schema"],
......
FROM node:18.7.0-alpine FROM python:3.9.16-alpine
# Add node for running JS validator tests
RUN apk add --update nodejs npm
WORKDIR /app/tests-js WORKDIR /app/tests-js
COPY tests-js/package.json /app/tests-js/package.json COPY tests-js/package.json /app/tests-js/package.json
RUN npm install -g yarn
RUN yarn install RUN yarn install
WORKDIR /app WORKDIR /app
COPY . /app COPY . /app
# compile test schema from message
RUN pip install -r requirements.txt
RUN python generate_schema_config.py -f tests/test_swagger.json
WORKDIR /app/tests-js WORKDIR /app/tests-js
CMD [ 'yarn', 'test' ] CMD [ 'yarn', 'test' ]
...@@ -3,7 +3,7 @@ const Validator = require('swagger-model-validator'); ...@@ -3,7 +3,7 @@ const Validator = require('swagger-model-validator');
const OpenAPISchemaValidator = require('openapi-schema-validator').default; const OpenAPISchemaValidator = require('openapi-schema-validator').default;
const getSchema = () => { const getSchema = () => {
const schema = require(`${__dirname}/../project/soar/swagger.json`); const schema = require(`${__dirname}/../tests/test_swagger.json`);
return schema; return schema;
}; };
......
...@@ -5,12 +5,28 @@ from jsonschema.validators import RefResolver ...@@ -5,12 +5,28 @@ from jsonschema.validators import RefResolver
import unittest import unittest
import json import json
import os import os
from generate_schema_config import write_schema, swagger_config
SCHEMA_DIR = "project/soar/swagger.json"
MOCK_DATA_DIR = "examples/" MOCK_DATA_DIR = "examples/"
class SchemaTestCase(unittest.TestCase):
def load_schema(self, file_path=None):
if file_path:
schema_path = file_path
else:
schema_path = os.getenv("SCHEMA_PATH")
schema, _ = read_from_filename(schema_path)
return schema
@classmethod
def setUpClass(cls):
test_schema_path = "tests/test_swagger.json"
os.environ["SCHEMA_PATH"] = test_schema_path
write_schema(swagger_config, test_schema_path)
class SchemaError(Exception): class SchemaError(Exception):
""" """
Test config specs of swagger.json for projects Test config specs of swagger.json for projects
...@@ -19,26 +35,37 @@ class SchemaError(Exception): ...@@ -19,26 +35,37 @@ class SchemaError(Exception):
def __init__(self, exception_type, message): def __init__(self, exception_type, message):
self.type = exception_type self.type = exception_type
self.message = message self.message = message
super().__init__(self.message) super().__init__(message)
class TestSpecs(unittest.TestCase): class TestCompiledSoarSpec(SchemaTestCase):
"""
Test the saved version of the compiled schema has been updated
"""
def test_compiled_spec_matches_definitions(self):
print("TEST: definitions match project/soar/swagger.json")
derived_schema = self.load_schema()
saved_schema = self.load_schema("project/soar/swagger.json")
self.assertEqual(saved_schema, derived_schema)
class TestSpecs(SchemaTestCase):
def test_swagger_specs(self): def test_swagger_specs(self):
""" """
Test specs (swagger.json generated from generate_schema_config.py) Test specs (swagger.json generated from generate_schema_config.py)
""" """
schema, spec_url = read_from_filename("tests/fixtures/swagger.json") print("TEST: compiled schema matches openapi v3 spec")
schema = self.load_schema()
self.assertIsNone(openapi_v30_spec_validator.validate(schema)) self.assertIsNone(openapi_v30_spec_validator.validate(schema))
class TestAllMessageExamples(unittest.TestCase): class TestAllMessageExamples(SchemaTestCase):
def test_schema_specs(self): def test_schema_specs(self):
""" """
Test specs (swagger.json generated from generate_schema_config.py) Test specs (swagger.json generated from generate_schema_config.py)
""" """
schema_ref = open(SCHEMA_DIR) schema = self.load_schema()
schema = json.load(schema_ref)
schema_ref.close()
partner_dir_list = os.listdir(MOCK_DATA_DIR) partner_dir_list = os.listdir(MOCK_DATA_DIR)
...@@ -70,8 +97,6 @@ class TestAllMessageExamples(unittest.TestCase): ...@@ -70,8 +97,6 @@ class TestAllMessageExamples(unittest.TestCase):
f.close() f.close()
print("Done.") print("Done.")
schema_ref.close()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
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