Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions aimdb-zenoh-connector/src/profile/attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

/// Its length: sequence number, source timestamp, the `0x10` length of the
/// GID array, and the GID.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) const ATTACHMENT_LEN: usize = 8 + 8 + 1 + 16;

/// A publication's attachment, as zenoh-cpp's `ext::Serializer` writes it.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) struct Attachment {
/// Per publisher, starting at 1.
pub sequence: i64,
Expand All @@ -15,6 +17,7 @@ pub(crate) struct Attachment {
}

impl Attachment {
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn encode(&self) -> [u8; ATTACHMENT_LEN] {
let mut out = [0; ATTACHMENT_LEN];
out[..8].copy_from_slice(&self.sequence.to_le_bytes());
Expand All @@ -25,6 +28,7 @@ impl Attachment {
}

/// `None` unless `bytes` has exactly this layout.
#[cfg(test)]
pub(crate) fn decode(bytes: &[u8]) -> Option<Self> {
let bytes: &[u8; ATTACHMENT_LEN] = bytes.try_into().ok()?;
if bytes[16] != 16 {
Expand Down
1 change: 1 addition & 0 deletions aimdb-zenoh-connector/src/profile/gid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

/// The GID rmw_zenoh derives from an entity's liveliness token: XXH3-128 of
/// the token, the low 64 bits then the high 64 bits, each little-endian.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn gid(token: &str) -> [u8; 16] {
let hash = twox_hash::XxHash3_128::oneshot(token.as_bytes());
let mut out = [0; 16];
Expand Down
7 changes: 7 additions & 0 deletions aimdb-zenoh-connector/src/profile/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use super::Qos;

/// A ROS 2 node's identity in liveliness tokens.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) struct Node<'a> {
pub domain: u32,
/// The Zenoh session id, as Zenoh prints it.
Expand All @@ -21,17 +22,20 @@ pub(crate) struct Node<'a> {

/// Whether an entity publishes or subscribes.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) enum EntityKind {
Publisher,
Subscription,
}

/// A name as a single token chunk: every `/` becomes `%`, and an empty name
/// is `%`. Written straight into the token, with no string of its own.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn mangle(name: &str) -> Mangled<'_> {
Mangled(name)
}

#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) struct Mangled<'a>(&'a str);

impl fmt::Display for Mangled<'_> {
Expand All @@ -51,12 +55,14 @@ impl fmt::Display for Mangled<'_> {

/// `<domain>/<topic>/<type>/<hash>`, the topic without its slashes at either
/// end: `0/cell4/temperature/sensor_msgs::msg::dds_::Temperature_/RIHS01_…`.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn data_key(domain: u32, topic: &str, type_name: &str, type_hash: &str) -> String {
let topic = topic.trim_matches('/');
alloc::format!("{domain}/{topic}/{type_name}/{type_hash}")
}

/// `@ros2_lv/<domain>/<zid>/<node id>/<node id>/NN/<enclave>/<namespace>/<name>`.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn node_token(node: &Node<'_>) -> String {
alloc::format!(
"@ros2_lv/{}/{}/{}/{}/NN/{}/{}/{}",
Expand All @@ -71,6 +77,7 @@ pub(crate) fn node_token(node: &Node<'_>) -> String {
}

/// `@ros2_lv/<domain>/<zid>/<node id>/<entity id>/MP|MS/<enclave>/<namespace>/<name>/<topic>/<type>/<hash>/<qos>`.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn entity_token(
node: &Node<'_>,
entity_id: u32,
Expand Down
21 changes: 14 additions & 7 deletions aimdb-zenoh-connector/src/profile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@
//! Pure functions over strings and bytes, `no_std + alloc`, so a ROS node on
//! an MCU can reuse them unchanged.

// Used by the ROS 2 connector, which builds on this module.
#![allow(dead_code, unused_imports)]

mod attachment;
mod gid;
mod keys;
mod names;
mod qos;

pub(crate) use attachment::{Attachment, ATTACHMENT_LEN};
#[cfg(any(feature = "std", test))]
pub(crate) use attachment::Attachment;
#[cfg(test)]
pub(crate) use attachment::ATTACHMENT_LEN;
#[cfg(any(feature = "std", test))]
pub(crate) use gid::gid;
pub(crate) use keys::{data_key, entity_token, mangle, node_token, EntityKind, Node};
pub(crate) use names::{validate_namespace, validate_node_name, validate_topic, NameError};
#[cfg(test)]
pub(crate) use keys::mangle;
#[cfg(any(feature = "std", test))]
pub(crate) use keys::{data_key, entity_token, node_token, EntityKind, Node};
#[cfg(feature = "std")]
pub(crate) use names::{validate_namespace, validate_node_name, validate_topic};
pub(crate) use qos::Qos;
pub use qos::Reliability;
pub(crate) use qos::{Durability, History, Qos};
#[cfg(any(feature = "std", test))]
pub(crate) use qos::{Durability, History};

#[cfg(test)]
mod golden;
Expand Down
8 changes: 8 additions & 0 deletions aimdb-zenoh-connector/src/profile/names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
use core::fmt;

/// The longest fully qualified topic name (`RMW_TOPIC_MAX_NAME_LENGTH`).
#[cfg_attr(not(feature = "std"), allow(dead_code))]
const TOPIC_MAX: usize = 255 - 8;
/// The longest namespace (`RMW_NAMESPACE_MAX_LENGTH`).
#[cfg_attr(not(feature = "std"), allow(dead_code))]
const NAMESPACE_MAX: usize = TOPIC_MAX - 2;
/// The longest node name (`RMW_NODE_NAME_MAX_NAME_LENGTH`).
#[cfg_attr(not(feature = "std"), allow(dead_code))]
const NODE_NAME_MAX: usize = 255;

/// Why a topic, namespace or node name breaks the ROS rules.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) enum NameError {
Empty,
NotAbsolute,
Expand All @@ -36,11 +40,13 @@ impl fmt::Display for NameError {
}

/// A fully qualified topic name: `/cell4/temperature`.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn validate_topic(name: &str) -> Result<(), NameError> {
absolute(name, TOPIC_MAX)
}

/// A namespace: `/` or `/cell4`.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn validate_namespace(name: &str) -> Result<(), NameError> {
if name == "/" {
return Ok(());
Expand All @@ -49,6 +55,7 @@ pub(crate) fn validate_namespace(name: &str) -> Result<(), NameError> {
}

/// A node name: `cell4_gateway`.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn validate_node_name(name: &str) -> Result<(), NameError> {
let Some(first) = name.bytes().next() else {
return Err(NameError::Empty);
Expand All @@ -65,6 +72,7 @@ pub(crate) fn validate_node_name(name: &str) -> Result<(), NameError> {
Ok(())
}

#[cfg_attr(not(feature = "std"), allow(dead_code))]
fn absolute(name: &str, max: usize) -> Result<(), NameError> {
if name.is_empty() {
return Err(NameError::Empty);
Expand Down
10 changes: 10 additions & 0 deletions aimdb-zenoh-connector/src/profile/qos.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
//! The QoS field of a liveliness token.

#[cfg(test)]
use alloc::string::String;
use core::fmt;

/// rmw_zenoh's own default depth: a token leaves the depth empty for it.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
const RMW_ZENOH_DEFAULT_DEPTH: u32 = 42;

/// The QoS a `ros2://` link advertises. Deadline, lifespan and liveliness
/// always take the ROS defaults.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) struct Qos {
pub reliability: Reliability,
pub durability: Durability,
Expand All @@ -27,6 +30,7 @@ pub enum Reliability {

impl Reliability {
/// The link config value.
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::Reliable => "reliable",
Expand All @@ -35,6 +39,7 @@ impl Reliability {
}

/// The inverse of [`as_str`](Self::as_str).
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) fn parse(value: &str) -> Option<Self> {
match value {
"reliable" => Some(Self::Reliable),
Expand All @@ -45,14 +50,18 @@ impl Reliability {
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) enum Durability {
Volatile,
#[allow(dead_code)]
TransientLocal,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(not(feature = "std"), allow(dead_code))]
pub(crate) enum History {
KeepLast,
#[allow(dead_code)]
KeepAll,
}

Expand All @@ -71,6 +80,7 @@ impl Default for Qos {

impl Qos {
/// The token's QoS field; see the [`Display`](fmt::Display) impl.
#[cfg(test)]
pub(crate) fn encode(&self) -> String {
alloc::format!("{self}")
}
Expand Down
24 changes: 14 additions & 10 deletions tools/aimdb-cli/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pub const EXIT_TIMEOUT: i32 = 4;

/// CLI-specific errors with helpful messages and hints
#[derive(Debug, Error)]
#[allow(dead_code)] // Some variants are for future use
pub enum CliError {
/// Failed to connect to AimDB instance
#[error("Connection failed: {endpoint}\n Reason: {reason}\n Hint: Check if AimDB instance is running")]
Expand All @@ -35,14 +34,17 @@ pub enum CliError {
NoInstancesFound,

/// Requested record does not exist
#[allow(dead_code)]
#[error("Record '{name}' not found\n Use 'aimdb record list' to see available records")]
RecordNotFound { name: String },

/// Attempted write operation on read-only record
#[allow(dead_code)]
#[error("Permission denied: {operation}\n Record '{name}' is not writable\n Hint: Check 'writable' column in 'aimdb record list'")]
WriteProtected { operation: String, name: String },

/// Protocol version mismatch between client and server
#[allow(dead_code)]
#[error("Protocol error: {message}\n Server version: {server_version}\n Client version: {client_version}")]
ProtocolMismatch {
message: String,
Expand Down Expand Up @@ -138,7 +140,6 @@ impl CliError {
}
}

#[allow(dead_code)] // Helper methods for future use
impl CliError {
/// Create a connection failed error
pub fn connection_failed(endpoint: impl Into<String>, reason: impl Into<String>) -> Self {
Expand All @@ -148,6 +149,17 @@ impl CliError {
}
}

/// Create an invalid JSON error
pub fn invalid_json(input: impl Into<String>, error: impl fmt::Display) -> Self {
Self::InvalidJson {
input: input.into(),
error: error.to_string(),
}
}
}

#[cfg(test)]
impl CliError {
/// Create a record not found error
pub fn record_not_found(name: impl Into<String>) -> Self {
Self::RecordNotFound { name: name.into() }
Expand All @@ -174,14 +186,6 @@ impl CliError {
}
}

/// Create an invalid JSON error
pub fn invalid_json(input: impl Into<String>, error: impl fmt::Display) -> Self {
Self::InvalidJson {
input: input.into(),
error: error.to_string(),
}
}

/// Create a server error from protocol response
pub fn server_error(
code: impl Into<String>,
Expand Down
1 change: 0 additions & 1 deletion tools/aimdb-mcp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ impl McpServer {
}

/// Set server state (internal use)
#[allow(dead_code)]
pub(crate) async fn set_state(&self, new_state: ServerState) {
*self.state.lock().await = new_state;
}
Expand Down
Loading