C++ API Reference

This section contains the automatically generated API documentation for the ros2_medkit C++ codebase.

Note

This documentation is generated from source code comments using Doxygen and Breathe. Run the “Docs: Build Doxygen” VS Code task or doxygen Doxyfile in the docs/ directory before building Sphinx locally. In CI, Doxygen XML is generated automatically.

ros2_medkit_gateway

The HTTP/REST gateway that exposes ROS 2 graph via SOVD-compatible API.

Classes

class GatewayNode : public rclcpp::Node

Public Functions

explicit GatewayNode(const rclcpp::NodeOptions &options = rclcpp::NodeOptions{})
~GatewayNode() override
GatewayNode(const GatewayNode&) = delete
GatewayNode &operator=(const GatewayNode&) = delete
GatewayNode(GatewayNode&&) = delete
GatewayNode &operator=(GatewayNode&&) = delete
const ThreadSafeEntityCache &get_thread_safe_cache() const

Get the thread-safe entity cache with O(1) lookups.

Returns:

Reference to ThreadSafeEntityCache

DataAccessManager *get_data_access_manager() const

Get the DataAccessManager instance.

Note

The returned pointer is valid as long as the GatewayNode exists. REST server is stopped before GatewayNode destruction to ensure safe access.

Returns:

Raw pointer to DataAccessManager (valid for lifetime of GatewayNode)

void set_topic_data_provider(std::shared_ptr<TopicDataProvider> provider)

Attach a TopicDataProvider to route topic sampling through.

