How to install and use a Flight Mission#

Description#

A flight mission can be in the following states:

  • UNAVAILABLE: mission is installed but not available. Possible reasons are:

    • BROKEN: the mission will never be able to load , e.g. because firmware version is not supported.

    • LOAD_FAILED: the last load failed, e.g. because of an exception in python code.

  • UNLOADED: mission is not loaded.

  • IDLE: mission is loaded and can be activated.

  • ACTIVE: mission is active.

../_images/8-mission_state.png

The flight mission is managed from the mission UI that is running on the controller. It can either be a smartphone application using GroundSDK (ex: OpenFlight) or an PC application using Olympe/framework.

The mission UI can list, install and exchange messages with the flight mission.

The mission UI is optional, a flight mission can be installed and setup to replace the default mission.

Listing and installing flight missions from mission UI#

From GroundSDK: MissionUpdater

From Olympe:

Todo

add link when available

Start a flight mission from mission UI#

Once installed, a flight mission shall first be loaded, then activated. Several flight missions can be loaded simultaneously but only one can be active. Activating a new flight mission automatically deactivate the previous one.

From GroundSDK: MissionManager

From Olympe: MissionManager

Exchanging messages between mission and mission UI#

You can define custom messages between your mission UI and your flight mission. Messages are written using Protobuf 3.

You need to comply with the following conditions for your custom protobuf message file: * A package declaration with “<your_company>.missions.” + mission name + “.airsdk.messages”

  • Two messages called Command and Event, each containing an union of all possible command or event using a oneof tag with the name id.

  • The following properties need to be set:

    • java_package: package name in the form “com.your_company.drone.missions.” + mission name + “.airsdk

    • java_outer_classname: mission name

We also recommend to follow those rules for consistency along all protobuf messages:

  • Enumerations should be put in their own separate files, use the name Enum and have a package name corresponding to the current service, followed by the enumeration name (ex: MyPackage.MyEnum). If this is not possible, add the enumeration name in the values (ex: enumeration MyEnum with value MY_ENUM_VALUE_ONE)

  • Message and Package names should be in snake_case

  • Type name should be in CamelCase

Minimal example:

syntax = "proto3";

package your_company.missions.mymission.airsdk.messages;

option java_package = "com.your_company.drone.missions.mymission.airsdk";
option java_outer_classname = "MyMission";
option (olympe_package) = "your_company.missions.mymission.airsdk";

import 'google/protobuf/empty.proto';

message Command {
    oneof id {
        google.protobuf.Empty my_empty_command = 1;
    }
}

message Event {
    oneof id {
        uint32 my_event = 1;
    }
}

On the drone side: Use the mission_environment given in the Mission constructor.

  • make_airsdk_service_pair(package): Create a communication channel with the Mission UI given a protobuf package.

From the returned service object, use the following methods to send and receive messages.

  • send(message): Send the given message to the mission UI.

  • observe(…): Register a callback for message received from mission UI.

import fsup.services.events as events
import your_company.missions.mymission.airsdk.messages_pb2 as messages_pb

class Mission(object):
    def __init__(self, mission_environment):
        # Setup service pair with protobuf description
        self.ext_ui_msgs = self.env.make_airsdk_service_pair(messages_pb)

        # Register for incoming messages (commands)
        self.ext_ui_msgs_observer = self.ext_ui_msgs.cmd.observe_messages({
            self.ext_ui_msgs.cmd.idx.my_empty_command: self._on_msg_my_empty_command
        })

        # Send a message (event)
        self.ext_ui_msgs.evt.sender.my_event(42)

    def _on_msg_my_empty_command(self, msg):
        # 'msg' is directly the arguments of 'my_empty_command'
        # (google.protobuf.Empty in this case)
        pass

On the mobile application side: Use the MissionManager peripheral from Ground SDK.

Send the given message to the drone:

  • MissionManager - func send(message: MissionMessage)

    • message.uid: UID of the destination mission.

    • message.packageName: Package name of the protobuf message.

    • message.messageNumber: Number of the message (the value of the id oneof of the message).

    • message.payload: Message payload serialized from the protobuf message.

Note

mettre à jour le code

let missionUid = "MyMission"

/// Send message to my mission
func sendMessage(drone: Drone) {
    /// Get mission manager
    let missionManager = drone.getPeripheral(Peripherals.missionManager) {  [unowned self] missionManager in
        if let missionManager = missionManager {
            // Set package name of my mission to mission manager.
            // Mission manager will then be able to send messages and received events
            missionManager.packageNames = [missionUid]
            // Create mission command
            var command = Your_Company_Missions_Mymissions_Airsdk_Messages_Command()
            // Set command id
            command.id = .myEmptyCommand(Google_Protobuf_Empty())
            // Create paylaod
            if let payload = try? command.serializedData() {
                // Create mission message
                let message = Message(messageNumber: 1,
                                                payload: payload)
                // Send message
                missionManager.send(message: message)
            }
        }
    }
}

