Skip to main content

SDK Session Control Implementation

Overview

This document describes the implementation of session control mechanisms in the roboticks-SDK, including:
  1. Disabled Auto-Session Creation: Sessions are no longer created automatically
  2. Start/Stop Session Methods: Explicit methods for session lifecycle control
  3. Remote Session Control: MQTT-based commands from backend
  4. Module-to-DeviceManager Communication: ZeroMQ-based session control
  5. HelloWorld Example: Demonstrates session start/stop from a module

Architecture

Session Control Flow

┌─────────────────────────────────────────────────────────────────┐
│                     Session Control Methods                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  1. MQTT Commands (Backend → DeviceManager)                      │
│     Topic: roboticks/devices/{dsn}/commands                      │
│     Payload: {"command_id": "...", "type": 3/4, "payload": ...}  │
│                                                                   │
│  2. ZeroMQ Commands (Module → DeviceManager)                     │
│     Topic: /roboticks/session/control                            │
│     Payload: {"action": "start"} or {"action": "stop"}           │
│                                                                   │
│  3. Direct API (Code → DeviceManager)                            │
│     DeviceManager::getInstance().startSession()                  │
│     DeviceManager::getInstance().stopSession()                   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

Implementation Details

1. DeviceManager Changes

Header (DeviceManager.hpp)

Added Configuration:
struct DeviceManagerConfig {
    // ... existing fields ...

    // Session control settings
    std::string session_control_topic = "/roboticks/session/control";
};
Added Methods:
// Session lifecycle control
bool startSession(const std::string& session_id = "");
bool stopSession();

// Internal session control handlers
void initializeSessionControl();
void shutdownSessionControl();
void onSessionControlReceived(const std::string& command_json);
Modified:
// Changed default auto_start from true to false
std::shared_ptr<Session> createSession(
    const std::unordered_map<std::string, std::string>& metadata = {},
    bool auto_start = false);  // Was: true

Implementation (DeviceManager.cpp)

1. Disabled Auto-Session Creation (Line 219-225)
initialized_.store(true);
LOG_INFO("Device manager initialized");

// Note: Auto-session creation is now disabled by default
// Sessions must be started explicitly via startSession() or remote command
LOG_INFO("Session auto-start disabled - use startSession() to begin");
2. startSession() Method (Line 1044-1124)
  • Creates new session if none exists
  • Starts existing session by ID if provided
  • Publishes session creation to backend via MQTT
  • Returns true on success
3. stopSession() Method (Line 1127-1139)
  • Gets active session
  • Calls finalizeSession() which:
    • Completes the session
    • Saves to disk
    • Publishes completion to backend
    • Uploads artifacts (if auto_upload enabled)
4. MQTT Command Handlers (Line 540-553)
// START_SESSION handler
registerCommandHandler(CommandType::START_SESSION,
    [this](const Command& cmd) -> bool {
        LOG_INFO("START_SESSION command received");
        return this->startSession();
    });

// STOP_SESSION handler
registerCommandHandler(CommandType::STOP_SESSION,
    [this](const Command& cmd) -> bool {
        LOG_INFO("STOP_SESSION command received");
        return this->stopSession();
    });
5. ZeroMQ Session Control (Line 1725-1800) initializeSessionControl():
  • Creates subscriber on /roboticks/session/control topic
  • Listens for session control commands from modules
  • Called during DeviceManager initialization
onSessionControlReceived():
  • Parses JSON command: {"action": "start"} or {"action": "stop"}
  • Calls appropriate method (startSession() or stopSession())
  • Logs success/failure
shutdownSessionControl():
  • Cleans up subscriber
  • Called during DeviceManager shutdown

2. HelloWorld Module Changes

Header (HelloWorldModule.hpp)

Added Members:
private:
    void sendSessionControlCommand(const std::string& action);

    std::chrono::steady_clock::time_point session_start_time_;
    bool session_started_;
    bool session_stopped_;

    // Session control publisher (ZeroMQ)
    std::shared_ptr<roboticks::messaging::Publisher<std::string>> session_control_pub_;

Implementation (HelloWorldModule.cpp)

1. Initialize Session Control Publisher (Line 24-43)
bool HelloWorldModule::onInitialize() {
    // ... existing code ...

    // Create publisher for session control commands
    auto& messaging = roboticks::messaging::MessagingSystem::getInstance();
    session_control_pub_ = messaging.createPublisher<std::string>("/roboticks/session/control");

    if (session_control_pub_) {
        LOG_INFO("Session control publisher created successfully");
    } else {
        LOG_ERROR("Failed to create session control publisher");
        return false;
    }

    return true;
}
2. Start Session on Module Start (Line 45-57)
bool HelloWorldModule::onStart() {
    LOG_INFO("HelloWorldModule starting...");

    last_publish_time_ = std::chrono::steady_clock::now();
    session_start_time_ = std::chrono::steady_clock::now();

    // Start a new session via ZeroMQ
    sendSessionControlCommand("start");
    session_started_ = true;

    LOG_INFO("HelloWorldModule started successfully - session will run for 60 seconds");
    return true;
}
3. Stop Session After 60 Seconds (Line 59-99)
void HelloWorldModule::onUpdate() {
    auto now = std::chrono::steady_clock::now();

    // Check if 60 seconds have elapsed since session start
    if (session_started_ && !session_stopped_) {
        auto session_elapsed = std::chrono::duration_cast<std::chrono::seconds>(
            now - session_start_time_
        ).count();

        if (session_elapsed >= 60) {
            LOG_INFO("60 seconds elapsed - stopping session");
            sendSessionControlCommand("stop");
            session_stopped_ = true;
        }
    }

    // ... existing message publishing code ...
}
4. Helper Method to Send Commands (Line 116-130)
void HelloWorldModule::sendSessionControlCommand(const std::string& action) {
    if (!session_control_pub_) {
        LOG_ERROR("Session control publisher not initialized");
        return;
    }

    // Create JSON command
    std::string command = "{\"action\":\"" + action + "\"}";

    if (session_control_pub_->publish(command)) {
        LOG_INFO("Sent session control command: {}", action);
    } else {
        LOG_ERROR("Failed to send session control command: {}", action);
    }
}

