syntax = "proto2";

import "dccl/option_extensions.proto";
import "jaiabot/messages/mission.proto";
import "jaiabot/messages/geographic_coordinate.proto";
import "jaiabot/messages/high_control.proto";
import "goby/middleware/protobuf/coroner.proto";
import "jaiabot/messages/health.proto";
import "jaiabot/messages/imu.proto";
import "jaiabot/messages/option_extensions.proto";
import "jaiabot/messages/link.proto";
import "jaiabot/messages/hub.proto";

package jaiabot.protobuf;

message Command
{
    /*
    Actual maximum size of message: 196 bytes / 1568 bits
        dccl.id head...........................8
        user head..............................0
        body................................1556
        padding to full byte...................4
    Allowed maximum size of message: 250 bytes / 2000 bits
    */
    option (dccl.msg) = {
        id: 80
        max_bytes: 250
        codec_version: 4
        unit_system: "si"
    };

    required uint32 bot_id = 1 [
        (dccl.field) = { min: 0 max: 255 },
        (jaia.field).rest_api.presence = OMITTED  // specified in 'targets'
    ];

    required uint64 time = 2 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" }
            precision: -6  // second precision

        },
        (jaia.field).rest_api.presence = OMITTED  // written by API server
    ];

    // Special case: not sent in message - written upon receipt
    // link: which link this message traversed from hub to bot
    optional Link link = 3 [(dccl.field).omit = true];

    optional uint32 from_hub_id = 4 [(dccl.field) = { min: 0 max: 30 }];

    enum CommandType
    {
        // pre mission
        MISSION_PLAN = 1 [(jaia.ev).rest_api.presence = GUARANTEED];
        ACTIVATE = 2 [(jaia.ev).rest_api.presence = GUARANTEED];
        START_MISSION = 3 [(jaia.ev).rest_api.presence = GUARANTEED];
        MISSION_PLAN_FRAGMENT = 4;

        // during any mission
        NEXT_TASK = 10 [(jaia.ev).rest_api.presence = GUARANTEED];
        RETURN_TO_HOME = 11 [(jaia.ev).rest_api.presence = GUARANTEED];
        STOP = 12 [(jaia.ev).rest_api.presence = GUARANTEED];
        PAUSE = 13 [(jaia.ev).rest_api.presence = GUARANTEED];
        RESUME = 14 [(jaia.ev).rest_api.presence = GUARANTEED];

        // during remote control mission
        REMOTE_CONTROL_SETPOINT = 20;
        REMOTE_CONTROL_TASK = 21;
        REMOTE_CONTROL_RESUME_MOVEMENT = 22;

        // post mission
        RECOVERED = 30 [(jaia.ev).rest_api.presence = GUARANTEED];
        SHUTDOWN = 31 [(jaia.ev).rest_api.presence = GUARANTEED];
        RETRY_DATA_OFFLOAD = 32;
        DATA_OFFLOAD_COMPLETE = 33;
        DATA_OFFLOAD_FAILED = 34;

        // debugging low level commands
        RESTART_ALL_SERVICES = 40;
        REBOOT_COMPUTER = 41;
        SHUTDOWN_COMPUTER = 42;
    }

    required CommandType type = 10
        [(jaia.field).rest_api.presence = GUARANTEED];

    oneof command_data
    {
        // required for type == MISSION_PLAN
        MissionPlan plan = 20 [(jaia.field).rest_api = {
            presence: GUARANTEED,
            doc: "Used with \"command\": { \"type\" : \"MISSION_PLAN\" }"
        }];

        // required for type == REMOTE_CONTROL_SETPOINT
        RemoteControl rc = 30 [(jaia.field).rest_api = {
            presence: GUARANTEED,
            doc: "Used with \"command\": { \"type\" : \"REMOTE_CONTROL_SETPOINT\" }"
        }];

        // required for type == REMOTE_CONTROL_TASK
        MissionTask rc_task = 31;
    }
}

message CommandForHub
{
    option (dccl.msg) = {
        unit_system: "si"
    };

    required uint32 hub_id = 1 [(jaia.field).rest_api.presence = OMITTED];
    required uint64 time = 2 [
        (dccl.field) = { units { prefix: "micro" derived_dimensions: "time" } },
        (jaia.field).rest_api.presence = OMITTED
    ];

    enum HubCommandType
    {
        SCAN_FOR_BOTS = 5;
        CTD_DATA_OFFLOAD = 6;

        // debugging low level commands
        RESTART_ALL_SERVICES = 40;
        REBOOT_COMPUTER = 41;
        SHUTDOWN_COMPUTER = 42;

        // simulator commands
        SET_HUB_LOCATION = 80 [(jaia.ev).rest_api.presence = GUARANTEED];
    }

    required HubCommandType type = 10
        [(jaia.field).rest_api.presence = GUARANTEED];

    optional uint32 scan_for_bot_id = 11;

    optional GeographicCoordinate hub_location = 80
        [(jaia.field).rest_api.presence = GUARANTEED];
}

