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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ RUN curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \

RUN apt-get update && apt-get install -y \
ros-jazzy-desktop-full \
ros-jazzy-test-msgs \
python3-colcon-common-extensions \
python3-rosdep \
&& rm -rf /var/lib/apt/lists/*
Expand Down
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
// Comment this out if you don't want zsh and oh-my-zsh
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true,
"configureZshAsDefaultShell": true,
"configureZshAsDefaultShell": false,
"installOhMyZsh": true
},
"ghcr.io/devcontainers/features/git:1": {},
Expand Down Expand Up @@ -47,7 +47,7 @@
"christian-kohler.path-intellisense"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.defaultProfile.linux": "bash",
"python.defaultInterpreterPath": "/usr/bin/python3",
"cmake.configureOnOpen": false
}
Expand Down
64 changes: 33 additions & 31 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up ROS 2 Jazzy
uses: ros-tooling/setup-ros@v0.7
Expand All @@ -23,7 +25,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang-format clang-tidy
sudo apt-get install -y clang-format clang-tidy ros-jazzy-test-msgs
source /opt/ros/jazzy/setup.bash
rosdep update
rosdep install --from-paths src --ignore-src -r -y
Expand Down Expand Up @@ -72,6 +74,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up ROS 2 Jazzy
uses: ros-tooling/setup-ros@v0.7
Expand All @@ -81,7 +85,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y lcov
sudo apt-get install -y lcov ros-jazzy-test-msgs
source /opt/ros/jazzy/setup.bash
rosdep update
rosdep install --from-paths src --ignore-src -r -y
Expand All @@ -107,38 +111,36 @@ jobs:

- name: Generate coverage report
run: |
# Capture coverage data with error tolerance
# Capture coverage data
# IMPORTANT: --ignore-errors gcov is required to skip .gcda files
# that have missing corresponding .gcno files (common in multi-package builds)
lcov --capture --directory build --output-file coverage.raw.info \
--ignore-errors mismatch,negative,empty || true

# Check if we have valid coverage data
if [ -s coverage.raw.info ] && grep -q 'SF:' coverage.raw.info; then
# Extract only our source code (exclude gtest_vendor and other ROS dependencies)
lcov --extract coverage.raw.info \
'*/ros2_medkit/src/*/src/*' \
'*/ros2_medkit/src/*/include/*' \
--output-file coverage.info \
--ignore-errors unused,empty || true

if [ -s coverage.info ]; then
lcov --list coverage.info
# Generate HTML report
genhtml coverage.info --output-directory coverage_html \
--ignore-errors source || true
else
echo "Filtered coverage.info is empty, using raw coverage"
cp coverage.raw.info coverage.info
lcov --list coverage.info || true
genhtml coverage.info --output-directory coverage_html \
--ignore-errors source || true
fi
else
echo "No valid coverage data found in coverage.raw.info"
echo "Creating empty coverage file to prevent upload failure"
touch coverage.info
mkdir -p coverage_html
--ignore-errors mismatch,negative,empty,gcov

# Validate coverage data exists
if [ ! -s coverage.raw.info ] || ! grep -q 'SF:' coverage.raw.info; then
echo "::error::No valid coverage data found in coverage.raw.info"
exit 1
fi

# Extract only our source code (exclude gtest_vendor and other ROS dependencies)
lcov --extract coverage.raw.info \
'*/ros2_medkit/src/*/src/*' \
'*/ros2_medkit/src/*/include/*' \
--output-file coverage.info \
--ignore-errors unused,empty

# Validate filtered coverage
if [ ! -s coverage.info ]; then
echo "::error::Filtered coverage.info is empty - no source files matched"
exit 1
fi

lcov --list coverage.info

# Generate HTML report
genhtml coverage.info --output-directory coverage_html --ignore-errors source

- name: Upload coverage HTML report as artifact
uses: actions/upload-artifact@v4
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Python
uses: actions/setup-python@v5
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/dynamic_message_introspection"]
path = src/dynamic_message_introspection
url = https://github.com/selfpatch/dynamic_message_introspection.git
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,14 @@ so the same concepts can be used across robots, vehicles, and other embedded sys
```bash
mkdir -p ~/ros2_medkit_ws/src
cd ~/ros2_medkit_ws/src
git clone https://github.com/selfpatch/ros2_medkit.git
git clone --recurse-submodules https://github.com/selfpatch/ros2_medkit.git
cd ~/ros2_medkit_ws
rosdep install --from-paths src --ignore-src -r -y
```

> **Note:** If you cloned without `--recurse-submodules`, run:
> `git submodule update --init --recursive`

### 2. Build

```bash
Expand Down
2 changes: 2 additions & 0 deletions docs/Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ INPUT = ../src/ros2_medkit_gateway/include \
../src/ros2_medkit_fault_reporter/src \
../src/ros2_medkit_diagnostic_bridge/include \
../src/ros2_medkit_diagnostic_bridge/src \
../src/ros2_medkit_serialization/include \
../src/ros2_medkit_serialization/src \
../src/ros2_medkit_msgs
RECURSIVE = YES
FILE_PATTERNS = *.hpp *.cpp *.h
Expand Down
20 changes: 20 additions & 0 deletions docs/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,23 @@ Bridge node that converts ROS 2 /diagnostics messages to FaultManager faults.

.. doxygenclass:: ros2_medkit_diagnostic_bridge::DiagnosticBridgeNode
:members:

ros2_medkit_serialization
-------------------------

Runtime JSON ↔ ROS 2 message serialization library.

.. doxygenclass:: ros2_medkit_serialization::JsonSerializer
:members:

.. doxygenclass:: ros2_medkit_serialization::TypeCache
:members:

.. doxygenclass:: ros2_medkit_serialization::SerializationError
:members:

.. doxygenclass:: ros2_medkit_serialization::TypeNotFoundError
:members:

.. doxygenclass:: ros2_medkit_serialization::JsonConversionError
:members:
1 change: 1 addition & 0 deletions docs/design/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ This section contains design documentation for the ros2_medkit project packages.
ros2_medkit_fault_manager/index
ros2_medkit_fault_reporter/index
ros2_medkit_gateway/index
ros2_medkit_serialization/index

1 change: 1 addition & 0 deletions docs/design/ros2_medkit_serialization
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ endpoints for data access, operations, configurations, and fault management.

.. note::

Version 0.1.0 First public release.
Version 0.1.0 - First public release still under construction...

Quick Links
-----------
Expand Down
8 changes: 7 additions & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,15 @@ ros2_medkit is currently distributed as source code. Binary packages will be ava

.. code-block:: bash

git clone https://github.com/selfpatch/ros2_medkit.git
git clone --recurse-submodules https://github.com/selfpatch/ros2_medkit.git
cd ros2_medkit

If you already cloned without submodules, initialize them:

.. code-block:: bash

git submodule update --init --recursive

3. **Install dependencies**

.. code-block:: bash
Expand Down
7 changes: 7 additions & 0 deletions docs/introduction.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,19 @@ ros2_medkit consists of several ROS 2 packages:
**ros2_medkit_gateway**
The main HTTP gateway node. Discovers ROS 2 entities and exposes them via REST API.

**ros2_medkit_serialization**
Runtime JSON ↔ ROS 2 message serialization library using dynmsg. Enables native
message handling without compile-time type dependencies.

**ros2_medkit_fault_manager**
Stores and manages fault lifecycle. Provides ROS 2 services for fault operations.

**ros2_medkit_fault_reporter**
Client library for reporting faults from your ROS 2 nodes.

**ros2_medkit_diagnostic_bridge**
Bridge node that converts standard ROS 2 ``/diagnostics`` messages to fault manager faults.

**ros2_medkit_msgs**
Message and service definitions for fault management.

Expand Down
10 changes: 8 additions & 2 deletions docs/tutorials/devcontainer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ Prerequisites
Quick Start
-----------

1. **Clone the repository:**
1. **Clone the repository with submodules:**

.. code-block:: bash

git clone https://github.com/selfpatch/ros2_medkit.git
git clone --recurse-submodules https://github.com/selfpatch/ros2_medkit.git
cd ros2_medkit

If you already cloned without submodules:

.. code-block:: bash

git submodule update --init --recursive

2. **Open in VS Code:**

.. code-block:: bash
Expand Down
1 change: 1 addition & 0 deletions src/dynamic_message_introspection
4 changes: 2 additions & 2 deletions src/ros2_medkit_gateway/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ find_package(action_msgs REQUIRED)
find_package(rclcpp_action REQUIRED)
find_package(example_interfaces REQUIRED)
find_package(ros2_medkit_msgs REQUIRED)
find_package(ros2_medkit_serialization REQUIRED)

# Find cpp-httplib using pkg-config
find_package(PkgConfig REQUIRED)
Expand Down Expand Up @@ -103,8 +104,6 @@ add_library(gateway_lib STATIC
src/config.cpp
src/gateway_node.cpp
src/discovery_manager.cpp
src/ros2_cli_wrapper.cpp
src/output_parser.cpp
src/data_access_manager.cpp
src/type_introspection.cpp
src/native_topic_sampler.cpp
Expand Down Expand Up @@ -139,6 +138,7 @@ ament_target_dependencies(gateway_lib
ament_index_cpp
action_msgs
ros2_medkit_msgs
ros2_medkit_serialization
)

target_link_libraries(gateway_lib
Expand Down
4 changes: 2 additions & 2 deletions src/ros2_medkit_gateway/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ curl http://localhost:8080/api/v1/components/nonexistent/data

**Behavior:**
- Returns array of all topics under the component's namespace
- Each topic is sampled once with `ros2 topic echo --once`
- Each topic is sampled once using native rclcpp GenericSubscription
- Empty array `[]` returned if component has no topics
- 3-second timeout per topic to accommodate slow-publishing topics
- Configurable timeout per topic (default: 2 seconds)

**Use Cases:**
- Remote diagnostics - Read all sensor values from a component
Expand Down
17 changes: 9 additions & 8 deletions src/ros2_medkit_gateway/config/gateway_params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,20 @@ ros2_medkit_gateway:
# Valid range: 1-50
max_parallel_topic_samples: 20

# Timeout (in seconds) for sampling topic data via ros2 topic echo
# Timeout (in seconds) for sampling topic data via native GenericSubscription
# Topics without publishers return immediately with metadata (no timeout wait)
# This timeout only applies when topics have active publishers
# Valid range: 0.1-30.0
topic_sample_timeout_sec: 2.0

# NOTE: Native sampling is always enabled
# The gateway uses native rclcpp APIs for topic discovery and sampling:
# - Uses node->get_topic_names_and_types() instead of `ros2 topic list`
# - Checks publisher counts before sampling to skip idle topics immediately
# - Returns metadata instantly for topics without publishers (no CLI timeout wait)
# This significantly improves UX when many topics are idle (e.g., robot waiting for commands)
# CLI is only used for publishing (ros2 topic pub)
# NOTE: Native-only implementation
# The gateway uses native rclcpp APIs for all ROS 2 interactions:
# - Topic discovery: node->get_topic_names_and_types()
# - Topic sampling: rclcpp::GenericSubscription + JsonSerializer
# - Topic publishing: rclcpp::GenericPublisher + JsonSerializer
# - Service calls: rclcpp::GenericClient + JsonSerializer
# - Action operations: Internal action services via GenericClient
# No ROS 2 CLI dependencies - pure C++ implementation using ros2_medkit_serialization.

# Authentication Configuration (REQ_INTEROP_086, REQ_INTEROP_087)
# JWT-based authentication with Role-Based Access Control (RBAC)
Expand Down
20 changes: 8 additions & 12 deletions src/ros2_medkit_gateway/design/architecture.puml
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,12 @@ package "ros2_medkit_gateway" {
+ sample_topics_parallel(): vector<TopicSampleResult>
}

class ROS2CLIWrapper {
+ exec(): string
+ is_command_available(): bool
+ escape_shell_arg(): string
}

class OutputParser {
+ parse_yaml(): json
- yaml_to_json(): json
class JsonSerializer {
+ serialize(): SerializedMessage
+ deserialize(): json
+ to_json(): json
+ from_json(): RosMessage_Cpp
+ yaml_to_json(): json {static}
}

class Area {
Expand Down Expand Up @@ -103,9 +100,8 @@ DiscoveryManager --> "rclcpp::Node" : uses
RESTServer --> GatewayNode : uses
RESTServer --> DataAccessManager : uses

' DataAccessManager owns utility classes
DataAccessManager *--> ROS2CLIWrapper : owns (publishing)
DataAccessManager *--> OutputParser : owns
' DataAccessManager owns utility classes (native implementation)
DataAccessManager *--> JsonSerializer : owns (serialization)
DataAccessManager *--> NativeTopicSampler : owns

' NativeTopicSampler uses Node interface
Expand Down
Loading