robotrt-cli 0.1.0-beta.1

RobotRT modular robotics runtime and middleware components.
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;

use core_api::{ServiceRequestId, UdpServiceClient, UdpServiceIncoming, UdpServiceServer};
use introspection_core::StatusSnapshot;
use serde::{Deserialize, Serialize};

pub const STATUS_SERVICE_API_VERSION: &str = "robotrt.status.service.v1";
pub const STATUS_SERVICE_NAME: &str = "/robotrt/status/query";
pub const NODE_INFO_API_VERSION: &str = "robotrt.node.info.v1";
pub const TOPIC_INFO_API_VERSION: &str = "robotrt.topic.info.v1";
pub const ACTION_INFO_API_VERSION: &str = "robotrt.action.info.v1";

static NEXT_STATUS_REQUEST_ID: AtomicU64 = AtomicU64::new(1);

#[derive(Debug, Serialize, Deserialize)]
pub struct StatusServiceRequest {
    pub api_version: String,
    pub op: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct StatusServiceResponse {
    pub api_version: String,
    pub snapshot: StatusSnapshot,
}

pub fn next_request_id() -> ServiceRequestId {
    ServiceRequestId(NEXT_STATUS_REQUEST_ID.fetch_add(1, Ordering::Relaxed))
}

pub fn build_snapshot_request(_request_id: ServiceRequestId) -> StatusServiceRequest {
    StatusServiceRequest {
        api_version: STATUS_SERVICE_API_VERSION.to_string(),
        op: "snapshot".to_string(),
    }
}

pub fn validate_request(req: &StatusServiceRequest) -> Result<(), String> {
    if req.api_version != STATUS_SERVICE_API_VERSION {
        return Err(format!(
            "unsupported api_version={} expected={}",
            req.api_version, STATUS_SERVICE_API_VERSION
        ));
    }
    if req.op != "snapshot" {
        return Err(format!("unsupported op={}", req.op));
    }
    Ok(())
}

pub fn success_response(
    _request_id: ServiceRequestId,
    snapshot: StatusSnapshot,
) -> StatusServiceResponse {
    StatusServiceResponse {
        api_version: STATUS_SERVICE_API_VERSION.to_string(),
        snapshot,
    }
}

pub fn validate_response(
    resp: &StatusServiceResponse,
    _request_id: ServiceRequestId,
) -> Result<(), String> {
    if resp.api_version != STATUS_SERVICE_API_VERSION {
        return Err(format!(
            "unsupported response api_version={} expected={}",
            resp.api_version, STATUS_SERVICE_API_VERSION
        ));
    }
    Ok(())
}

pub fn make_udp_service_client(
    endpoint: String,
    timeout_ms: u64,
) -> Result<UdpServiceClient, String> {
    UdpServiceClient::new(endpoint, Duration::from_millis(timeout_ms))
        .map_err(|err| format!("create udp service client failed: {err}"))
}

pub fn make_udp_service_server(bind: &str) -> Result<UdpServiceServer, String> {
    UdpServiceServer::bind(bind).map_err(|err| format!("bind udp service server failed: {err}"))
}

pub fn recv_status_request(
    server: &UdpServiceServer,
) -> Result<UdpServiceIncoming<StatusServiceRequest>, String> {
    server
        .recv_json::<StatusServiceRequest>()
        .map_err(|err| format!("recv status service request failed: {err}"))
}