message BotStatus
{
    /*
    Actual maximum size of message: 48 bytes / 384 bits
        dccl.id head...........................8
        user head..............................0
        body.................................369
        padding to full byte...................7
    Allowed maximum size of message: 250 bytes / 2000 bits
    */
    option (dccl.msg) = {
        id: 81
        max_bytes: 250
        codec_version: 4
        unit_system: "si"
    };

    required uint32 bot_id = 1 [
        (dccl.field) = { min: 0 max: 255 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    required uint64 time = 2 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" },
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    optional uint64 last_command_time = 3 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional goby.middleware.protobuf.HealthState health_state = 4
        [(jaia.field).rest_api.presence = GUARANTEED];
    repeated Error error = 5 [
        (dccl.field).max_repeat = 5,
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    repeated Warning warning = 6 [
        (dccl.field).max_repeat = 5,
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    enum BotType
    {
        HYDRO = 1 [(jaia.ev).rest_api.presence = GUARANTEED];
        ECHO = 2 [(jaia.ev).rest_api.presence = GUARANTEED];
        BIO = 3 [(jaia.ev).rest_api.presence = GUARANTEED];
    }
    optional BotType bot_type = 7 [(jaia.field).rest_api.presence = GUARANTEED];

    // Special case: not sent in message - written upon receipt
    // link: which link this message traversed from bot to hub
    optional Link link = 8 [(dccl.field).omit = true];

    // Active links with last-received time per link (time written on hub upon receipt, not transmitted over air)
    message ActiveLink {
        required Link link = 1;
        optional uint64 last_received_time = 2 [
            (dccl.field) = {
                omit: true
                units { prefix: "micro" derived_dimensions: "time" }
            }
        ];
    }

    // list of all active links on the Bot (based on BotStatus subscription messages)
    repeated ActiveLink active_links = 9 [
        (dccl.field).max_repeat = 10
    ];

    optional GeographicCoordinate location = 10
        [(jaia.field).rest_api.presence = GUARANTEED];

    optional double sensor_depth = 11 [
        (dccl.field) = {
            min: -1
            max: 100
            precision: 1
            units: { derived_dimensions: "length" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional double depth = 12 [
        (dccl.field) = {
            min: -1
            max: 100
            precision: 1
            units: { derived_dimensions: "length" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    message Attitude
    {
        optional double roll = 1 [
            (dccl.field) = {
                min: -180
                max: 180
                precision: 0
                units {
                    derived_dimensions: "plane_angle"
                    system: "angle::degree"
                }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
        optional double pitch = 2 [
            (dccl.field) = {
                min: -180
                max: 180
                precision: 0
                units {
                    derived_dimensions: "plane_angle"
                    system: "angle::degree"
                }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
        optional double heading = 3 [
            (dccl.field) = {
                min: 0
                max: 360
                precision: 0
                units {
                    derived_dimensions: "plane_angle"
                    system: "angle::degree"
                }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
        optional double course_over_ground = 4 [
            (dccl.field) = {
                min: 0
                max: 360
                precision: 0
                units {
                    derived_dimensions: "plane_angle"
                    system: "angle::degree"
                }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
    }
    optional Attitude attitude = 20
        [(jaia.field).rest_api.presence = GUARANTEED];

    message Speed
    {
        optional double over_ground = 1 [
            (dccl.field) = {
                min: -5
                max: 10
                precision: 1
                units { derived_dimensions: "velocity" }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
        optional double over_water = 2 [(dccl.field) = {
            min: -5
            max: 10
            precision: 1
            units { derived_dimensions: "velocity" }
        }];
    }
    optional Speed speed = 30 [(jaia.field).rest_api.presence = GUARANTEED];

    optional MissionState mission_state = 40
        [(jaia.field).rest_api.presence = GUARANTEED];

    // bounds should match MissionPlan.goal max_repeat value *
    // expected_fragments max
    optional int32 active_goal = 41 [
        (dccl.field) = { min: 0 max: 80 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    optional double distance_to_active_goal = 42 [
        (dccl.field) = {
            min: 0
            max: 1000
            precision: 1
            units: { derived_dimensions: "length" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    optional uint32 active_goal_timeout = 43 [
        (dccl.field) = {
            min: 0
            max: 3600
            precision: 0
            units { base_dimensions: "T" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    optional int32 repeat_index = 44 [
        (dccl.field) = { min: 0 max: 1000 precision: 0 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional double salinity = 51
        [(dccl.field) = { min: 0 max: 100 precision: 1 }];

    optional double temperature = 52 [(dccl.field) = {
        min: -50
        max: 100
        precision: 2
        units { derived_dimensions: "temperature" system: "celsius" }
    }];

    optional double battery_percent = 53 [
        (dccl.field) = { min: 0 max: 100 precision: 0 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional int32 calibration_status = 54 [(dccl.field) = { min: 0 max: 3 }];

    optional IMUCalibrationState calibration_state = 55;

    optional double hdop = 56 [(dccl.field) = { min: 0 max: 100 precision: 2 }];

    optional double pdop = 57 [(dccl.field) = { min: 0 max: 100 precision: 2 }];

    optional int32 wifi_link_quality_percentage = 58 [
        (dccl.field) = { min: 0 max: 100 precision: 0 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional uint64 received_time = 59 [
        (dccl.field) = {
            omit: true
            units { prefix: "micro" derived_dimensions: "time" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional uint64 mission_command_time = 71 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional string mission_name = 72
        [(dccl.field).omit = true];  // not sent, restored on Hub from map
}

message DriftPacket
{
    option (dccl.msg) = {
        unit_system: "si"
    };

    optional int32 drift_duration = 1 [
        default = 0,
        (dccl.field) = {
            min: 0
            max: 3600
            precision: -1
            units { base_dimensions: "T" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    message EstimatedDrift
    {
        required double speed = 1 [
            (dccl.field) = {
                min: 0
                max: 10
                precision: 1
                units { derived_dimensions: "velocity" }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];

        optional double heading = 3 [
            (dccl.field) = {
                min: 0
                max: 360
                precision: 0
                units {
                    derived_dimensions: "plane_angle"
                    system: "angle::degree"
                }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
    }

    // should correspond to ocean current velocity
    optional EstimatedDrift estimated_drift = 10
        [(jaia.field).rest_api.presence = GUARANTEED];

    // location C
    optional GeographicCoordinate start_location = 11
        [(jaia.field).rest_api.presence = GUARANTEED];
    // location D
    optional GeographicCoordinate end_location = 12
        [(jaia.field).rest_api.presence = GUARANTEED];

    // Significant wave height is defined as the average wave height,
    // from trough to crest, of the highest one-third of the waves
    optional double significant_wave_height = 13 [
        (dccl.field) = {
            min: 0
            max: 50
            precision: 3
            units: { derived_dimensions: "length" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
}

message DivePacket
{
    option (dccl.msg) = {
        unit_system: "si"
    };

    required double dive_rate = 10 [
        (dccl.field) = {
            min: 0
            max: 10
            precision: 1
            units { derived_dimensions: "velocity" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional double unpowered_rise_rate = 11 [
        (dccl.field) = {
            min: 0
            max: 10
            precision: 1
            units { derived_dimensions: "velocity" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    optional double powered_rise_rate = 12 [
        (dccl.field) = {
            min: 0
            max: 10
            precision: 1
            units { derived_dimensions: "velocity" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    required double depth_achieved = 13 [
        (dccl.field) = {
            min: 0
            max: 100
            precision: 1
            units: { derived_dimensions: "length" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    message Measurements
    {
        optional double mean_depth = 1 [
            (dccl.field) = {
                min: 0
                max: 100
                precision: 1
                units: { derived_dimensions: "length" }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];

        optional double mean_temperature = 2 [
            (dccl.field) = {
                min: -1
                max: 50
                precision: 1
                units { derived_dimensions: "temperature" system: "celsius" }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];

        optional double mean_salinity = 3 [
            (dccl.field) = { min: 0 max: 45 precision: 1 },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
    }

    repeated Measurements measurement = 14 [
        (dccl.field) = { max_repeat: 50 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    // location A
    optional GeographicCoordinate start_location = 15
        [(jaia.field).rest_api.presence = GUARANTEED];

    optional double duration_to_acquire_gps = 16 [
        (dccl.field) = {
            min: 0
            max: 120
            precision: 1
            units { base_dimensions: "T" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    // Did we reach seafloor?
    optional bool bottom_dive = 17
        [default = false, (jaia.field).rest_api.presence = GUARANTEED];

    // Did we reach min depth?
    optional bool reached_min_depth = 18
        [default = false, (jaia.field).rest_api.presence = GUARANTEED];

    // If we reached bottom, what was the bottom type
    enum BottomType
    {
        HARD = 1 [(jaia.ev).rest_api.presence = GUARANTEED];
        SOFT = 2 [(jaia.ev).rest_api.presence = GUARANTEED];
    }

    optional BottomType bottom_type = 19
        [(jaia.field).rest_api.presence = GUARANTEED];

    // For bottom characterization
    optional double max_acceleration = 20 [
        (dccl.field) = {
            min: 0
            max: 100
            precision: 1
            units { derived_dimensions: "acceleration" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];

    message SubsurfaceCurrentVector
    {
        required double velocity = 1 [
            (dccl.field) = {
                min: 0
                max: 10
                precision: 1
                units { derived_dimensions: "velocity" }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];

        required double heading = 2 [
            (dccl.field) = {
                min: 0
                max: 360
                precision: 0
                units {
                    derived_dimensions: "plane_angle"
                    system: "angle::degree"
                }
            },
            (jaia.field).rest_api.presence = GUARANTEED
        ];
    }

    optional SubsurfaceCurrentVector subsurface_current = 21
        [(jaia.field).rest_api.presence = GUARANTEED];
}

message TaskPacket
{
    /*
    Actual maximum size of message: 220 bytes / 1760 bits
            dccl.id head..........................16
            user head..............................0
            body................................1744
            padding to full byte...................0
    Allowed maximum size of message: 250 bytes / 2000 bits
    */
    option (dccl.msg) = {
        id: 0x5001
        max_bytes: 250
        codec_version: 4
        unit_system: "si"
    };

    required uint32 bot_id = 1 [
        (dccl.field) = { min: 0 max: 255 },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    required uint64 start_time = 2 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    required uint64 end_time = 3 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    required MissionTask.TaskType type = 4
        [(jaia.field).rest_api.presence = GUARANTEED];

    // Special case: not sent in message - written upon receipt based on the
    // Link traversed
    optional Link link = 5 [(dccl.field).omit = true];

    optional DivePacket dive = 10 [(jaia.field).rest_api.presence = GUARANTEED];
    optional DriftPacket drift = 11
        [(jaia.field).rest_api.presence = GUARANTEED];

    // The command_time is the time that the command associated with this task
    // was sent from the Hub. This is used, along with the bot_id, to populate
    // the mission_name on the Hub.
    optional uint64 mission_command_time = 71 [
        (dccl.field) = {
            codec: "dccl.time2"
            units { prefix: "micro" derived_dimensions: "time" }
        },
        (jaia.field).rest_api.presence = GUARANTEED
    ];
    optional string mission_name = 72
        [(dccl.field).omit = true];  // not sent, restored on Hub from map
}

message ContactUpdate
{
    option (dccl.msg) = {
        id: 0x5002
        max_bytes: 16
        codec_version: 4
        unit_system: "si"
    };

    optional int32 contact = 1 [(dccl.field) = { min: 1, max: 15 }];
    required GeographicCoordinate location = 2;
    optional double speed_over_ground = 3 [(dccl.field) = {
        min: -5
        max: 10
        precision: 1
        units { derived_dimensions: "velocity" }
    }];
    optional double heading_or_cog = 5 [(dccl.field) = {
        min: 0
        max: 360
        precision: 0
        units { derived_dimensions: "plane_angle" system: "angle::degree" }
    }];
}

message Hub2HubData
{
    option (dccl.msg) = {
        id: 0x5003
        max_bytes: 1000
        codec_version: 4
        unit_system: "si"
    };

    required uint32 hub_id = 1 [(dccl.field) = { min: 0 max: 30 }];
    required uint64 time = 2 [(dccl.field) = {
        codec: "dccl.time"
        units { prefix: "micro" derived_dimensions: "time" }
        precision: -5  // deci-second precision
    }];

    optional Link bot_link = 3;  // which link did this originally come over
                                 // from the bot (for BotStatus/TaskPacket)?

    oneof contents
    {
        BotStatus bot_status = 10;
        TaskPacket task_packet = 11;
        Command command_for_bot = 12;
        CommandCommsResult command_comms_result = 13;
        HubStatus hub_status = 14;
    }
}

message CommandCommsResult
{
    enum CommsResult
    {
        SUCCESS = 1;
        FAILURE = 2;
    }
    required CommsResult result = 1;
    required Link link = 2;
    required Command orig_command = 3;
}
