IBMW User Guide

This guide covers getting started with IBMW, configuration, running demos, and the complete YAML configuration reference.

Table of Contents

  1. Getting Started
  2. Build Options
  3. Running Demos
  4. Demo Reference
  5. YAML Configuration Reference
  6. Troubleshooting

Getting Started

Installation

# Install prerequisites (Ubuntu 22.04+)
sudo apt update
sudo apt install build-essential cmake gcc-11 g++-11
# Native FastDDS/FastCDR metadata (rcl runtime is NOT required).
# For AArch64 cross builds, use the source-built DDS prefix workflow in
# docs/guides/cross-compilation.md instead of host ROS 2.
sudo apt install ros-humble-fastrtps

# Native build: source ROS 2 for FastDDS/FastCDR headers/libraries and host
# generator metadata only. The set +u / set -u envelope is required because
# /opt/ros/humble/setup.bash references unset shell variables.
set +u
source /opt/ros/humble/setup.bash
set -u

# Build IBMW (recommended: use CMake presets). The full preset itself does not
# enable tests, benchmarks, or ROS2 interop; the four -D flags below are the
# complete verification build configuration.
cmake --preset full -DIBMW_BUILD_TESTS=ON -DIBMW_BUILD_BENCHMARKS=ON \
                    -DIBMW_BUILD_WITH_ROS2=ON -DIBMW_BUILD_ROS2_EXTENSION=ON
cmake --build build -j$(nproc)

# Generate IDL code targets (required before install)
cd build && make ibmw_schema_common_idl_gencode \
  ibmw_schema_geometry_idl_gencode \
  ibmw_schema_sensor_idl_gencode \
  ibmw_schema_actuator_idl_gencode \
  dds_ext_proto

# Install (copies binaries, libs, configs into build/)
cmake --install . --prefix .

# Remove stale config files that interfere with ibmw-gen projects
rm -f ibmw-config.cmake ibmw-config-version.cmake

Note: The full preset builds all extensions and examples without any ROS2 rcl runtime dependency. If you need the deprecated ROS2 rcl integration, use cmake --preset ros2 (requires full ROS2 installation). AArch64 DDS cross builds use the source-built DDS prefix workflow documented in docs/guides/cross-compilation.md; do not use host /opt/ros/humble as the target dependency source.

Running Your First Module

source /opt/ros/humble/setup.bash
cd build
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/opt/ros/humble/lib:$(pwd)/lib:$(pwd):$(pwd)/bin:$LD_LIBRARY_PATH"

# Run the Hello World example
./bin/ibmw run ./cfg/examples_cpp_helloworld_cfg.yaml

You should see:

IBMW v0.1.0 starting...
Info ... [core] IBMW v0.1.0 | IB Middleware Framework
Info ... [HelloWorldModule] Setup
Info ... [HelloWorldModule] Run

Press Ctrl+C to shut down.

Three Execution Modes

IBMW supports three ways to run modules:

Pkg mode -- The ibmw binary loads module shared libraries at runtime:

./bin/ibmw run ./cfg/my_config.yaml

App mode -- A standalone executable with modules compiled in:

./my_app ./cfg/my_config.yaml

Registration mode -- Modules registered programmatically in main():

./my_app_registration_mode

Starting a New Project with ibmw-gen

To bootstrap a new project without writing boilerplate by hand, use ibmw-gen. Define your modules, messages, and services in one .ibmw.yaml file, then generate the full project:

# 1. Generate project from .ibmw.yaml
python3 src/tools/ibmw_gen/ibmw_gen.py my_project.ibmw.yaml -o my_project

# 2. Build the generated project
source /opt/ros/humble/setup.bash
cd my_project && mkdir -p build && cd build
rm -rf CMakeCache.txt CMakeFiles
export IBMW_BUILD_DIR="<ibmw-build-dir>"
cmake .. -DCMAKE_PREFIX_PATH="$IBMW_BUILD_DIR"
make -j$(nproc)

# 3. Run
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/opt/ros/humble/lib:$IBMW_BUILD_DIR/lib:$IBMW_BUILD_DIR:$IBMW_BUILD_DIR/bin:$LD_LIBRARY_PATH"
./my_app cfg/my_app.yaml

Important: Set $IBMW_BUILD_DIR to your actual IBMW build directory. The LD_LIBRARY_PATH must include build/lib, build/, and build/bin because IBMW installs shared libraries and executables into these subdirectories.

ibmw-gen produces module scaffolds, CMake rules, YAML configs, and DDS/JSON type definitions. Fill in the TODO markers with your business logic.

For a hands-on walkthrough with 8 working demos plus an extension-loading example, see ibmw-template/ and the ibmw-gen reference.


Build Options

All build options are CMake variables set with -D:

Core Options

Option Default Description
IBMW_BUILD_RUNTIME ON Build the core runtime engine
IBMW_BUILD_EXAMPLES OFF Build example applications
IBMW_BUILD_TESTS OFF Build unit tests
IBMW_BUILD_CLI_TOOLS OFF Build the ibmw CLI tool
IBMW_BUILD_WITH_ROS2 OFF Enable ROS2 rcl integration (deprecated)
IBMW_BUILD_PROTOCOLS ON Build DDS IDL protocol definitions
IBMW_USE_FMT_LIB ON Use the fmt library for formatting
IBMW_ENABLE_DLOPEN_DEEPBIND ON Use RTLD_DEEPBIND for plugin loading