Usage Examples

Example 1: Module-Initiated Session Control

// In any module's onStart() method
auto& messaging = roboticks::messaging::MessagingSystem::getInstance();
auto session_pub = messaging.createPublisher<std::string>("/roboticks/session/control");

// Start session
session_pub->publish("{\"action\":\"start\"}");

// Later, stop session
session_pub->publish("{\"action\":\"stop\"}");

Example 2: Direct API Usage

// Start session programmatically
auto& device_manager = roboticks::device::DeviceManager::getInstance();
device_manager.startSession();  // Creates and starts new session

// Stop active session
device_manager.stopSession();  // Completes and uploads

Example 3: Backend MQTT Command

Backend sends:
{
  "command_id": "cmd-123",
  "type": 3,
  "payload": ""
}
Device processes command and starts session automatically

Session Lifecycle

CREATED → [startSession()] → ACTIVE → [stopSession()] → COMPLETED → UPLOADED

State Transitions

  1. CREATED: Session object exists but not started
  2. ACTIVE: Session running, logs being collected
  3. COMPLETED: Session ended, ready for upload
  4. UPLOADED: Artifacts uploaded to backend

Backend Integration

MQTT Topics

Device Publishes:
  • Session Creation: roboticks/fleet/sessions
    {
      "action": "create",
      "session_id": "uuid",
      "device_id": "ROBOT-xxxxx",
      "status": "active",
      "started_at": 1234567890
    }
    
  • Session Completion: roboticks/fleet/sessions
    {
      "action": "complete",
      "session_id": "uuid",
      "device_id": "ROBOT-xxxxx",
      "status": "completed",
      "completed_at": 1234567890,
      "duration_seconds": 60.5
    }
    
Device Subscribes:
  • Commands: roboticks/devices/{dsn}/commands
    • CommandType::START_SESSION (3)
    • CommandType::STOP_SESSION (4)

File Upload Integration (TODO)

The session completion now integrates with the file upload mechanism documented in DEVICE_FILE_UPLOAD.md. Planned Flow:
  1. Session completes (stopSession() called)
  2. DeviceManager collects all artifacts
  3. For each artifact:
    • Request presigned URL via MQTT: roboticks/fleet/{device_id}/file_upload/request
    • Receive response: roboticks/fleet/{device_id}/file_upload/response
    • Upload file directly to S3 using presigned URL
  4. Mark session as UPLOADED
  5. Notify backend of completion

Testing

Manual Test Procedure

  1. Build SDK:
    cd /Users/mujacic/roboticks-sdk
    mkdir -p build && cd build
    cmake .. && make
    
  2. Run HelloWorld Composition:
    ./roboticks-runtime --composition ../compositions/HelloWorldComposition/HelloWorldComposition.yaml
    
  3. Expected Behavior:
    • DeviceManager starts WITHOUT auto-creating session
    • HelloWorldModule starts and creates session via ZeroMQ
    • Session runs for 60 seconds
    • HelloWorldModule automatically stops session after 60s
    • Session is finalized and marked as completed
  4. Check Logs:
    tail -f /var/roboticks/logs/device_manager.log
    
    Look for:
    • “Session auto-start disabled”
    • “Session control subscriber created successfully”
    • “Sent session control command: start”
    • “Started existing session” or “Created and started new session”
    • “60 seconds elapsed - stopping session”
    • “Session completion published via MQTT”

Migration Guide

For Existing Code

Before:
// Sessions were created automatically
auto& dm = DeviceManager::getInstance();
dm.initialize(config);
// Session auto-created here
After:
// Sessions must be started explicitly
auto& dm = DeviceManager::getInstance();
dm.initialize(config);
dm.startSession();  // Explicit start required

For Modules

To control sessions from a module:
  1. Create publisher in onInitialize()
  2. Publish start command in onStart()
  3. Publish stop command when done (timer, condition, etc.)
See HelloWorldModule for complete example.

Future Enhancements

  1. File Upload Implementation: Complete artifact upload using AWS IoT presigned URLs
  2. Signal Handling: Graceful shutdown on SIGINT/SIGTERM with file upload
  3. Interrupted Session Recovery: Resume and complete interrupted sessions
  4. Session Persistence: Save/load session state across restarts
  5. Session Metadata API: Rich metadata tagging from modules
  6. Multi-Session Support: Run multiple sessions concurrently