// Message class
class Message: MissionMessage {
    var uid: String = "MyMission"
    var messageNumber: UInt
    var packageName: String = "your_company.missions.mymission.airsdk.messages"
    var payload: Data

    init(messageNumber: UInt, payload: Data) {
            self.messageNumber = messageNumber
            self.payload = payload
    }
}

Read the latest message sent by the drone:

  • MissionManager - var latestMessage: MissionMessage

    • latestMessage.uid: UID of the destination mission.

    • latestMessage.packageName: Package name of the protobuf message.

    • latestMessage.messageNumber: Number of the message (the value of the id oneof of the message).

    • latestMessage.payload: Message payload serialized from the protobuf message.

Note

mettre à jour le code

var missionManager: Ref<MissionManager>?

/// Observer for my mission
func observeMission(drone: Drone) {
    // Get mission manager
    missionManager = drone.getPeripheral(Peripherals.missionManager) {  [unowned self] missionManager in
        // Get missions
        if let missions = missionManager?.missions {
            // Test if my mission is inside this array.
            if missions.keys.contains(missionUid) {
                // Get the latest message
                if let message = missionManager?.latestMessage {
                    let uid = message.uid
                    let messageNumber = message.messageNumber
                    let packageName = message.packageName
                    let payload = message.payload

                    // Print mission parameters
                    print("uid: \(uid) messageNumber: \(messageNumber) packageName: \(packageName)")
                    // Decode mission message
                    do {
                        let decodeInfo = try Your_Company_Missions_Mymissions_Airsdk_Messages_Event(
                            serializedData: payload)
                        DispatchQueue.main.async {
                           print("Event: \(decodeInfo.myEvent)")
                        }
                    } catch {
                        print("Failed to extract protobuf data from My mission message")
                    }
                }
            }
        }
    }
}

Mission web server REST API#

Instead of using the Ground SDK to manage flight missions, the drone web server REST API can also be used.

Get Missions List#

GET /api/v1/mission/missions
GET /api/v1/mission/missions/

Response:#

  • Success:

Code

Return type

Description

200

Array of mission object

Mission list

  • Errors:

Code

Description

405

Request error

500

If there is a server internal error

  • Mission object:

parameter

type

Description

uid

string

Unique ID of the mission

name

string

Name of the mission

desc

string

Description of the mission

version

string

Version of the mission

target_model_id

integer

Model ID of the drone product

target_min_version

string

Minimum version of drone firmware supported

target_max_version

string

Maximum version of drone firmware supported

digest

string

Digest of the mission (sha256 or sha512)

  • Example

{
    {
        "uid": "com.your_company.missions.mymission1",
        "name": "mymission1",
        "version": "1.0.0",
        "desc": " description of mymission 1",
        "target_model_id": 2330,
        "target_min_version": "1.0.0",
        "target_max_version": "2.0.0"
        "digest": "sha512:5862815df39863818738079d4471555710859b13c64427d3a0325bcd987504665c827c91d78a12c276b6fc4846b7568db37e38f659d9ba701393de33c93b408"
    },
    {
        "uid": "com.your_company.missions.mymission5",
        "name": "mymission5",
        "version": "2.3.0",
        "desc": " description of mymission 5",
        "target_model_id": 2330,
        "target_min_version": "1.0.0",
        "target_max_version": "2.0.0"
        "digest": "sha512:74655467cc1ac9a9bfb60645748a0d4aa8504f900ebc6e16acd79863ea92f1bd38f87452d3ee8137d8ca0dd4dcf8d436fade2ede445c0e14d6c6f4a47a7b3f71"
    }
}

Add / Update a Mission#

PUT /api/v1/mission/missions/
<mission file content in request body>

Parameters:#

allow_overwrite: yes / no (optional, no by default)
is_default: yes / no (optional, no by default)
file: mymission_file.tar.gz

Response:#

  • Success:

Code

Description

200

Successful

  • Errors:

Code

Description

405

Request error

403

if mission with same uid is found (allow_overwrite = no)

415

If there is an installation error

500

If there is a server internal error

507

If there is no more space left in internal storage

550

If the signature is invalid

551

If the targetted drone version is invalid

552

If the targetted drone model is invalid

553

If something is wrong in info file (json)

554

If the mission file is invalid/corrupted

Delete Mission#

Uid of mission to delete is “com.your_company.mymission5”

DELETE /api/v1/mission/mission/com.your_company.missions.mymission5

Parameters:#

uid: com.your_company.missions.mymission5

Response:#

  • Success:

Code

Description

200

Successful

  • Errors:

Code

Description

405

Request error

403

If mission UID to delete is not found

415

If mission deletion failed