Extension Options

All extensions default to OFF and require IBMW_BUILD_RUNTIME=ON:

Option Extension
IBMW_BUILD_DDS_EXTENSION DDS transport + RPC (FastDDS)
IBMW_BUILD_NET_EXTENSION HTTP/TCP/UDP transport + RPC
IBMW_BUILD_MQTT_EXTENSION MQTT transport
IBMW_BUILD_ICEORYX_EXTENSION Iceoryx shared-memory transport (zero-copy)
IBMW_BUILD_GRPC_EXTENSION gRPC RPC
IBMW_BUILD_ROS2_EXTENSION ROS2 transport (deprecated) -- use DDS extension instead
IBMW_BUILD_ECHO_EXTENSION Echo RPC (testing)
IBMW_BUILD_PROXY_EXTENSION Topic proxy/forwarding
IBMW_BUILD_PARAMETER_EXTENSION Parameter service
IBMW_BUILD_LOG_CONTROL_EXTENSION Runtime log level control
IBMW_BUILD_RECORD_PLAYBACK_EXTENSION Message recording/playback
IBMW_BUILD_TIME_MANIPULATOR_EXTENSION Time manipulation
IBMW_BUILD_TOPIC_LOGGER_EXTENSION Topic-based log output
IBMW_BUILD_SERVICE_INTROSPECTION_EXTENSION Service discovery

Running Demos

General Setup

source /opt/ros/humble/setup.bash
cd build
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/opt/ros/humble/lib:$(pwd)/lib:$(pwd):$(pwd)/bin:$LD_LIBRARY_PATH"

Using Shell Scripts

Most demos provide shell scripts in build/:

bash start_examples_cpp_helloworld.sh
bash start_examples_cpp_executor.sh
bash start_pubsub_local.sh

Using the ibmw Binary (Pkg Mode)

./bin/ibmw run ./cfg/<config_name>.yaml

Using Standalone Apps (App Mode)

./<app_name> ./cfg/<config_name>.yaml

Demo Reference

1. quickstart -- Hello World

Purpose: Minimal module lifecycle (Setup -> Run -> Stop).

What it demonstrates:

  • Module lifecycle hooks
  • Per-module YAML configuration access
  • MW_INFO() logging macro
  • All three execution modes (Pkg, App, Registration)

How to run:

# Pkg mode
./bin/ibmw run ./cfg/examples_cpp_helloworld_cfg.yaml

# App mode
./helloworld_app ./cfg/examples_cpp_helloworld_app_mode_cfg.yaml

# Registration mode (no config needed)
./helloworld_app_registration_mode

Required extensions: None (core only).


2. executor_demo -- Task Scheduling

Purpose: Demonstrates the executor framework: thread pools, strand executors, timed dispatch, timers, coroutines, and real-time scheduling.

What it demonstrates:

  • core_.GetExecutor().GetExecutor("name") -- obtaining executor handles
  • executor.Dispatch(task) / executor.DispatchAfter(duration, task)
  • Strand executor for serialized (thread-safe) execution
  • ibmw::executor::CreateTimer(executor, interval, callback) -- recurring timers
  • co_await co::Schedule(executor) -- coroutine scheduling
  • Real-time: SCHED_FIFO, SCHED_RR policies with CPU binding

How to run:

bash start_examples_cpp_executor.sh           # Basic
bash start_examples_cpp_executor_timer.sh     # Timer
bash start_examples_cpp_executor_co.sh        # Coroutines
bash start_examples_cpp_executor_co_loop.sh   # Coroutine loop
bash start_examples_cpp_executor_real_time.sh # Real-time (requires privileges)

Required extensions: None (core only).


3. logging_demo -- Logging System

Purpose: Demonstrates all log levels, custom formatting, file rotation, synchronous writes, and benchmarks.

What it demonstrates:

  • MW_TRACE/MW_DEBUG/MW_INFO/MW_WARN/MW_ERROR/MW_FATAL() macros
  • Console and file log backends
  • Log file rotation by size
  • Per-module log levels
  • Log throughput benchmarking

How to run:

bash start_examples_cpp_logger.sh                       # All levels
bash start_examples_cpp_logger_format.sh                # Custom format
bash start_examples_cpp_logger_rotate_file.sh           # File rotation
bash start_examples_cpp_logger_rotate_file_with_sync.sh # Sync writes
bash start_examples_cpp_logger_bench.sh                 # Benchmark

Required extensions: None (core only).


4. config_param_demo -- Parameters

Purpose: Demonstrates the runtime parameter API for key-value storage.

What it demonstrates:

  • core_.GetParams() -- parameter handle
  • SetParameter(key, value) / GetParameter(key)
  • Concurrent parameter access from multiple threads

How to run:

bash start_examples_cpp_parameter.sh

Required extensions: None (core only).


5. context_demo -- Context API

Purpose: Demonstrates the high-level Context API for resource-managed module development with transport, RPC, logging, and executor access.

