DEV Community

vmodal_ai
vmodal_ai

Posted on Originally published at v-modal.com

Developing a Robot Mission-Orchestration SDK with Kotlin

Developing a Robot Mission-Orchestration SDK with Kotlin

Complex robot operations—such as multi-point inspection, payload delivery, and security patrols—require robust task orchestration engines. The orchestration layer must handle sequential actions, concurrent sub-tasks, error recovery, and instant safety preemption (pause, resume, abort).

In this tutorial, we will write a Kotlin-first Mission Orchestration SDK utilizing Coroutines, Channels, and Finite State Machines (FSM).


1. Architecture of the Mission Engine

+-------------------------------------------------------------+
|                      Mission Executor                       |
+-------------------------------------------------------------+
            |                                     |
            v                                     v
+-----------------------+             +-----------------------+
|   Waypoint Task       |  ------->   |    Manipulate Payload |
+-----------------------+             +-----------------------+
            |                                     |
            v                                     v
+-----------------------+             +-----------------------+
|  Safety Guard Check   |             |   Return to Dock      |
+-----------------------+             +-----------------------+
Enter fullscreen mode Exit fullscreen mode

2. Defining Task Primitives and Mission States

package com.vmodal.sdk.mission.model

sealed class TaskResult {
    object Success : TaskResult()
    data class Failure(val reason: String) : TaskResult()
    object Aborted : TaskResult()
}

interface MissionTask {
    val taskId: String
    val description: String
    suspend fun execute(context: MissionContext): TaskResult
}

data class MissionContext(
    val robotId: String,
    val variables: MutableMap<String, Any> = mutableMapOf()
)

sealed class MissionStatus {
    object Idle : MissionStatus()
    data class Running(val currentTaskIndex: Int, val currentTaskId: String) : MissionStatus()
    object Paused : MissionStatus()
    data class Completed(val totalTasksExecuted: Int) : MissionStatus()
    data class Failed(val failedTaskId: String, val reason: String) : MissionStatus()
    object Aborted : MissionStatus()
}
Enter fullscreen mode Exit fullscreen mode

3. Implementing Concrete Mission Tasks

package com.vmodal.sdk.mission.tasks

import com.vmodal.sdk.mission.model.MissionContext
import com.vmodal.sdk.mission.model.MissionTask
import com.vmodal.sdk.mission.model.TaskResult
import kotlinx.coroutines.delay

class MoveToWaypointTask(
    override val taskId: String,
    private val x: Double,
    private val y: Double,
    private val heading: Double
) : MissionTask {
    override val description: String = "Navigating to ($x, $y) at $heading rad"

    override suspend fun execute(context: MissionContext): TaskResult {
        println("Exec [$taskId]: Starting navigation to x=$x, y=$y")
        // Simulate navigation loop delay
        delay(1500)
        println("Exec [$taskId]: Reached waypoint successfully.")
        return TaskResult.Success
    }
}

class WaitTask(
    override val taskId: String,
    private val durationMs: Long
) : MissionTask {
    override val description: String = "Pausing for ${durationMs}ms"

    override suspend fun execute(context: MissionContext): TaskResult {
        println("Exec [$taskId]: Waiting ${durationMs}ms...")
        delay(durationMs)
        return TaskResult.Success
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Building the Core Mission Orchestrator

package com.vmodal.sdk.mission

import com.vmodal.sdk.mission.model.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class MissionOrchestrator(
    private val scope: CoroutineScope
) {
    private val _status = MutableStateFlow<MissionStatus>(MissionStatus.Idle)
    val status: StateFlow<MissionStatus> = _status.asStateFlow()

    private var missionJob: Job? = null
    private var isPaused = false

    fun executeMission(context: MissionContext, tasks: List<MissionTask>) {
        if (_status.value is MissionStatus.Running) return

        missionJob = scope.launch(Dispatchers.Default) {
            println("Starting Mission execution for Robot ${context.robotId} with ${tasks.size} tasks.")

            for ((index, task) in tasks.withIndex()) {
                // Check for pause condition loop
                while (isPaused) {
                    _status.value = MissionStatus.Paused
                    delay(500)
                }

                if (!isActive) {
                    _status.value = MissionStatus.Aborted
                    return@launch
                }

                _status.value = MissionStatus.Running(index, task.taskId)

                when (val result = task.execute(context)) {
                    is TaskResult.Success -> {
                        println("Task ${task.taskId} completed successfully.")
                    }
                    is TaskResult.Failure -> {
                        _status.value = MissionStatus.Failed(task.taskId, result.reason)
                        return@launch
                    }
                    TaskResult.Aborted -> {
                        _status.value = MissionStatus.Aborted
                        return@launch
                    }
                }
            }

            _status.value = MissionStatus.Completed(tasks.size)
        }
    }

    fun pause() {
        if (_status.value is MissionStatus.Running) {
            isPaused = true
        }
    }

    fun resume() {
        if (isPaused) {
            isPaused = false
        }
    }

    fun abort() {
        missionJob?.cancel()
        _status.value = MissionStatus.Aborted
        isPaused = false
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Execution Script Example

import com.vmodal.sdk.mission.MissionOrchestrator
import com.vmodal.sdk.mission.model.MissionContext
import com.vmodal.sdk.mission.tasks.MoveToWaypointTask
import com.vmodal.sdk.mission.tasks.WaitTask
import kotlinx.coroutines.*

fun main() = runBlocking {
    val orchestrator = MissionOrchestrator(this)
    val context = MissionContext(robotId = "AMR-9021")

    val plan = listOf(
        MoveToWaypointTask("TASK_01", 12.5, 4.0, 0.0),
        WaitTask("TASK_02", 1000),
        MoveToWaypointTask("TASK_03", 20.0, 15.2, 1.57)
    )

    orchestrator.executeMission(context, plan)

    delay(2000)
    println("Operator presses PAUSE button...")
    orchestrator.pause()

    delay(2000)
    println("Operator presses RESUME button...")
    orchestrator.resume()

    delay(3000)
    println("Final Mission State: ${orchestrator.status.value}")
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

By leveraging Kotlin’s structured concurrency, suspendable tasks, and state flow models, building resilient mission execution pipelines for autonomous robots becomes clean and maintainable.


Useful Links

Top comments (0)