Called by main() after the rclcpp::Executor is constructed and the gateway node added to it, so the provider can build its subscription node. Stores the shared_ptr to keep the provider alive for the gateway node’s lifetime and forwards the raw pointer into DataAccessManager. The pool-backed provider is the single sampling path (issue #375 race fix).

inline TopicDataProvider *get_topic_data_provider() const
Returns:

Non-owning pointer to the currently attached TopicDataProvider, or nullptr when no provider has been wired up yet.

OperationManager *get_operation_manager() const

Get the OperationManager instance.

Returns:

Raw pointer to OperationManager (valid for lifetime of GatewayNode)

DiscoveryManager *get_discovery_manager() const

Get the DiscoveryManager instance.

Returns:

Raw pointer to DiscoveryManager (valid for lifetime of GatewayNode)

ConfigurationManager *get_configuration_manager() const

Get the ConfigurationManager instance.

Returns:

Raw pointer to ConfigurationManager (valid for lifetime of GatewayNode)

FaultManager *get_fault_manager() const

Get the FaultManager instance.

Returns:

Raw pointer to FaultManager (valid for lifetime of GatewayNode)

BulkDataStore *get_bulk_data_store() const

Get the BulkDataStore instance.

Returns:

Raw pointer to BulkDataStore (valid for lifetime of GatewayNode)

LogManager *get_log_manager() const

Get the LogManager instance.

Returns:

Raw pointer to LogManager (valid for lifetime of GatewayNode)

SubscriptionManager *get_subscription_manager() const

Get the SubscriptionManager instance.

Returns:

Raw pointer to SubscriptionManager (valid for lifetime of GatewayNode)

UpdateManager *get_update_manager() const

Get the UpdateManager instance.

Returns:

Raw pointer to UpdateManager (valid for lifetime of GatewayNode), or nullptr if disabled

LockManager *get_lock_manager() const

Get the LockManager instance.

Returns:

Raw pointer to LockManager (valid for lifetime of GatewayNode), or nullptr if locking disabled

ScriptManager *get_script_manager() const

Get the ScriptManager instance.

Returns:

Raw pointer to ScriptManager (valid for lifetime of GatewayNode), or nullptr if not initialized

PluginManager *get_plugin_manager() const

Get the PluginManager instance.

Returns:

Raw pointer to PluginManager (valid for lifetime of GatewayNode)

ResourceSamplerRegistry *get_sampler_registry() const

Get the ResourceSamplerRegistry instance.

Returns:

Raw pointer to ResourceSamplerRegistry (valid for lifetime of GatewayNode)

TransportRegistry *get_transport_registry() const

Get the TransportRegistry instance.

Returns:

Raw pointer to TransportRegistry (valid for lifetime of GatewayNode)

std::shared_ptr<SSEClientTracker> get_sse_client_tracker() const

Get the SSEClientTracker instance.

Returns:

Shared pointer to SSEClientTracker

ResourceChangeNotifier *get_resource_change_notifier() const

Get the ResourceChangeNotifier instance.

Returns:

Raw pointer to ResourceChangeNotifier (valid for lifetime of GatewayNode)

TriggerManager *get_trigger_manager() const

Get the TriggerManager instance.

Returns:

Raw pointer to TriggerManager (valid for lifetime of GatewayNode), or nullptr if disabled

ConditionRegistry *get_condition_registry() const

Get the ConditionRegistry instance.

Returns:

Raw pointer to ConditionRegistry (valid for lifetime of GatewayNode)

AggregationManager *get_aggregation_manager() const

Get the AggregationManager instance.

Returns:

Raw pointer to AggregationManager (valid for lifetime of GatewayNode), or nullptr if disabled

void handle_entity_change_notification(const EntityChangeScope &scope)

Handle a plugin’s PluginContext::notify_entities_changed request.

Runs a single refresh_cache() pass synchronously. The scope hint is accepted and logged but the current implementation ignores it and always does a full refresh - future work may limit the rediscovery to the indicated area / component. The entry point is safe to call from any thread: refresh passes triggered by plugin notifications, the periodic refresh timer, and startup are serialized by an internal mutex inside refresh_cache() because refresh touches discovery state (e.g., the merge pipeline cached inside DiscoveryManager) that itself assumes single-threaded execution - ThreadSafeEntityCache’s own mutex is not sufficient.

void trigger_reentrant_notification_for_testing(const EntityChangeScope &scope)

Test hook: simulate a plugin calling notify_entities_changed from within its own IntrospectionProvider::introspect() callback.

Sets the per-thread in-refresh flag (the same flag the real refresh path uses) and invokes handle_entity_change_notification(scope). The gateway is expected to short-circuit the call with a warning log and return without reloading the manifest.

Exists purely to let unit tests exercise the reentrancy guard without spinning up a full plugin with a real IntrospectionProvider. Do NOT call this from production code.

Public Static Functions

static size_t count_peer_nodes(const std::vector<std::pair<std::string, std::string>> &nodes_and_namespaces, const std::string &self_fqn)

Count application (peer) nodes from (name, namespace) pairs: excludes hidden nodes and the gateway’s own nodes (whose FQN starts with self_fqn).

Static and public so the startup-summary counting logic is unit-testable.

static std::string connectable_host(const std::string &bind_host)

Translate a bind address into a connectable address for the sample curl (bind-all “0.0.0.0”/”::”/”” -> loopback).

Static and public for testing.

class DiscoveryManager : public ros2_medkit_gateway::ServiceActionResolver

Orchestrates entity discovery using a configurable merge pipeline.

The discovery_mode parameter selects which layers the merge pipeline activates:

  • RUNTIME_ONLY: ROS 2 graph introspection only (no merge pipeline; the built-in Ros2RuntimeIntrospection provider is queried directly)

  • MANIFEST_ONLY: Manifest as the sole source of truth

  • HYBRID: Manifest + runtime + plugin layers merged through the pipeline

The DiscoveryManager provides a unified interface for discovering:

  • Areas: Logical groupings (manifest-only)

  • Components: Software/hardware units (HostInfoProvider, manifest, plugins)

  • Apps: Software applications (manifest, runtime nodes, plugins)

  • Functions: Functional groupings (manifest, namespaces, plugins)

See also

ros2::Ros2RuntimeIntrospection

See also

discovery::MergePipeline

See also

discovery::ManifestManager

Public Functions

explicit DiscoveryManager(rclcpp::Node *node)

Construct the discovery manager.

Parameters:

node – ROS 2 node for graph introspection (must outlive this manager)

bool initialize(const DiscoveryConfig &config)

Initialize with configuration.

Loads manifest if configured, creates appropriate strategy. For RUNTIME_ONLY mode, this is a no-op.

Parameters:

config – Discovery configuration

Returns:

true if initialization succeeded

std::vector<Area> discover_areas()

Discover all areas.

Returns:

Vector of discovered Area entities

std::vector<Component> discover_components()

Discover all components.

Returns:

Vector of discovered Component entities

std::vector<App> discover_apps()

Discover all apps.

Returns:

Vector of discovered App entities (empty in runtime-only mode)

std::vector<Function> discover_functions()

Discover all functions.

Returns:

Vector of discovered Function entities (empty in runtime-only mode)

std::vector<Function> discover_functions(const std::vector<App> &apps)

Discover functions using pre-discovered apps.

Avoids redundant ROS 2 graph introspection when apps have already been discovered in the same refresh cycle. Falls back to the no-arg overload for modes that don’t support this optimization (manifest-only, hybrid).

Parameters:

apps – Pre-discovered apps (used only in RUNTIME_ONLY mode)

Returns:

Vector of discovered Function entities

std::optional<Area> get_area(const std::string &id)

Get area by ID.

Parameters:

idArea identifier

Returns:

Area if found

std::optional<Component> get_component(const std::string &id)

Get component by ID.

Parameters:

idComponent identifier

Returns:

Component if found

std::optional<App> get_app(const std::string &id)

Get app by ID.

Parameters:

id – App identifier

Returns:

App if found

std::optional<Function> get_function(const std::string &id)

Get function by ID.

Parameters:

id – Function identifier

Returns:

Function if found

std::vector<Area> get_subareas(const std::string &area_id)

Get subareas of an area.

Parameters:

area_id – Parent area ID

Returns:

Vector of child areas

std::vector<Component> get_subcomponents(const std::string &component_id)

Get subcomponents of a component.

Parameters:

component_id – Parent component ID

Returns:

Vector of child components

std::vector<Component> get_components_for_area(const std::string &area_id)

Get components in an area.

Parameters:

area_idArea ID

Returns:

Vector of components in the area

std::vector<App> get_apps_for_component(const std::string &component_id)

Get apps for a component.

Parameters:

component_idComponent ID

Returns:

Vector of apps associated with the component

std::vector<std::string> get_hosts_for_function(const std::string &function_id)

Get host component IDs for a function.

Parameters:

function_id – Function ID

Returns:

Vector of component IDs that host the function

std::vector<ServiceInfo> discover_services()

Discover all services in the system.

Returns:

Vector of ServiceInfo with schema information

std::vector<ActionInfo> discover_actions()

Discover all actions in the system.

Returns:

Vector of ActionInfo with schema information

virtual std::optional<ServiceInfo> find_service(const std::string &component_ns, const std::string &operation_name) const override

Find a service by component namespace and operation name.

Parameters:
  • component_nsComponent namespace

  • operation_name – Service name

Returns:

ServiceInfo if found, nullopt otherwise

virtual std::optional<ActionInfo> find_action(const std::string &component_ns, const std::string &operation_name) const override

Find an action by component namespace and operation name.

Parameters:
  • component_nsComponent namespace

  • operation_name – Action name

Returns:

ActionInfo if found, nullopt otherwise

void set_topic_data_provider(TopicDataProvider *provider)

Set the topic sampler for component-topic mapping.

Parameters:

provider – Pointer to TopicDataProvider (must outlive DiscoveryManager)

void set_type_introspection(ros2_medkit_serialization::TypeIntrospection *introspection)

Set the type introspection for operation schema enrichment.

Parameters:

introspection – Pointer to TypeIntrospection (must outlive DiscoveryManager)

void refresh_topic_map()

Refresh the cached topic map.

bool is_topic_map_ready() const

Check if topic map has been built at least once.

Returns:

true if topic map is ready, false if not yet built

void add_plugin_layer(const std::string &plugin_name, IntrospectionProvider *provider)

Add a plugin layer to the merge pipeline.

Wraps an IntrospectionProvider as a PluginLayer and adds it to the pipeline. Only works in HYBRID mode.

Parameters:
  • plugin_name – Name of the plugin

  • provider – Non-owning pointer to IntrospectionProvider

void register_introspection_provider(const std::string &name, std::shared_ptr<IntrospectionProvider> provider)

Register an introspection provider with the merge pipeline.

Equivalent to add_plugin_layer for shared-ownership providers; the manager keeps the provider alive for the lifetime of the pipeline. Only meaningful in HYBRID mode.

void refresh_pipeline()

Re-execute the merge pipeline (hybrid mode only)

Call after adding plugin layers to trigger a single pipeline refresh.

discovery::ManifestManager *get_manifest_manager()

Get the manifest manager.

Returns:

Pointer to manifest manager (nullptr if not using manifest)

bool has_host_info_provider() const

Check if a host info provider is active.

Returns:

true if default component is enabled and provider exists

std::optional<Component> get_default_component() const

Get the default Component from HostInfoProvider.

Returns:

Component entity representing the local host, or nullopt if not enabled

inline DiscoveryMode get_mode() const

Get current discovery mode.

Returns:

Active discovery mode

std::string get_strategy_name() const

Get a human-readable name for the active discovery configuration.

Returns:

One of “runtime”, “manifest”, “hybrid”.

std::optional<discovery::MergeReport> get_merge_report() const

Get the last merge pipeline report (hybrid mode only)

Returns:

MergeReport if in hybrid mode, nullopt otherwise

std::optional<discovery::LinkingResult> get_linking_result() const

Get the last linking result (hybrid mode only)

Returns:

LinkingResult if in hybrid mode, nullopt otherwise

class DataAccessManager

Application service for topic publish + sample.

Pure C++; ROS-side I/O is performed by the injected TopicTransport adapter (typically Ros2TopicTransport). Type introspection and the per-entity TopicDataProvider pointer remain on the manager because they are consumed directly by handlers and the discovery manager.

Public Functions

explicit DataAccessManager(std::shared_ptr<TopicTransport> transport, double topic_sample_timeout_sec = 1.0)
Parameters:
  • transport – Concrete TopicTransport adapter. Manager takes shared ownership.

  • topic_sample_timeout_sec – Default sample timeout in seconds. Used when callers pass a negative timeout.

~DataAccessManager() = default
DataAccessManager(const DataAccessManager&) = delete
DataAccessManager &operator=(const DataAccessManager&) = delete
DataAccessManager(DataAccessManager&&) = delete
DataAccessManager &operator=(DataAccessManager&&) = delete
json publish_to_topic(const std::string &topic_path, const std::string &msg_type, const json &data, double timeout_sec = 5.0)

Publish data to a specific topic.

Parameters:
  • topic_path – Full topic path (e.g., /chassis/brakes/command).

  • msg_type – ROS 2 message type (e.g., std_msgs/msg/Float32).

  • data – JSON data to publish.

  • timeout_sec – Timeout for the publish operation.

Returns:

JSON with publish status.

json get_topic_sample_with_fallback(const std::string &topic_name, double timeout_sec = -1.0)

Get topic sample with fallback to metadata on timeout.

If the topic is publishing, returns actual data with type info. If the topic times out, returns metadata (type, schema, pub/sub counts) instead of an error.

Parameters:
  • topic_name – Full topic path.

  • timeout_sec – Timeout for data retrieval. Use -1.0 to use the configured default.

json get_topic_sample_native(const std::string &topic_name, double timeout_sec = 1.0)

Get single topic sample using the native fast path.

Fast path for single-topic sampling. When no publishers are present, returns metadata-only without calling into the transport.

Parameters:
  • topic_name – Full topic path.

  • timeout_sec – Timeout for sampling (only used if topic has publishers).

inline ros2_medkit_serialization::TypeIntrospection *get_type_introspection() const

Get the type introspection instance (used by handlers and discovery).

Forwarded to the transport adapter, which owns the rclcpp-coupled implementation. May return nullptr for transports that do not support introspection.

inline void set_topic_data_provider(TopicDataProvider *provider)

Attach a TopicDataProvider for sampling.

The provider owns the pool-backed subscription path. Non-owning pointer; caller retains ownership. Safe to call once at wiring time.

inline TopicDataProvider *get_topic_data_provider() const
inline double get_topic_sample_timeout() const

Get the configured topic sample timeout in seconds.

class OperationManager

Application service for ROS 2 service / action operations.

Pure C++; ROS-side I/O is performed by the injected ServiceTransport and ActionTransport adapters. Goal tracking, UUID generation, type validation and component-namespace resolution remain in the manager body.

Public Types

using LogSink = std::function<void(int level, std::string_view message)>

Sink for internal diagnostics (stuck-goal eviction, etc.).

Keeps OperationManager middleware-neutral while preserving observability when the gateway adapter forwards to /rosout. May be null - the sink is then a no-op, which is the production behaviour today.

Public Functions

OperationManager(std::shared_ptr<ServiceTransport> service_transport, std::shared_ptr<ActionTransport> action_transport, ServiceActionResolver *resolver, int service_call_timeout_sec = 10, LogSink log_sink = nullptr)
Parameters:
  • service_transport – Concrete ServiceTransport adapter (typically Ros2ServiceTransport).

  • action_transport – Concrete ActionTransport adapter (typically Ros2ActionTransport).

  • resolver – Service / action lookup interface (DiscoveryManager implements it). Must outlive this manager. May be nullptr in tests that do not exercise the component-name resolution paths.

  • service_call_timeout_sec – Timeout in seconds applied to every service / action call.

  • log_sink – Optional sink for internal diagnostics. Pass nullptr to silence stuck-goal eviction warnings (default).

~OperationManager()
OperationManager(const OperationManager&) = delete
OperationManager &operator=(const OperationManager&) = delete
OperationManager(OperationManager&&) = delete
OperationManager &operator=(OperationManager&&) = delete
void shutdown()

Idempotent teardown.

Clears tracked goals and unsubscribes from status topics. Subsequent calls are no-ops. Transport teardown is owned by the transport destructors.

void set_notifier(ResourceChangeNotifier *notifier)

Set optional notifier for broadcasting operation status changes to trigger subsystem.

ServiceCallResult call_service(const std::string &service_path, const std::string &service_type, const json &request)

Call a ROS 2 service synchronously through the ServiceTransport.

ServiceCallResult call_component_service(const std::string &component_ns, const std::string &operation_name, const std::optional<std::string> &service_type, const json &request)

Find and call a service by component and operation name.

Resolves the path / type via the ServiceActionResolver when type is unset.

ActionSendGoalResult send_action_goal(const std::string &action_path, const std::string &action_type, const json &goal, const std::string &entity_id = "")

Send a goal to an action server through the ActionTransport.

ActionSendGoalResult send_component_action_goal(const std::string &component_ns, const std::string &operation_name, const std::optional<std::string> &action_type, const json &goal, const std::string &entity_id = "")

Send a goal using component namespace + operation name.

Resolves through the ServiceActionResolver when type is unset.

ActionCancelResult cancel_action_goal(const std::string &action_path, const std::string &goal_id)

Cancel a running action goal.

ActionGetResultResult get_action_result(const std::string &action_path, const std::string &action_type, const std::string &goal_id)

Get the result of a completed action.

std::optional<ActionGoalInfo> get_tracked_goal(const std::string &goal_id) const

Get tracked goal info by goal_id.

std::vector<ActionGoalInfo> list_tracked_goals() const

List all tracked goals.

std::vector<ActionGoalInfo> get_goals_for_action(const std::string &action_path) const

Get all goals for a specific action path, sorted by created_at (newest first).

std::optional<ActionGoalInfo> get_latest_goal_for_action(const std::string &action_path) const

Get the most recent goal for a specific action path.

void update_goal_status(const std::string &goal_id, ActionGoalStatus status)

Update goal status in tracking. No-op if the goal is unknown.

void update_goal_feedback(const std::string &goal_id, const json &feedback)

Update goal feedback in tracking. No-op if the goal is unknown.

void cleanup_old_goals(std::chrono::seconds max_age = std::chrono::seconds(300))

Remove completed goals older than max_age.

Forwards the unsubscribe request to the action transport for each path that becomes empty.

void subscribe_to_action_status(const std::string &action_path)

Subscribe (idempotently) to action status updates.

Wires the transport callback into update_goal_status() so external status changes update the tracking map on the transport’s executor thread.

void unsubscribe_from_action_status(const std::string &action_path)

Unsubscribe from action status updates. Idempotent.

void inject_tracked_goal_for_testing(ActionGoalInfo info)

Test-only helper: inject a fully-formed ActionGoalInfo directly into the tracking map.

Used by unit tests to exercise paths (e.g. stuck-goal eviction) without driving real action server traffic. Not part of the production API.

Public Static Functions

static bool is_valid_message_type(const std::string &type)

Validate message type format (package/srv/Type or package/action/Type or package/msg/Type).

static bool is_valid_uuid_hex(const std::string &uuid_hex)

Validate UUID hex string format (32 hex characters).

static bool is_service_type(const std::string &type)

Check if type is a service type (contains /srv/).

static bool is_action_type(const std::string &type)

Check if type is an action type (contains /action/).

Public Static Attributes

static constexpr int kLogLevelWarn = 30

Severity used by the OperationManager’s log_sink callback.

Numeric values match rcl/rcutils log levels (30=WARN, 40=ERROR) so an adapter can forward to RCLCPP_* macros without translation. Mirrors LogManager’s kLogLevelWarn / kLogLevelError contract.

static constexpr int kLogLevelError = 40
class ConfigurationManager

Application service for ROS 2 node parameters.

Pure C++; ROS-side I/O is performed by the injected ParameterTransport adapter (typically Ros2ParameterTransport). All rclcpp::SyncParametersClient usage, the rclcpp::Parameter defaults cache, the negative-cache for unreachable nodes, the spin_mutex serialising parameter-client spins, and the JSON <-> rclcpp::ParameterValue conversion helpers live in the adapter.

The manager retains the high-level orchestration:

  • shutdown ordering contract (idempotent shutdown forwards to transport)

  • reset orchestration (compose set_parameter with cached default values)

  • the public CRUD surface consumed by handler_context handlers

Self-node short-circuiting (avoiding IPC for the gateway’s own parameters) is delegated to the transport because identifying the gateway’s own FQN requires the rclcpp node handle.

Public Functions

explicit ConfigurationManager(std::shared_ptr<ParameterTransport> transport)
Parameters:

transport – Concrete ParameterTransport adapter. Manager takes shared ownership.

~ConfigurationManager()
ConfigurationManager(const ConfigurationManager&) = delete
ConfigurationManager &operator=(const ConfigurationManager&) = delete
ConfigurationManager(ConfigurationManager&&) = delete
ConfigurationManager &operator=(ConfigurationManager&&) = delete
void shutdown()

Idempotent teardown.

Forwards to the transport’s shutdown so cached parameter clients drop their references before rclcpp::shutdown() runs. Must be called before rclcpp::shutdown() to prevent use-after-free of the parameter-client internal node held by the transport.

ParameterResult list_parameters(const std::string &node_name)

List all parameters for a node.

Parameters:

node_name – Fully qualified node name (e.g., “/powertrain/engine/engine_temp_sensor”).

Returns:

ParameterResult with array of {name, value, type} objects.

ParameterResult get_parameter(const std::string &node_name, const std::string &param_name)

Get a specific parameter value.

Parameters:
  • node_name – Fully qualified node name.

  • param_name – Parameter name.

Returns:

ParameterResult with {name, value, type, description, read_only}.

ParameterResult set_parameter(const std::string &node_name, const std::string &param_name, const json &value)

Set a parameter value.

Parameters:
  • node_name – Fully qualified node name.

  • param_name – Parameter name.

  • value – New value (JSON type will be converted to the appropriate ROS 2 type by the transport).

Returns:

ParameterResult with {name, value, type}.

ParameterResult reset_parameter(const std::string &node_name, const std::string &param_name)

Reset a specific parameter to its default (initial) value.

Parameters:
  • node_name – Fully qualified node name.

  • param_name – Parameter name.

Returns:

ParameterResult with reset parameter info.

ParameterResult reset_all_parameters(const std::string &node_name)

Reset all parameters of a node to their default (initial) values.

Parameters:

node_name – Fully qualified node name.

Returns:

ParameterResult with count of reset parameters.

Data Models

struct QosProfile

QoS profile information for a topic endpoint.

Public Functions

inline json to_json() const

Public Members

std::string reliability

“reliable”, “best_effort”, “system_default”, “unknown”

std::string durability

“volatile”, “transient_local”, “system_default”, “unknown”

std::string history

“keep_last”, “keep_all”, “system_default”, “unknown”

size_t depth = {0}

History depth (for keep_last)

std::string liveliness

“automatic”, “manual_by_topic”, “system_default”, “unknown”

struct TopicEndpoint

Information about an endpoint (publisher or subscriber) on a topic.

Public Functions

inline std::string fqn() const

Get fully qualified node name.

inline json to_json() const

Public Members

std::string node_name

Name of the node (e.g., “controller_server”)

std::string node_namespace

Namespace of the node (e.g., “/navigation”)

std::string topic_type

Message type (e.g., “geometry_msgs/msg/Twist”)

QosProfile qos

QoS profile of this endpoint.

struct TopicConnection

Topic with its publishers and subscribers.

Public Functions

inline json to_json() const

Public Members

std::string topic_name

Full topic path (e.g., “/cmd_vel”)

std::string topic_type

Message type.

std::vector<TopicEndpoint> publishers
std::vector<TopicEndpoint> subscribers
struct Area

SOVD Area entity - represents a logical grouping (ROS 2 namespace)

Areas are derived from ROS 2 namespaces or defined in manifests. They provide a hierarchical organization of components.

Public Functions

inline json to_json() const

Convert to JSON representation.

SOVD EntityReference fields: id, name, href, translation_id, tags ROS 2 extensions in x-medkit: namespace, type, description, parentAreaId

Returns:

JSON object with area data

inline json to_entity_reference(const std::string &base_url) const

Create SOVD EntityReference format (strictly compliant)

Parameters:

base_url – Base URL for self links

Returns:

JSON object in EntityReference format: id, name, href, [translationId, tags]

inline json to_capabilities(const std::string &base_url) const

Create SOVD Entity Capabilities format (strictly compliant)

Parameters:

base_url – Base URL for capability links

Returns:

JSON object listing available sub-resources

Public Members

std::string id

Unique identifier (e.g., “powertrain”)

std::string name

Human-readable name (e.g., “Powertrain System”)

std::string namespace_path

ROS 2 namespace path (e.g., “/powertrain”)

std::string type = "Area"

Entity type (always “Area”)

std::string translation_id

Internationalization key.

std::string description

Human-readable description.

std::vector<std::string> tags

Tags for filtering.

std::string parent_area_id

Parent area ID for sub-areas.

std::string source

Origin of this area (e.g., “manifest”, “heuristic”)

std::vector<std::string> contributors

Aggregation provenance: “local” and/or “peer:<name>”.

struct Component

SOVD Component entity - represents a software/hardware component (ROS 2 node)

Components are derived from ROS 2 nodes or defined in manifests. They expose operations (services/actions), data (topics), and configurations (parameters).

Public Functions

inline json to_json() const

Convert to JSON representation.

SOVD EntityReference fields: id, name, href, translation_id, tags ROS 2 extensions in x-medkit: namespace, fqn, entityType, area, source, variant, etc.

Returns:

JSON object with component data

inline json to_entity_reference(const std::string &base_url) const

Create SOVD EntityReference format (strictly compliant)

Parameters:

base_url – Base URL for self links

Returns:

JSON object in EntityReference format: id, name, href, [translationId, tags]

inline json to_capabilities(const std::string &base_url) const

Create SOVD Entity Capabilities format (strictly compliant)

Parameters:

base_url – Base URL for capability links

Returns:

JSON object listing available sub-resources

Public Members

std::string id

Unique identifier (node name)

std::string name

Human-readable name.

std::string namespace_path

ROS 2 namespace path.

std::string fqn

Fully qualified name (namespace + id)

std::string type = "Component"

Entity type (always “Component”)

std::string area

Parent area ID.

std::string source = "node"

Discovery source: “node”, “topic”, or “manifest”.

std::string translation_id

Internationalization key.

std::string description

Human-readable description.

std::string variant

Hardware variant identifier.

std::vector<std::string> tags

Tags for filtering.

std::string parent_component_id

Parent component ID for sub-components.

std::vector<std::string> depends_on

Component IDs this component depends on.

std::vector<std::string> contributors

Aggregation provenance: “local” and/or “peer:<name>”.

std::vector<ServiceInfo> services

Services exposed by this component.

std::vector<ActionInfo> actions

Actions exposed by this component.

ComponentTopics topics

Topics this component publishes/subscribes.

std::optional<json> host_metadata

Host system metadata (for runtime default component)

ros2_medkit_fault_manager

Central fault storage and management node.

class FaultManagerNode : public rclcpp::Node

Central fault manager node.

Provides service interfaces for fault reporting, querying, and clearing. Supports configurable storage backends (memory or SQLite) via ROS parameters.

Parameters:

  • storage_type (string): “memory” or “sqlite” (default: “sqlite”)

  • database_path (string): Path to SQLite database file (default: “/var/lib/ros2_medkit/faults.db”) Use “:memory:” for in-memory SQLite database (useful for testing)

Public Functions

explicit FaultManagerNode(const rclcpp::NodeOptions &options = rclcpp::NodeOptions())
~FaultManagerNode() override
FaultManagerNode(const FaultManagerNode&) = delete
FaultManagerNode &operator=(const FaultManagerNode&) = delete
FaultManagerNode(FaultManagerNode&&) = delete
FaultManagerNode &operator=(FaultManagerNode&&) = delete
inline const FaultStorage &get_storage() const

Get read-only access to fault storage (for testing)

inline const std::string &get_storage_type() const

Get the storage type being used.

inline int capture_pool_size_for_test() const
inline int capture_queue_depth_for_test() const
inline QueueFullPolicy capture_queue_full_policy_for_test() const

Public Static Functions

static bool matches_entity(const std::vector<std::string> &reporting_sources, const std::string &entity_id)

Check if entity matches any reporting source.

Parameters:
  • reporting_sources – List of reporting sources from fault

  • entity_id – Entity ID to match (exact match or as suffix of FQN)

Returns:

true if entity_id matches any source

class FaultStorage

Abstract interface for fault storage backends.

Subclassed by ros2_medkit_fault_manager::InMemoryFaultStorage, ros2_medkit_fault_manager::SqliteFaultStorage

Public Functions

virtual ~FaultStorage() = default
virtual void set_debounce_config(const DebounceConfig &config) = 0

Set debounce configuration.

virtual DebounceConfig get_debounce_config() const = 0

Get current debounce configuration.

virtual bool report_fault_event(const std::string &fault_code, uint8_t event_type, uint8_t severity, const std::string &description, const std::string &source_id, const rclcpp::Time &timestamp, const DebounceConfig &config) = 0

Report a fault event (FAILED or PASSED)

Parameters:
  • fault_code – Global fault identifier

  • event_type – EVENT_FAILED (0) or EVENT_PASSED (1)

  • severity – Fault severity level (only used for FAILED events)

  • description – Human-readable description (only used for FAILED events)

  • source_id – Reporting source identifier

  • timestamp – Current time for tracking

  • config – Debounce configuration to apply for this event (resolved per-entity by the node)

Returns:

true if this is a new occurrence (new fault or reactivated CLEARED fault), false if existing active fault was updated

virtual std::vector<ros2_medkit_msgs::msg::Fault> list_faults(bool filter_by_severity, uint8_t severity, const std::vector<std::string> &statuses) const = 0

Get faults matching filter criteria.

Parameters:
  • filter_by_severity – Whether to filter by severity

  • severity – Severity level to filter (if filter_by_severity is true)

  • statuses – List of statuses to include (empty = CONFIRMED only)

Returns:

Vector of matching faults

virtual std::optional<ros2_medkit_msgs::msg::Fault> get_fault(const std::string &fault_code) const = 0

Get a single fault by fault_code.

Parameters:

fault_code – The fault code to look up

Returns:

The fault if found, nullopt otherwise

virtual bool clear_fault(const std::string &fault_code) = 0

Clear a fault by fault_code (manual acknowledgment)

Parameters:

fault_code – The fault code to clear

Returns:

true if fault was found and cleared, false if not found

virtual size_t size() const = 0

Get total number of stored faults.

virtual bool contains(const std::string &fault_code) const = 0

Check if a fault exists.

virtual size_t check_time_based_confirmation(const rclcpp::Time &current_time) = 0

Check and confirm PREFAILED faults that have been pending too long (time-based confirmation)

Parameters:

current_time – Current timestamp for age calculation

Returns:

Number of faults that were confirmed

inline virtual void set_max_snapshots_per_fault(size_t)

Set maximum snapshots per fault code (0 = unlimited)

virtual void store_snapshot(const SnapshotData &snapshot) = 0

Store a snapshot captured when a fault was confirmed.

Parameters:

snapshot – The snapshot data to store

virtual std::vector<SnapshotData> get_snapshots(const std::string &fault_code, const std::string &topic_filter = "") const = 0

Get snapshots for a fault.

Parameters:
  • fault_code – The fault code to get snapshots for

  • topic_filter – Optional topic filter (empty = all topics)

Returns:

Vector of snapshots for the fault

virtual void store_rosbag_file(const RosbagFileInfo &info) = 0

Store rosbag file metadata for a fault.

Parameters:

info – The rosbag file info to store (replaces any existing entry for fault_code)

virtual std::optional<RosbagFileInfo> get_rosbag_file(const std::string &fault_code) const = 0

Get rosbag file info for a fault.

Parameters:

fault_code – The fault code to get rosbag for

Returns:

Rosbag file info if exists, nullopt otherwise

virtual bool delete_rosbag_file(const std::string &fault_code) = 0

Delete rosbag file record and the actual file for a fault.

Parameters:

fault_code – The fault code to delete rosbag for

Returns:

true if record was deleted, false if not found

virtual size_t get_total_rosbag_storage_bytes() const = 0

Get total size of all stored rosbag files in bytes.

Returns:

Total size in bytes

virtual std::vector<RosbagFileInfo> get_all_rosbag_files() const = 0

Get all rosbag files ordered by creation time (oldest first)

Returns:

Vector of rosbag file info

virtual std::vector<RosbagFileInfo> list_rosbags_for_entity(const std::string &entity_fqn) const = 0

Get rosbags for all faults associated with an entity.

Parameters:

entity_fqn – The entity’s fully qualified name to filter by

Returns:

Vector of rosbag file info for faults reported by this entity

virtual std::vector<ros2_medkit_msgs::msg::Fault> get_all_faults() const = 0

Get all stored faults regardless of status (for filtering)

Returns:

Vector of all faults in storage

class InMemoryFaultStorage : public ros2_medkit_fault_manager::FaultStorage

Thread-safe in-memory fault storage implementation.

Public Functions

InMemoryFaultStorage() = default
virtual void set_debounce_config(const DebounceConfig &config) override

Set debounce configuration.

virtual DebounceConfig get_debounce_config() const override

Get current debounce configuration.

virtual bool report_fault_event(const std::string &fault_code, uint8_t event_type, uint8_t severity, const std::string &description, const std::string &source_id, const rclcpp::Time &timestamp, const DebounceConfig &config) override

Report a fault event (FAILED or PASSED)

Parameters:
  • fault_code – Global fault identifier

  • event_type – EVENT_FAILED (0) or EVENT_PASSED (1)

  • severity – Fault severity level (only used for FAILED events)

  • description – Human-readable description (only used for FAILED events)

  • source_id – Reporting source identifier

  • timestamp – Current time for tracking

  • config – Debounce configuration to apply for this event (resolved per-entity by the node)

Returns:

true if this is a new occurrence (new fault or reactivated CLEARED fault), false if existing active fault was updated

virtual std::vector<ros2_medkit_msgs::msg::Fault> list_faults(bool filter_by_severity, uint8_t severity, const std::vector<std::string> &statuses) const override

Get faults matching filter criteria.

Parameters:
  • filter_by_severity – Whether to filter by severity

  • severity – Severity level to filter (if filter_by_severity is true)

  • statuses – List of statuses to include (empty = CONFIRMED only)

Returns:

Vector of matching faults

virtual std::optional<ros2_medkit_msgs::msg::Fault> get_fault(const std::string &fault_code) const override

Get a single fault by fault_code.

Parameters:

fault_code – The fault code to look up

Returns:

The fault if found, nullopt otherwise

virtual bool clear_fault(const std::string &fault_code) override

Clear a fault by fault_code (manual acknowledgment)

Parameters:

fault_code – The fault code to clear

Returns:

true if fault was found and cleared, false if not found

virtual size_t size() const override

Get total number of stored faults.

virtual bool contains(const std::string &fault_code) const override

Check if a fault exists.

virtual size_t check_time_based_confirmation(const rclcpp::Time &current_time) override

Check and confirm PREFAILED faults that have been pending too long (time-based confirmation)

Parameters:

current_time – Current timestamp for age calculation

Returns:

Number of faults that were confirmed

virtual void set_max_snapshots_per_fault(size_t max_count) override

Set maximum snapshots per fault code (0 = unlimited)

virtual void store_snapshot(const SnapshotData &snapshot) override

Store a snapshot captured when a fault was confirmed.

Parameters:

snapshot – The snapshot data to store

virtual std::vector<SnapshotData> get_snapshots(const std::string &fault_code, const std::string &topic_filter = "") const override

Get snapshots for a fault.

Parameters:
  • fault_code – The fault code to get snapshots for

  • topic_filter – Optional topic filter (empty = all topics)

Returns:

Vector of snapshots for the fault

virtual void store_rosbag_file(const RosbagFileInfo &info) override

Store rosbag file metadata for a fault.

Parameters:

info – The rosbag file info to store (replaces any existing entry for fault_code)

virtual std::optional<RosbagFileInfo> get_rosbag_file(const std::string &fault_code) const override

Get rosbag file info for a fault.

Parameters:

fault_code – The fault code to get rosbag for

Returns:

Rosbag file info if exists, nullopt otherwise

virtual bool delete_rosbag_file(const std::string &fault_code) override

Delete rosbag file record and the actual file for a fault.

Parameters:

fault_code – The fault code to delete rosbag for

Returns:

true if record was deleted, false if not found

virtual size_t get_total_rosbag_storage_bytes() const override

Get total size of all stored rosbag files in bytes.

Returns:

Total size in bytes

virtual std::vector<RosbagFileInfo> get_all_rosbag_files() const override

Get all rosbag files ordered by creation time (oldest first)

Returns:

Vector of rosbag file info

virtual std::vector<RosbagFileInfo> list_rosbags_for_entity(const std::string &entity_fqn) const override

Get rosbags for all faults associated with an entity.

Parameters:

entity_fqn – The entity’s fully qualified name to filter by

Returns:

Vector of rosbag file info for faults reported by this entity

virtual std::vector<ros2_medkit_msgs::msg::Fault> get_all_faults() const override

Get all stored faults regardless of status (for filtering)

Returns:

Vector of all faults in storage

ros2_medkit_fault_reporter

Client library for reporting faults to the fault manager.

class FaultReporter

Client library for reporting faults to the central FaultManager.

Provides a simple API for ROS 2 nodes to report faults with optional local filtering to reduce noise from repeated fault occurrences.

Example usage:

class MyNode : public rclcpp::Node {
 public:
  MyNode() : Node("my_node") {
    reporter_ = std::make_unique<FaultReporter>(
        shared_from_this(), get_fully_qualified_name());
  }

  void on_error() {
    reporter_->report("SENSOR_FAILURE", Fault::SEVERITY_ERROR, "Sensor timeout");
  }

 private:
  std::unique_ptr<FaultReporter> reporter_;
};

Public Functions

FaultReporter(const rclcpp::Node::SharedPtr &node, const std::string &source_id, const std::string &service_name = "/fault_manager/report_fault")

Construct a FaultReporter.

Parameters:
  • node – The ROS 2 node to use for service client and parameters

  • source_id – Identifier for this reporter (typically node’s FQN)

  • service_name – Name of the ReportFault service (default: /fault_manager/report_fault)

void report(const std::string &fault_code, uint8_t severity, const std::string &description)

Report a FAILED event (fault occurrence)

The fault will be forwarded to the FaultManager if local filtering allows it (threshold met within time window, or high severity).

Parameters:
  • fault_code – Global fault identifier (e.g., “MOTOR_OVERHEAT”)

  • severity – Severity level (use Fault::SEVERITY_* constants)

  • description – Human-readable fault description

void report_passed(const std::string &fault_code)

Report a PASSED event (fault condition cleared)

PASSED events bypass local filtering and are always forwarded to FaultManager. Use this when the condition that caused a fault is no longer present.

Parameters:

fault_code – Global fault identifier (must match a previously reported fault)

bool is_service_ready() const

Check if the FaultManager service is available.

inline const LocalFilter &filter() const

Get read-only access to the local filter (for testing)

class LocalFilter

Local filter for fault reports.

Tracks fault occurrences per fault_code and determines whether a report should be forwarded to the central FaultManager. Implements threshold-based filtering within a sliding time window.

Thread-safe: All public methods can be called from multiple threads.

Public Functions

explicit LocalFilter(const FilterConfig &config = FilterConfig{})
bool should_forward(const std::string &fault_code, uint8_t severity)

Check if a fault report should be forwarded.

Parameters:
  • fault_code – The fault identifier

  • severity – The fault severity level

Returns:

true if the report should be sent to FaultManager

bool should_forward_passed(const std::string &fault_code)

Check if a PASSED event should be forwarded.

void reset(const std::string &fault_code)

Reset tracking for a specific fault code.

void reset_all()

Reset all tracking state.

inline FilterConfig config() const

Get the current configuration (thread-safe copy)

void set_config(const FilterConfig &config)

Update configuration (clears existing state)

ros2_medkit_diagnostic_bridge

Bridge node that converts ROS 2 /diagnostics messages to FaultManager faults.

class DiagnosticBridgeNode : public rclcpp::Node

Bridge node that converts ROS2 /diagnostics messages to FaultManager faults.

Subscribes to /diagnostics topic and forwards diagnostic status messages to the FaultManager via the ReportFault service.

Severity mapping:

  • OK (0) -> PASSED event (healing)

  • WARN (1) -> WARN severity (1)

  • ERROR (2) -> ERROR severity (2)

  • STALE (3) -> CRITICAL severity (3)

Example launch configuration:

diagnostic_bridge:
  ros__parameters:
    diagnostics_topic: "/diagnostics"
    auto_generate_codes: true
    # Custom mappings: "name_to_code.<diagnostic_name>": "<FAULT_CODE>"
    "name_to_code.motor_controller: Status": "MOTOR_001"

Public Functions

explicit DiagnosticBridgeNode(const rclcpp::NodeOptions &options = rclcpp::NodeOptions())
std::string map_to_fault_code(const std::string &diagnostic_name) const

Map diagnostic name to fault code Uses custom mapping if available, otherwise auto-generates from name.

Public Static Functions

static std::optional<uint8_t> map_to_severity(uint8_t diagnostic_level)

Map DiagnosticStatus level to Fault severity Returns std::nullopt if level is OK (should send PASSED instead)

static bool is_ok_level(uint8_t diagnostic_level)

Check if diagnostic level indicates OK status.

ros2_medkit_log_bridge

Bridge node that promotes ROS 2 /rosout log entries to FaultManager faults.

class LogBridgeNode : public rclcpp::Node

Bridge node that promotes ROS 2 /rosout log entries to FaultManager faults.

Subscribes to /rosout (rcl_interfaces/msg/Log) and forwards entries at or above a configurable severity floor to the FaultManager, attributing each fault to the originating node’s fully-qualified name via a per-source FaultReporter. Drop-in compat adapter, same category as ros2_medkit_diagnostic_bridge: native FaultReporter instrumentation stays the canonical path; this bridge is the fallback for nodes that only log. Level mapping and the WARN/LocalFilter caveat are documented in README.md.

Hard limitation by construction: only sees rclcpp logs that reach /rosout from a still-alive node. Console-only loggers and crash-before-flush are out of reach.

Public Functions

explicit LogBridgeNode(const rclcpp::NodeOptions &options = rclcpp::NodeOptions())
std::string generate_fault_code(const std::string &source_id, const std::string &message) const

Auto-generate a stable fault code from the originating node’s FQN and the log message.

Numbers/hex/paths in the message are normalized away so the same logical message maps to the same code across occurrences. Format: <PREFIX>_<NODE>_<HASH>, clamped to medkit’s [A-Z0-9_] / 64-char rule.

bool node_is_eligible(const std::string &source_id) const

Whether a given originating node should be promoted, honouring the include/exclude lists.

Exposed for unit testing.

bool skips_medkit_stack(const std::string &logger_name) const

Whether this log should be skipped because it comes from the medkit stack itself - applies the exclude_medkit_stack toggle to is_medkit_stack_logger.

Exposed for unit testing the toggle.

bool cooldown_allows(const std::string &fault_code, uint8_t severity, rclcpp::Time now)

Whether a (fault_code, severity) may be forwarded now under the cooldown (first occurrence passes; same code+severity within report_cooldown_sec is suppressed; 0.0 disables).

Keyed by severity so a WARN never suppresses a same-message ERROR escalation. Exposed for unit testing.

ros2_medkit_fault_reporter::FaultReporter *reporter_for(const std::string &source_id)

Fetch (or lazily create) the per-source FaultReporter for an originating node, so the fault’s source_id is the node that logged, not the bridge.

Bounded by max_tracked_nodes_ with LRU eviction. Exposed for unit testing.

size_t tracked_reporter_count()

Number of currently tracked per-node reporters. Exposed for unit testing.

Public Static Functions

static bool map_level_to_severity(uint8_t log_level, uint8_t severity_floor, uint8_t *severity_out)

Map an rcl_interfaces/msg/Log level to a Fault severity.

Returns false when the level is below the floor / not promotable.

static std::string normalize_message(const std::string &message)

Normalize a log message into a stable template (lowercased, digit/hex/path runs stripped, whitespace collapsed, isolated single-letter tokens dropped).

Exposed for unit testing.

static bool is_medkit_stack_logger(const std::string &logger_name)

Whether a logger name belongs to the medkit stack’s own infrastructure (fault_manager, gateway, the other bridges).

Matched on the raw logger name so namespaced nodes are caught. Exposed for unit testing.

static std::string node_source_id(const std::string &log_name)

Map an rcl_interfaces/msg/Log.name (a logger name, e.g.

“bt_navigator” or “controller_manager.resource_manager”) to the originating node’s fully-qualified name (“/bt_navigator”, “/controller_manager”). The gateway discovers runtime entities by node FQN, so the fault’s source_id must use the same form for faults (and their snapshots / rosbag) to associate with the entity in the SOVD tree. Exposed for unit testing.

static std::string fnv1a_hex(const std::string &in)

FNV-1a 32-bit hash, fixed spec, emitted as 8 lowercase hex chars.

Exposed for unit testing so a known input asserts a known constant.

ros2_medkit_action_status_bridge

Bridge node that turns terminal ROS 2 action goal states into FaultManager faults.

class ActionStatusBridgeNode : public rclcpp::Node

Bridge node that turns terminal ROS 2 action goal states into FaultManager faults.

Generic across every action: it observes the per-action /<action>/_action/status topic (action_msgs/msg/GoalStatusArray) that every action server publishes, so no per-project code is needed.

This catches the authoritative “the goal failed” verdict that neither the /diagnostics bridge nor the /rosout log bridge can see - e.g. a Nav2 NavigateToPose aborting or a MoveIt MoveGroup goal aborting. The reason (action-specific error code in the result) is a separate enrichment concern; this bridge delivers the generic terminal status.

Status mapping (action_msgs/msg/GoalStatus):

  • ABORTED (6) -> fault (severity configurable, default ERROR)

  • CANCELED (5) -> fault only if canceled_is_fault (usually intentional)

  • SUCCEEDED (4)-> PASSED (heals the per-action fault code) if enabled

Fault state is per-ACTION, not per-goal: every message is scanned for the net state of the whole GoalStatusArray and a fault is raised/healed only on the action-level transition. See derive_state.

Public Types

enum class ActionState

Net fault state of an action, derived from a whole GoalStatusArray.

  • kUnknown: no terminal goal in the array yet (no transition)

  • kHealthy: array has terminal goals and none of them are failing

  • kFailed: at least one goal is failing (ABORTED, or CANCELED when canceled_is_fault)

Values:

enumerator kUnknown
enumerator kHealthy
enumerator kFailed

Public Functions

explicit ActionStatusBridgeNode(const rclcpp::NodeOptions &options = rclcpp::NodeOptions())
~ActionStatusBridgeNode() override
ActionStatusBridgeNode(const ActionStatusBridgeNode&) = delete
ActionStatusBridgeNode &operator=(const ActionStatusBridgeNode&) = delete
ActionStatusBridgeNode(ActionStatusBridgeNode&&) = delete
ActionStatusBridgeNode &operator=(ActionStatusBridgeNode&&) = delete
std::string fault_code_for(const std::string &action_name, bool canceled) const

Build the fault code for an action name and terminal status.

Format: <PREFIX>_<ACTION>_<ABORTED|CANCELED>, charset/length per medkit rules. canceled selects the CANCELED suffix.

ActionState apply_message(const std::string &action_name, const action_msgs::msg::GoalStatusArray &msg, ros2_medkit_fault_reporter::FaultReporter *reporter)

Update the desired per-action state from a message and reconcile it, acting on the transition only.

Returns the state that was reported on (kFailed on raise, kHealthy on heal) or kUnknown when nothing was reported

  • including when the report could not be delivered yet (the FaultManager service is not discovered), in which case the transition stays pending and is retried on the next rescan. A null reporter is the unit-test seam: delivery is a no-op treated as successful, so the transition decision and stored per-action state still update and tests assert on the return value.

Public Static Functions

static std::string action_name_from_status_topic(const std::string &topic)

Derive the action name from a /<action>/_action/status topic name.

Returns empty when the topic is not an action status topic.

static std::string server_fqn_from_endpoint(const std::string &node_name, const std::string &node_namespace)

Build a node FQN from a publisher endpoint’s node name + namespace, or “” if unresolved.

During DDS discovery rcl reports the placeholders “_NODE_NAME_UNKNOWN_” / “_NODE_NAMESPACE_UNKNOWN_” before the real name propagates; those (and an empty name) are treated as unresolved so a fault is never attributed to the placeholder.

static std::string uuid_to_hex(const std::array<uint8_t, 16> &uuid)

Lowercase hex of a 16-byte goal UUID, for dedup keys and short display.

static ActionState derive_state(const action_msgs::msg::GoalStatusArray &msg, bool canceled_is_fault)

Scan a whole GoalStatusArray and return the action-level net state.

canceled_is_fault decides whether CANCELED counts as failing. Order of the goals in the array does not affect the result (any failing goal wins).

ros2_medkit_serialization

Runtime JSON ↔ ROS 2 message serialization library.

class JsonSerializer

JSON serializer for ROS 2 messages using dynmsg.

This class provides the main API for converting between JSON and ROS 2 messages at runtime. It uses dynmsg’s YAML capabilities internally and converts between JSON and YAML formats.

Note

This class is stateless and thread-safe.

Public Functions

JsonSerializer() = default
~JsonSerializer() = default
JsonSerializer(const JsonSerializer&) = delete
JsonSerializer &operator=(const JsonSerializer&) = delete
JsonSerializer(JsonSerializer&&) = default
JsonSerializer &operator=(JsonSerializer&&) = default
nlohmann::json to_json(const TypeInfo_Cpp *type_info, const void *message_data) const

Convert a ROS 2 message to JSON.

Parameters:
  • type_info – Type introspection info

  • message_data – Pointer to the message data

Throws:

JsonConversionError – if conversion fails

Returns:

JSON representation of the message

nlohmann::json to_json(const std::string &type_string, const void *message_data) const

Convert a ROS 2 message to JSON using type string.

Parameters:
  • type_string – Full type string (e.g., “std_msgs/msg/String”)

  • message_data – Pointer to the message data

Throws:
Returns:

JSON representation of the message

RosMessage_Cpp from_json(const TypeInfo_Cpp *type_info, const nlohmann::json &json) const

Convert JSON to a ROS 2 message.

Note

Caller is responsible for calling ros_message_destroy_with_allocator

Parameters:
  • type_info – Type introspection info

  • json – JSON data to convert

Throws:

JsonConversionError – if conversion fails

Returns:

RosMessage_Cpp containing allocated message data

RosMessage_Cpp from_json(const std::string &type_string, const nlohmann::json &json) const

Convert JSON to a ROS 2 message using type string.

Note

Caller is responsible for calling ros_message_destroy_with_allocator

Parameters:
  • type_string – Full type string (e.g., “std_msgs/msg/String”)

  • json – JSON data to convert

Throws:
Returns:

RosMessage_Cpp containing allocated message data

void from_json_to_message(const TypeInfo_Cpp *type_info, const nlohmann::json &json, void *message_data) const

Populate an existing message from JSON.

Parameters:
  • type_info – Type introspection info

  • json – JSON data to convert

  • message_data – Pointer to pre-allocated message to populate

Throws:

JsonConversionError – if conversion fails

nlohmann::json get_schema(const TypeInfo_Cpp *type_info) const

Generate a JSON schema for a ROS 2 type.

Parameters:

type_info – Type introspection info

Returns:

JSON schema object

nlohmann::json get_schema(const std::string &type_string) const

Generate a JSON schema using type string.

Parameters:

type_string – Full type string

Throws:

TypeNotFoundError – if type cannot be loaded

Returns:

JSON schema object

nlohmann::json get_defaults(const TypeInfo_Cpp *type_info) const

Get default values for a ROS 2 type as JSON.

Parameters:

type_info – Type introspection info

Returns:

JSON object with default values

nlohmann::json get_defaults(const std::string &type_string) const

Get default values using type string.

Parameters:

type_string – Full type string

Throws:

TypeNotFoundError – if type cannot be loaded

Returns:

JSON object with default values

rclcpp::SerializedMessage serialize(const std::string &type_string, const nlohmann::json &json) const

Serialize JSON to CDR format for use with GenericClient.

Parameters:
  • type_string – Full type string (e.g., “std_srvs/srv/SetBool_Request”)

  • json – JSON data to serialize

Throws:
Returns:

SerializedMessage containing CDR data

nlohmann::json deserialize(const std::string &type_string, const rclcpp::SerializedMessage &serialized_msg) const

Deserialize CDR data to JSON.

Parameters:
  • type_string – Full type string

  • serialized_msg – SerializedMessage containing CDR data

Throws:
Returns:

JSON representation of the message

Public Static Functions

static nlohmann::json yaml_to_json(const YAML::Node &yaml)

Convert YAML node to JSON.

Parameters:

yaml – YAML node to convert

Returns:

JSON representation

static YAML::Node json_to_yaml(const nlohmann::json &json)

Convert JSON to YAML node.

Parameters:

json – JSON to convert

Returns:

YAML node representation

class TypeCache

Thread-safe cache for ROS 2 type introspection data.

This class wraps dynmsg’s type loading functions and caches the results to avoid repeated dynamic library lookups. It is implemented as a singleton for global access throughout the gateway.

Note

All public methods are thread-safe.

Public Functions

TypeCache(const TypeCache&) = delete

Delete copy constructor.

TypeCache &operator=(const TypeCache&) = delete

Delete copy assignment.

const TypeInfo_Cpp *get_message_type_info(const std::string &package_name, const std::string &type_name)

Get type info for a message type (C++ introspection)

Parameters:
  • package_name – Package name (e.g., “std_msgs”)

  • type_name – Type name without prefix (e.g., “String”)

Returns:

Pointer to type info, or nullptr if not found

const TypeInfo_Cpp *get_message_type_info(const std::string &package_name, const std::string &interface_type, const std::string &type_name)

Get type info for any interface type (msg/srv/action)

Parameters:
  • package_name – Package name (e.g., “std_srvs”)

  • interface_type – Interface type (“msg”, “srv”, or “action”)

  • type_name – Type name (e.g., “Trigger_Request”)

Returns:

Pointer to type info, or nullptr if not found

const TypeInfo_Cpp *get_message_type_info(const std::string &full_type)

Get type info from full type string.

Parameters:

full_type – Full type string (e.g., “std_msgs/msg/String” or “std_srvs/srv/Trigger_Request”)

Returns:

Pointer to type info, or nullptr if not found

bool is_cached(const std::string &package_name, const std::string &type_name) const

Check if a type is cached.

Parameters:
  • package_name – Package name

  • type_name – Type name

Returns:

true if the type is in the cache

void clear()

Clear the cache.

size_t size() const

Get the number of cached types.

Public Static Functions

static TypeCache &instance()

Get the singleton instance.

static std::optional<std::tuple<std::string, std::string, std::string>> parse_type_string(const std::string &full_type)

Parse a full type string into package, interface type, and type name.

Parameters:

full_type – Full type string (e.g., “std_msgs/msg/String”)

Returns:

Tuple of (package_name, interface_type, type_name), or nullopt if invalid format

class SerializationError : public std::runtime_error

Base class for all serialization errors.

Subclassed by ros2_medkit_serialization::JsonConversionError, ros2_medkit_serialization::MissingFieldError, ros2_medkit_serialization::TypeMismatchError, ros2_medkit_serialization::TypeNotFoundError, ros2_medkit_serialization::YamlConversionError

Public Functions

inline explicit SerializationError(const std::string &message)
class TypeNotFoundError : public ros2_medkit_serialization::SerializationError

Error when type cannot be found or loaded.

Public Functions

inline explicit TypeNotFoundError(const std::string &type_name)
inline const std::string &type_name() const noexcept
class JsonConversionError : public ros2_medkit_serialization::SerializationError

Error during JSON parsing or conversion.

Public Functions

inline explicit JsonConversionError(const std::string &message)