What it demonstrates:

  • ibmw::context::Context(core) -- creating a context
  • ctx_ptr_->LetMe() -- thread-local context ownership
  • Publisher/Subscriber through context (inline and on-executor callbacks)
  • RPC Client/Server through context
  • ibmw::context::Running() -- lifecycle-aware loop control

How to run (8 sub-demos):

bash start_examples_cpp_context_executor.sh
bash start_examples_cpp_context_logger.sh

# Transport (run publisher + subscriber in separate terminals)
bash start_examples_cpp_context_publisher.sh
bash start_examples_cpp_context_subscriber_inline.sh

# RPC (run client + server in separate terminals)
bash start_examples_cpp_context_rpc_client.sh
bash start_examples_cpp_context_rpc_server_inline.sh

Required extensions: None (local transport).


6. transport_demo -- Pub/Sub

Purpose: Demonstrates publish/subscribe messaging with local and DDS backends.

What it demonstrates:

  • core_.GetTransport().GetPublisher("topic") / GetSubscriber("topic")
  • ibmw::transport::RegisterSendType<T>() / ibmw::transport::Send()
  • ibmw::transport::OnMessage<T>(subscriber, callback)
  • Local vs DDS backend routing
  • Cross-process DDS communication

How to run:

# Local backend (single process)
bash start_pubsub_local.sh

# DDS backend (single process)
bash start_pubsub_dds.sh

# DDS backend (two processes)
bash start_publisher_dds.sh   # Terminal 1
bash start_subscriber_dds.sh  # Terminal 2

Required extensions: DDS (for DDS demos); none for local.


7. dds_native_demo -- Raw DDS Types

Purpose: Direct DDS pub/sub with hand-written types (no IDL code generation).

What it demonstrates:

  • Custom DDS type with IBMW_DDS_TYPE_NAME() macro
  • Hand-written FastCDR serialize/deserialize functions
  • Single-process DDS loopback

How to run:

./dds_native_app ./dds_native_cfg.yaml

Required extensions: DDS.


8. dds_image_demo -- High-Throughput Messaging

Purpose: Demonstrates large-payload DDS messaging (640x480 RGB images at 10Hz).

What it demonstrates:

  • ~1MB per message, ~10MB/s sustained throughput
  • Sub-millisecond latency for large payloads
  • Synthetic RGB gradient test patterns

How to run:

./dds_image_app ./dds_image_cfg.yaml

Required extensions: DDS.


9. loaned_message_demo -- Zero-Copy

Purpose: End-to-end validation of the LoanedMessage API for zero-copy publishing.

What it demonstrates:

  • BorrowLoanedMessage<T>() / SendLoaned() -- zero-copy publishing
  • Plain types (zero-copy via SHM) vs non-Plain types (heap fallback)
  • Three configuration modes: AUTO, STRICT, COMPAT
  • Zero-copy statistics tracking

How to run:

# Full end-to-end test
./loaned_message_app ./cfg/loaned_message_cfg.yaml

# Configuration mode test (6 automated scenarios)
./config_test_app ./cfg/config_test_cfg.yaml

Required extensions: DDS + ROS2 (rosidl build-time only; no rcl runtime).

Note: Requires full CMake preset (or higher) with IBMW_BUILD_ROS2_TYPES=ON. Runs without rcl at runtime (pure FastDDS transport).


10. ros2_chn -- ROS2 Transport (deprecated)

Purpose: Demonstrates ROS2 message-based transport pub/sub using the local backend.

What it demonstrates:

  • ROS2 .msg protocol types (example_ros2::msg::RosTestMsg)
  • ibmw::transport::RegisterSendType<T>() with ROS2 types
  • Pkg mode (two packages) and App mode variants

How to run:

bash start_examples_cpp_ros2_chn.sh               # Two packages
bash start_examples_cpp_ros2_chn_single_pkg.sh     # Single package
bash start_examples_cpp_ros2_chn_publisher_app.sh  # App-mode publisher
bash start_examples_cpp_ros2_chn_subscriber_app.sh # App-mode subscriber

Required extensions: ROS2 (local backend).

Deprecated: ros2_chn requires the ros2_extension which depends on rcl at runtime. Prefer the DDS extension (dds_ext) for new projects. Use the ros2 CMake preset to build this demo.


11. ros2_rpc -- RPC Patterns (deprecated — use dds_rpc_demo instead)

Purpose: Comprehensive RPC demonstration with 7 variants covering all calling patterns.

What it demonstrates:

  • Synchronous RPC: SyncAccess -- blocking call with timeout
  • Asynchronous RPC: AsyncAccess -- callback-based
  • Future RPC: FutureAccess -- std::future-based
  • Coroutine RPC: CoAccess -- co_await-based
  • RPC filters/middleware for logging and timing
  • Per-call timeout via ctx_ptr->SetTimeout(3s)

How to run:

bash start_examples_cpp_ros2_rpc_sync.sh       # Sync
bash start_examples_cpp_ros2_rpc_async.sh      # Async
bash start_examples_cpp_ros2_rpc_future.sh     # Future
bash start_examples_cpp_ros2_rpc_co.sh         # Coroutine + filters
bash start_examples_cpp_ros2_rpc_single_pkg.sh # Single package
bash start_examples_cpp_ros2_rpc_server_app.sh # App-mode server
bash start_examples_cpp_ros2_rpc_client_app.sh # App-mode client

