""" Generate swagger to view models/schemas. Command: 1/ python3 generate_swagger.py 2/ Go to http://127.0.0.1:5000/soardocs WARNING: API Endpoints are NOT functional. Purely for easy-reading. """ from flask import Flask, request from flasgger import Swagger, LazyString from flask_restx import Api, fields, Resource app = Flask(__name__) api = Api(app) swagger_template = dict( info={ "title": LazyString(lambda: "SoAR Backbone Message Formats"), "version": LazyString(lambda: "0.1"), "description": LazyString( lambda: "Backbone Message Format component for the Squad of Autonomous Robots (SoAR) message definitions." ), }, host=LazyString(lambda: request.host), ) message_header_schema = api.model( "MessageHeader", { "timestamp": fields.DateTime( required=True, description="Timestamp of message", example="2022-11-16T00:00:00Z", ), "source": fields.String( required=True, description="Where is this message from", example="autonomy_engine", ), "destination": fields.String( required=True, description="What is the destination of this message", example="ah-1", ), "encoded": fields.Boolean( required=True, description="Indicate that message raw (encoded) or decoded. " + "Options: encoded=True, decoded=False", example=False, ), "type": fields.String( required=True, description="Type of message", example="platform_status", ), # "payload":# TODO: schema applicable changes according to "type" }, ) platform_schema = api.model( "Platform", { "platform_ID": fields.Integer( required=True, description="unique identifier for platform", example="ah-1", ), "serial": fields.Integer( required=True, description="platform serial number", example="ah-1", ), "model": fields.Integer( required=True, description="platform serial number", example="ah-1", ), "active": fields.Boolean( required=False, description="platform in mission", example=True, ), }, ) squad_metadata_schema = api.model( "SquadMetadata", { "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( "Constraints", # 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( "Configuration", { "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", ), }, ) 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="", ), }, ) observation_schema = api.model( "Observation", { "message": fields.Nested( message_header_schema, required=True, description="Message header", ), "platform": fields.Nested(platform_schema), "time": fields.String( required=True, description="Timestamp of message", example="2022-11-16T00:00:00Z", ), "discuss_other_fields": fields.String( required=False, description="", # we can track the version of the AE plan? example="", ), } ) platform_status_message_schema = api.model( "PlatformStatusMessage", { "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, ), }, ) swagger_config = { "headers": [], "specs": [ { "endpoint": "swagger", "route": "/swagger.json", "rule_filter": lambda rule: True, "model_filter": lambda tag: True, } ], "static_url_path": "/flasgger_static", "swagger_ui": True, "specs_route": "/soardocs/", "swagger": "2.0", "basePath": "/soar", "info": { "title": "soar", "version": "0.1", "description": "SoAR message schemas", }, "produces": ["application/json"], "consumes": ["application/json"], } 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) def get(self): pass ns2 = api.namespace("platform_status", description="Platform Status Message Format") @ns2.route("") class PlatformStatus(Resource): @ns2.response(200, "Success", platform_status_message_schema) def get(self): pass ns3 = api.namespace( "autonomy_configuration", description="Autonomy Configuration Format" ) @ns3.route("") class AutonomyConfiguration(Resource): @ns3.response(200, "Success", autonomy_configuration_schema) def get(self): 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") @ns4.route("/ecosub") class MissionPlanEcosub(Resource): @ns4.response(200, "Success", {}) def get(self): pass @ns4.route("/reav") class MissionPlanReav(Resource): @ns4.response(200, "Success", {}) def get(self): pass @ns4.route("/autosubhover") class MissionPlanAutosubHover(Resource): @ns4.response(200, "Success", {}) def get(self): pass ns5 = api.namespace("observation", description="Observation Format --> Per Platform or generic?") @ns5.route("") class Observation(Resource): @ns5.response(200, "Success", observation_schema) def get(self): pass if __name__ == "__main__": app.run()