Required extensions: ROS2 (local RPC).

Deprecated: ros2_rpc uses type: ros2 service driver which requires rcl at runtime. Use type: dds service driver and see dds_rpc_demo for the recommended pure-DDS alternative. Use the ros2 CMake preset to build this demo.


12. ros2_interop_demo -- ROS2 Interoperability

Purpose: Demonstrates bidirectional communication with ROS2 nodes via FastDDS without any RCL/RMW dependency.

What it demonstrates:

  • ibmw::ros2::transport::RegisterSendType<T>() -- ROS2-compatible type registration
  • ros2_compatible_mode: true -- ROS2 topic naming with rt/ prefix
  • Bidirectional: ros2 topic echo /chatter sees IBMW-published messages
  • Standard ROS2 types (String, Int32, Point, Pose, Twist)

How to run:

# Terminal 1: IBMW node
./ros2_interop_app ./cfg/ros2_interop_cfg.yaml

# Terminal 2: Verify with ROS2 CLI
ros2 topic list
ros2 topic echo /chatter

# Terminal 3: ROS2 talker
ros2 run demo_nodes_cpp talker

Required extensions: DDS + ROS2 (rosidl build-time only; no rcl runtime).

Note: Requires full CMake preset (or higher) with IBMW_BUILD_ROS2_TYPES=ON. Runs without rcl at runtime (pure FastDDS transport).


13. ros2_point_demo -- Zero-Copy Plain Types

Purpose: Demonstrates zero-copy communication with ROS2 Plain types (geometry_msgs::msg::Point).

What it demonstrates:

  • Plain type detection (trivially_copyable + standard_layout)
  • Automatic zero-copy optimization via DDS loan_sample
  • ROS2 interoperability: ros2 topic echo /rt/point_topic

How to run:

./ros2_point_app ./cfg/ros2_point_cfg.yaml

# Verify in another terminal:
ros2 topic echo /rt/point_topic geometry_msgs/msg/Point

Required extensions: DDS + ROS2 (rosidl build-time only; no rcl runtime).

Note: Requires full CMake preset (or higher) with IBMW_BUILD_ROS2_TYPES=ON. Runs without rcl at runtime (pure FastDDS transport).


14. dds_rpc_demo -- Pure-DDS RPC (Native .srv, ROS2-Compatible)

Purpose: Demonstrates the DDS RPC service driver (type: dds) using native .srv mode for cross-process RPC over FastDDS. Business types are directly serialized/deserialized by FastDDS (no envelope wrappers), enabling 100% wire-compatible interop with ROS2 standard services.

What it demonstrates:

  • type: dds service driver with native .srv TypeSupport
  • Pure-DDS RPC -- no rcl/rclcpp needed, just FastDDS
  • 100% ROS2 wire-compatible: ros2 service call and ros2 service list work directly
  • srv: field in ibmw-gen for automatic ROS2 DDS type name derivation
  • Bidirectional ROS2 interop (IBMW server ↔ ROS2 client, ROS2 server ↔ IBMW client)
  • Coroutine-based (co_await) RPC client and server patterns
  • ibmw-gen end-to-end code generation for DDS RPC projects

How to run:

# Generate from .ibmw.yaml
cd src/samples/cpp/dds_rpc_demo
python3 <ibmw>/src/tools/ibmw_gen/ibmw_gen.py dds_rpc_demo.ibmw.yaml -o dds_rpc_demo_gen

# Build
cd dds_rpc_demo_gen && mkdir -p build && cd build
rm -rf CMakeCache.txt CMakeFiles
cmake .. -DCMAKE_PREFIX_PATH=<ibmw-install-dir>
make -j$(nproc)

# Set up runtime environment
source /opt/ros/humble/setup.bash
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/opt/ros/humble/lib:<ibmw-install-dir>/lib:<ibmw-install-dir>:<ibmw-install-dir>/bin:$LD_LIBRARY_PATH"

# Run (two terminals)
./dds_rpc_server_app cfg/dds_rpc_server_app.yaml   # Terminal 1
./dds_rpc_client_app cfg/dds_rpc_client_app.yaml   # Terminal 2

# Verify ROS2 sees the service (optional)
ros2 service list

# ROS2 interop: call IBMW server from ROS2
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 3, b: 5}"

# ROS2 interop: IBMW client calls a ROS2 server
ros2 run demo_nodes_cpp add_two_ints_server   # Terminal 3
# Then run the IBMW client -- it will call the ROS2 server

Required extensions: DDS.


15. iceoryx_inproc_demo -- Iceoryx Single-Process Pub/Sub

Purpose: Demonstrates the Iceoryx shared-memory transport in a single process, exercising automatic SHM expansion with messages from 1 KB to 1 MB.

What it demonstrates:

  • IBMW_BUILD_ICEORYX_EXTENSION build path
  • iceoryx_extension + type: iceoryx transport driver loaded together
  • IDL type registered via IBMW_DDS_TYPE_NAME + RegisterSendType<T>
  • Driver-agnostic byte transfer (the iceoryx driver reuses dds_types CDR serialization, not FastDDS at runtime)
  • Automatic SHM segment doubling when payload outgrows shm_init_size
  • RouDi daemon (iox-roudi) is required, even single-process

How to run:

# Build (tests optional)
source /opt/ros/humble/setup.bash
cmake -B build -DIBMW_BUILD_ICEORYX_EXTENSION=ON
cmake --build build -j$(nproc) --target iceoryx_inproc_app iox-roudi \
                                        ibmw_samples_cpp_iceoryx_inproc_demo_build_all

# Run (single terminal -- start_*.sh manages RouDi for you)
cd build
bash start_iceoryx_inproc.sh

Required extensions: Iceoryx (IBMW_BUILD_ICEORYX_EXTENSION=ON).

Tip: shm_init_size in the YAML is intentionally tiny (256 bytes) so that 1 MB payloads exercise the LoanShm doubling loop. Iceoryx's own SHM growth logs are at Debug level; successful end-to-end delivery of 1 MB samples is sufficient evidence the loop works.


16. iceoryx_interproc_demo -- Iceoryx Two-Process Pub/Sub

Purpose: Same payload mix as iceoryx_inproc_demo, but publisher and subscriber run in separate processes communicating through a single shared RouDi daemon.

What it demonstrates:

  • Two IBMW runtimes with distinct runtime_id sharing one RouDi
  • Subscriber must register its Listener before the publisher's first send (iceoryx history depth defaults are small)
  • Cross-process zero-copy: 1 MB payload end-to-end RTT remains in the ~1.5 ms range -- only the SHM offset crosses the process boundary

How to run:

# Build
source /opt/ros/humble/setup.bash
cmake -B build -DIBMW_BUILD_ICEORYX_EXTENSION=ON
cmake --build build -j$(nproc) \
    --target iceoryx_interproc_publisher_app iceoryx_interproc_subscriber_app \
             iox-roudi ibmw_samples_cpp_iceoryx_interproc_demo_build_all

# Run via the helper script (auto-starts RouDi, then sub, then pub)
cd build
DURATION=15 bash start_iceoryx_interproc.sh

# Or run manually in three terminals:
./iox-roudi                                                # Terminal 1
./iceoryx_interproc_subscriber_app cfg/subscriber.yaml     # Terminal 2 (start first)
./iceoryx_interproc_publisher_app cfg/publisher.yaml       # Terminal 3

Required extensions: Iceoryx (IBMW_BUILD_ICEORYX_EXTENSION=ON).


17. multi_driver_demo -- One Module, Multiple Drivers

Purpose: Demonstrates the variadic IBMW_USE_DRIVERS(...) macro, binding two independent transport drivers (DDS + Iceoryx) to typed slots inside a single Module class. This shows that the typed driver-slot ABI composes cleanly across extensions.

What it shows:

  • One user-region module holds both DdsTransportDriver* and IceoryxTransportDriver* member pointers
  • A single IBMW_USE_DRIVERS(IBMW_DRIVER_ENTRY(...), IBMW_DRIVER_ENTRY(...)) declaration covers both drivers (the single-driver helper is IBMW_USE_DRIVER)
  • Covers all four module flavours simultaneously: managed source, standalone app binary, pkg .so, and ibmw-gen regenerated tree (byte-equal)

How to run:

cd build

# App mode (no live transport extension required)
./multi_driver_demo_app ./cfg/multi_driver_demo_app_mode_cfg.yaml

# Pkg mode
./bin/ibmw run ./cfg/multi_driver_demo_cfg.yaml

Required extensions: DDS (IBMW_BUILD_DDS_EXTENSION=ON) and Iceoryx (IBMW_BUILD_ICEORYX_EXTENSION=ON); both are enabled by the full preset.

Minimal code skeleton:

class MultiDriverModule : public ibmw::Module {
 private:
  ibmw::extensions::dds_extension::DdsTransportDriver* dds_drv_ = nullptr;
  ibmw::extensions::iceoryx_extension::IceoryxTransportDriver* iox_drv_ = nullptr;

  // One IBMW_USE_DRIVERS binds both drivers to this module.
  IBMW_USE_DRIVERS(
      IBMW_DRIVER_ENTRY(ibmw::extensions::dds_extension::DdsTransportDriver,
                        dds_drv_),
      IBMW_DRIVER_ENTRY(ibmw::extensions::iceoryx_extension::IceoryxTransportDriver,
                        iox_drv_))
};

See src/samples/cpp/multi_driver_demo/README.md for the full source walkthrough.


All IBMW configuration lives under the ibmw: top-level key. Module-specific configuration uses separate top-level keys matching the module name.

Environment variables are supported: ${MY_VAR} is substituted before parsing.

Top-Level Structure

ibmw:
  extensions: ...      # Extension/plugin loading
  main_loop: ...       # Main thread executor
  watchdog: ...        # Watchdog thread
  scheduling: ...      # User-defined executors
  logging: ...         # Log backends and levels
  memory: ...          # Allocator config (reserved)
  service: ...          # Service (RPC) drivers and routing
  messaging: ...       # Transport drivers and routing
  parameter: ...       # Parameter store (reserved)
  modules: ...         # Package loading, per-module config
  config: ...           # Config subsystem options

MyModule: ...          # Custom config for "MyModule"

Subsystem Initialization Order

Extensions -> Main Loop -> Watchdog -> Executors -> Logging -> Memory -> RPC -> Transport -> Parameters -> Modules -> Configurator


ibmw.extensions

ibmw:
  extensions:
    items:
      - name: <string>       # Unique extension name (required)
        path: <string>       # Path to .so library (required)
        options: { ... }     # Extension-specific config (optional)

ibmw.main_loop

Key Type Default Description
name string "main_loop" Executor name
thread_sched_policy string "" OS scheduling policy (e.g., "SCHED_FIFO:50")
thread_bind_cpu uint32[] [] CPU affinity list

ibmw.watchdog

Key Type Default Description
name string "watchdog" Executor name
thread_sched_policy string "" OS scheduling policy
thread_bind_cpu uint32[] [] CPU affinity list
queue_threshold uint32 0 Queue depth alarm threshold (0=disabled)
threshold_alarm_interval_ms int32 0 Min interval between alarms (ms)

ibmw.scheduling

Array of executor definitions:

ibmw:
  scheduling:
    - name: <string>      # Unique name (required)
      type: <string>      # Executor type (required)
      options: { ... }    # Type-specific options

thread_pool options

Key Type Default Description
thread_num uint32 1 Number of threads
thread_sched_policy string "" OS scheduling policy
thread_bind_cpu uint32[] [] CPU affinity
timeout_alarm_threshold_us uint64 0 Task timeout alarm (us, 0=disabled)
use_system_clock bool false Use system clock for timeouts

dedicated options

Key Type Default Description
thread_sched_policy string "" OS scheduling policy
thread_bind_cpu uint32[] [] CPU affinity
queue_threshold uint32 0 Queue depth alarm (0=disabled)

strand options

Key Type Default Description
bind_thread_pool_executor_name string -- Required. Thread pool to bind to
timeout_alarm_threshold_us uint64 0 Task timeout alarm (us)

timer options

Key Type Default Description
bind_executor string "" Executor to bind to (empty=own thread)
dt_us uint64 1000 Tick resolution (us)
wheel_size size_t[] [1000] Time wheel level sizes
thread_sched_policy string "" OS scheduling policy (own thread only)
thread_bind_cpu uint32[] [] CPU affinity (own thread only)

parallel options

Key Type Default Description
thread_num uint32 1 Number of TBB threads
thread_sched_policy string "" OS scheduling policy
thread_bind_cpu uint32[] [] CPU affinity
queue_threshold uint32 0 Queue depth alarm (0=disabled)

ibmw.logging

Key Type Default Description
core_level string "Info" Framework log level: Trace/Debug/Info/Warn/Error/Fatal/Off
default_module_level string "Info" Default module log level
enable_crash_log bool true Enable signal handler for crash logging
backends array [] Log backend configurations

Console backend (type: console)

Key Type Default Description
color bool true ANSI color output
module_filter string "" Regex filter on module names
pattern string "" Custom log format pattern
log_executor_name string "" Executor for async logging

Rotate file backend (type: rotate_file)

Key Type Default Description
path string "" Log file directory
filename string "" Base filename
max_file_size_m uint32 10 Max file size (MB)
max_file_num uint32 5 Max rotated files
module_filter string "" Regex filter
pattern string "" Custom format pattern
enable_sync bool false Periodic flush
sync_interval_ms uint32 1000 Flush interval (ms)
sync_executor_name string "" Executor for flush task
suffix_with_timestamp bool false Append timestamp to filename

ibmw.service

ibmw:
  service:
    drivers:
      - type: <string>       # local, dds, http, grpc
        options: { ... }
    clients:
      - func_name: <regex>   # Match RPC function names
        enable_drivers: [...]
        enable_filters: [...]
    servers:
      - func_name: <regex>
        enable_drivers: [...]
        enable_filters: [...]

Local RPC driver (type: local)

Key Type Default Description
timeout_executor string "" Executor for client timeout handling
timeout_handle_executor string "" Executor for server timeout handling

DDS RPC driver (type: dds)

The DDS RPC driver enables cross-process RPC over FastDDS without any rcl/rclcpp dependency. It uses native .srv mode: business request/response types are directly serialized by FastDDS using the TypeSupport from gencode (no envelope wrappers). This makes the wire format 100% compatible with ROS2 standard services.

When ros2_compatible is true (the default when loaded from the DDS extension), it uses ROS2 service wire protocol conventions (rq//rr/ topic prefixes, dds_:: type name infixes, URL-encoded names), making the service discoverable and callable by standard ROS2 nodes (ros2 service call, ros2 service list).

Key Type Default Description
ros2_compatible bool true Use ROS2 naming conventions for DDS topics

How it works: Each RPC function maps to a request topic (rq/<service_name>Request) and a reply topic (rr/<service_name>Reply). The DDS type names and CDR payloads are derived from the .srv definition (e.g., example_interfaces::srv::dds_::AddTwoInts_Request_), matching exactly what ROS2 uses. Request/response correlation uses the RTPS WriteParams / SampleInfo.related_sample_identity mechanism -- the same mechanism used by rmw_fastrtps internally.

Routing example:

ibmw:
  service:
    drivers:
      - type: dds
        options:
          ros2_compatible: true
      - type: local
    clients:
      - func_name: "dds:(.*)"     # Route dds:-prefixed functions to DDS
        enable_drivers: [dds]
      - func_name: "(.*)"         # Everything else stays in-process
        enable_drivers: [local]
    servers:
      - func_name: "dds:(.*)"
        enable_drivers: [dds]
      - func_name: "(.*)"
        enable_drivers: [local]

ibmw.messaging

ibmw:
  messaging:
    drivers:
      - type: <string>       # local, dds, mqtt, http, tcp, udp, iceoryx
        options: { ... }
    publishers:
      - topic: <regex>       # Match topic names
        enable_drivers: [...]
        enable_filters: [...]
    subscribers:
      - topic: <regex>
        enable_drivers: [...]
        enable_filters: [...]

Local transport driver (type: local)

Key Type Default Description
subscriber_use_inline_executor bool false Run callbacks in publisher thread
subscriber_executor string "" Default subscriber callback executor

ibmw.modules

ibmw:
  modules:
    packages:
      - path: <string>                 # Path to .so (required)
        enable_modules: [<string>]     # Whitelist (optional)
        disable_modules: [<string>]    # Blacklist (optional)
    modules:
      - name: <string>                 # Module name (required)
        log_level: <string>            # Override log level
        enable: <bool>                 # Enable/disable module
        cfg_file_path: <string>        # Path to module config file

Extension-Specific Options

DDS Extension

Extension options:

Key Type Default Description
domain_id uint32 0 DDS domain ID
participant_name string "" DDS participant name

DDS transport driver options (type: dds under messaging):

Key Type Default Description
ros2_compatible bool false ROS2-compatible topic naming
ros2_topic_prefix string "rt" ROS2 topic prefix
publishers[].topic string -- Topic name
publishers[].qos.history string "keep_last" keep_last or keep_all
publishers[].qos.depth int 10 History depth
publishers[].qos.reliability string "reliable" reliable or best_effort
publishers[].qos.durability string "volatile" volatile or transient_local
subscribers[].topic string -- Topic name or regex
subscribers[].qos.history string "keep_last" keep_last or keep_all
subscribers[].qos.depth int 10 History depth
subscribers[].qos.reliability string "reliable" reliable or best_effort
subscribers[].qos.durability string "volatile" volatile or transient_local
subscribers[].executor string "" Callback executor. An unknown name is a configuration error.
subscribers[].loan_mode string "auto" auto, sync, or async. auto runs synchronously unless executor is set; with executor it dispatches asynchronously. async requires executor. sync ignores executor.
zero_copy.mode string "auto" auto, strict, compat

When DDS subscriber callbacks are dispatched through an executor, loaned DDS samples stay checked out until the executor task completes. Use a single-threaded executor when per-topic callback order matters, and size DDS history/loan pools for the callback hold time.

gRPC Extension

Extension options:

Key Type Default Description
thread_num uint32 1 gRPC thread count
listen_ip string "0.0.0.0" Listen address
listen_port uint16 0 Listen port

gRPC RPC driver options (type: grpc):

Key Type Default Description
clients[].func_name string -- RPC function name
clients[].server_url string -- Remote server URL
servers[].func_name string -- RPC function name

Net (HTTP/TCP/UDP) Extension

Extension options:

Key Type Default Description
thread_num uint32 -- Network I/O threads (required)
http_options.listen_ip string "0.0.0.0" HTTP listen IP
http_options.listen_port uint16 0 HTTP listen port
tcp_options.listen_ip string "0.0.0.0" TCP listen IP
tcp_options.listen_port uint16 0 TCP listen port
udp_options.listen_ip string "0.0.0.0" UDP listen IP
udp_options.listen_port uint16 0 UDP listen port
udp_options.max_pkg_size uint32 65535 Max UDP datagram

MQTT Extension

Extension options:

Key Type Default Description
broker_addr string -- Broker address (required)
client_id string -- Client ID (required)
max_pkg_size_k uint32 0 Max package size KB
reconnect_interval_ms uint32 5000 Reconnect interval
truststore string "" CA cert path (TLS)
client_cert string "" Client cert (TLS)
client_key string "" Client key (TLS)
callback_executor_name string "" Callback executor

MQTT transport driver (type: mqtt) per-topic QoS: 0, 1, or 2.

Iceoryx Extension

Shared-memory zero-copy transport. Requires the iox-roudi daemon to be running before any IBMW process using the iceoryx driver starts.

Extension options (name: iceoryx_extension):

Key Type Default Description
shm_init_size uint64 1024 Initial loaned SHM size in bytes. Doubled automatically by LoanShm/UpdateLoanShm when a payload does not fit.
runtime_id string "iceoryx<pid>" Iceoryx PoshRuntime identifier. Must be unique across processes sharing the same RouDi.

Iceoryx transport driver options (type: iceoryx under messaging):

Key Type Default Description
listener_thread_name string "" Name applied to the subscriber Listener thread (visible in ps -L/htop).
listener_thread_sched_policy string "" OS scheduling policy, e.g. "SCHED_FIFO:50".
listener_thread_bind_cpu uint32[] [] CPU affinity list for the Listener thread.

Topic-to-iceoryx URL mapping: each registered IBMW topic becomes the 3-segment iceoryx ServiceDescription /transport / <UrlEncode(topic_name)> / <UrlEncode(msg_type)>. Each segment is truncated to iox::MAX_RUNTIME_NAME_LENGTH characters with a ... ellipsis if longer (a warning is logged).

Minimal example (single process):

ibmw:
  extensions:
    items:
      - name: iceoryx_extension
        path: ./lib/libibmw_iceoryx_transport.so
        options:
          shm_init_size: 256        # tiny, exercises auto-expansion
          runtime_id: my_iox_app
  messaging:
    drivers:
      - type: iceoryx
        options:
          listener_thread_name: iox_listen
    publishers:
      - topic: "(.*)"
        enable_drivers: [iceoryx]
    subscribers:
      - topic: "(.*)"
        enable_drivers: [iceoryx]

For two-process pub/sub, give each process a different runtime_id (both pointing at the same RouDi) and start the subscriber before the publisher's first send.

ROS2 Extension

Key Type Default Description
node_name string -- ROS2 node name (required)
executor_type string "SingleThreaded" ROS2 executor type
executor_thread_num uint32 1 Thread count (MultiThreaded)

Record/Playback Extension

Key Type Default Description
service_name string "" RPC service name
timer_executor string "" Timer executor
type_support_pkgs[].path string -- Type support .so
record_actions[].name string -- Action name
record_actions[].options.bag_path string -- Bag storage dir
record_actions[].options.mode string -- imd or signal
record_actions[].options.topic_meta_list[].topic string -- Topic
record_actions[].options.topic_meta_list[].msg_type string -- Type

Complete Example

ibmw:
  extensions:
    items:
      - name: dds_ext
        path: ./lib/libibmw_dds_transport.so
        options:
          domain_id: 0
          participant_name: my_node

  scheduling:
    - name: work_pool
      type: thread_pool
      options:
        thread_num: 4

    - name: timer_exec
      type: timer
      options:
        dt_us: 1000

  logging:
    core_level: Info
    default_module_level: Info
    backends:
      - type: console
        options:
          color: true
      - type: rotate_file
        options:
          path: /var/log/ibmw
          filename: app.log
          max_file_size_m: 50
          max_file_num: 10

  messaging:
    drivers:
      - type: local
        options:
          subscriber_executor: work_pool
      - type: dds
        options:
          subscribers:
            - topic: /sensor_data
              qos:
                reliability: best_effort
                depth: 1
              executor: work_pool
    publishers:
      - topic: ".*"
        enable_drivers: [local, dds]
    subscribers:
      - topic: ".*"
        enable_drivers: [local, dds]

  service:
    drivers:
      - type: local
        options:
          timeout_executor: timer_exec
    clients:
      - func_name: ".*"
        enable_drivers: [local]
    servers:
      - func_name: ".*"
        enable_drivers: [local]

  modules:
    packages:
      - path: ./lib/libmy_module_pkg.so
    modules:
      - name: SensorModule
        log_level: Debug
      - name: ProcessorModule
        log_level: Info

SensorModule:
  device: /dev/ttyUSB0
  publish_rate_hz: 100

ProcessorModule:
  algorithm: kalman_filter
  output_topic: /processed_data

Troubleshooting

DDS demos fail with shared library errors

Ensure the ROS2 libraries are on the library path:

source /opt/ros/humble/setup.bash
export LD_LIBRARY_PATH="/usr/lib/x86_64-linux-gnu:/opt/ros/humble/lib:<ibmw-build-dir>/lib:<ibmw-build-dir>:<ibmw-build-dir>/bin:$LD_LIBRARY_PATH"

Build fails with "FastCDR not found"

Install FastCDR or ensure _deps/fastcdr-install/ exists. With ROS2 Humble:

sudo apt install ros-humble-fastrtps

DDS demos hang on shutdown

Some DDS demos take 30-60 seconds to shut down due to DDS discovery cleanup. Use timeout -s KILL <seconds> to force-terminate:

timeout -s KILL 10 ./dds_native_app ./dds_native_cfg.yaml

Module .so not found

Ensure the path in your YAML config is correct relative to the working directory. Use absolute paths or ${ENV_VAR} substitution for portability.

Permission denied for real-time scheduling

Real-time executor demos (SCHED_FIFO, SCHED_RR) require elevated privileges:

sudo ./bin/ibmw run ./cfg/examples_cpp_executor_real_time_cfg.yaml

Or set capabilities:

sudo setcap cap_sys_nice+ep ./bin/ibmw

ibmw-gen project cmake fails with "Could not find a configuration file for package ibmw"

After cmake --install . --prefix ., CMake generates ibmw-config.cmake and ibmw-config-version.cmake in the build directory. These stale files can interfere with ibmw-gen projects that use -DCMAKE_PREFIX_PATH=<build-dir>, causing CMake to find an incomplete config instead of the correct targets.

Fix: Delete the stale files after install:

cd build
rm -f ibmw-config.cmake ibmw-config-version.cmake

This is already included in the standard build instructions above.