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
99 changes: 99 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
BasedOnStyle: Google
Language: Cpp

ColumnLimit: 120
IndentWidth: 2
TabWidth: 2
UseTab: Never
MaxEmptyLinesToKeep: 1

Standard: c++17

# Pointers & References - ROS 2 style (spaces around * and &)
DerivePointerAlignment: false
PointerAlignment: Middle
ReferenceAlignment: Middle

# Spaces
SpaceBeforeParens: ControlStatements
SpacesInAngles: false
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true

# Templates
AlwaysBreakTemplateDeclarations: Yes

# Binary/Ternary operators
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true

# Constructor initializers (modern syntax - replaces deprecated options)
BreakConstructorInitializers: BeforeComma
ConstructorInitializerIndentWidth: 2
PackConstructorInitializers: NextLine

# Short statements
AllowShortBlocksOnASingleLine: Never
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortLambdasOnASingleLine: Empty

# Braces
InsertBraces: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false

# Comments
AlignTrailingComments: true
SpacesBeforeTrailingComments: 2
ReflowComments: true

# Includes - ROS 2 friendly
SortIncludes: CaseSensitive
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^<.*>'
Priority: 1
- Regex: '^"rclcpp/.*"'
Priority: 2
- Regex: '^".*"'
Priority: 3

# Qualifiers
QualifierAlignment: Custom
QualifierOrder: ['static', 'inline', 'constexpr', 'const', 'volatile', 'type']

# Modern C++ options
InsertNewlineAtEOF: true
SeparateDefinitionBlocks: Leave
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock

# Alignment
AlignAfterOpenBracket: Align
AlignOperands: Align
AlignEscapedNewlines: Left

# Penalties (prefer breaking at certain places)
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
35 changes: 35 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
# Clang-tidy configuration for ROS 2 Medkit Gateway
# ROS 2 uses snake_case convention for methods/functions, so we disable
# identifier-naming rules that would conflict with ROS 2 style guidelines.
Checks: '-*,
performance-*,
-performance-enum-size,
llvm-namespace-comment,
modernize-redundant-void-arg,
modernize-use-nullptr,
modernize-use-default,
modernize-use-override,
modernize-loop-convert,
modernize-make-shared,
modernize-make-unique,
modernize-avoid-bind,
misc-unused-parameters,
readability-braces-around-statements,
readability-named-parameter,
readability-redundant-smartptr-get,
readability-redundant-string-cstr,
readability-simplify-boolean-expr,
readability-container-size-empty,
readability-static-definition-in-anonymous-namespace,
'
HeaderFilterRegex: ''
CheckOptions:
- key: llvm-namespace-comment.ShortNamespaceLines
value: '10'
- key: llvm-namespace-comment.SpacesBeforeComments
value: '2'
- key: misc-unused-parameters.StrictMode
value: '1'
- key: readability-braces-around-statements.ShortStatementLines
value: '2'
1 change: 1 addition & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
yq \
ca-certificates \
clang-format \
clang-tidy \
graphviz \
plantuml \
&& locale-gen en_US.UTF-8 \
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y clang-format clang-tidy
rosdep update
rosdep install --from-paths src --ignore-src -r -y

Expand All @@ -33,12 +34,21 @@ jobs:
--cmake-args -DCMAKE_BUILD_TYPE=Release \
--event-handlers console_direct+

- name: Run tests
- name: Run linters (clang-format, clang-tidy, etc.)
run: |
source /opt/ros/jazzy/setup.bash
source install/setup.bash
colcon test --return-code-on-test-failure \
--ctest-args -L linter \
--event-handlers console_direct+

- name: Run unit and integration tests
timeout-minutes: 15
run: |
source /opt/ros/jazzy/setup.bash
source install/setup.bash
colcon test --return-code-on-test-failure \
--ctest-args -LE linter \
--event-handlers console_direct+

- name: Show test results
Expand Down
10 changes: 8 additions & 2 deletions src/ros2_medkit_gateway/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ project(ros2_medkit_gateway)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
Expand Down Expand Up @@ -63,8 +64,13 @@ install(DIRECTORY launch config
if(BUILD_TESTING)
# Linting and code quality checks
find_package(ament_lint_auto REQUIRED)
# Exclude uncrustify since we use clang-format
list(APPEND AMENT_LINT_AUTO_EXCLUDE ament_cmake_uncrustify)

# Use custom clang-format and clang-tidy configs from repo root
set(ament_cmake_clang_format_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../../.clang-format")
set(ament_cmake_clang_tidy_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../../.clang-tidy")

# Exclude uncrustify (conflicts with clang-format) and cpplint (conflicts with clang-format include order)
list(APPEND AMENT_LINT_AUTO_EXCLUDE ament_cmake_uncrustify ament_cmake_cpplint)
ament_lint_auto_find_test_dependencies()

# Add GTest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,66 +15,53 @@
#pragma once

#include <memory>
#include <nlohmann/json.hpp>
#include <rclcpp/rclcpp.hpp>
#include <string>
#include <vector>

#include <rclcpp/rclcpp.hpp>
#include <nlohmann/json.hpp>

#include "ros2_medkit_gateway/ros2_cli_wrapper.hpp"
#include "ros2_medkit_gateway/output_parser.hpp"
#include "ros2_medkit_gateway/ros2_cli_wrapper.hpp"

namespace ros2_medkit_gateway {

using json = nlohmann::json;

class DataAccessManager {
public:
explicit DataAccessManager(rclcpp::Node* node);
public:
explicit DataAccessManager(rclcpp::Node * node);

/**
* @brief Get a single sample from a specific topic
*/
json get_topic_sample(
const std::string& topic_name,
double timeout_sec = 3.0
);
/**
* @brief Get a single sample from a specific topic
*/
json get_topic_sample(const std::string & topic_name, double timeout_sec = 3.0);

/**
* @brief Get all data from topics under a component's namespace
*/
json get_component_data(
const std::string& component_namespace,
double timeout_sec = 3.0
);
/**
* @brief Get all data from topics under a component's namespace
*/
json get_component_data(const std::string & component_namespace, double timeout_sec = 3.0);

/**
* @brief Publish data to a specific topic
* @param topic_path Full topic path (e.g., /chassis/brakes/command)
* @param msg_type ROS 2 message type (e.g., std_msgs/msg/Float32)
* @param data JSON data to publish
* @param timeout_sec Timeout for the publish operation
* @return JSON with publish status
*/
json publish_to_topic(
const std::string& topic_path,
const std::string& msg_type,
const json& data,
double timeout_sec = 5.0
);
/**
* @brief Publish data to a specific topic
* @param topic_path Full topic path (e.g., /chassis/brakes/command)
* @param msg_type ROS 2 message type (e.g., std_msgs/msg/Float32)
* @param data JSON data to publish
* @param timeout_sec Timeout for the publish operation
* @return JSON with publish status
*/
json publish_to_topic(const std::string & topic_path, const std::string & msg_type, const json & data,
double timeout_sec = 5.0);

private:
/**
* @brief Find all topics under a given namespace
*/
std::vector<std::string> find_component_topics(
const std::string& component_namespace
);
private:
/**
* @brief Find all topics under a given namespace
*/
std::vector<std::string> find_component_topics(const std::string & component_namespace);

rclcpp::Node* node_;
std::unique_ptr<ROS2CLIWrapper> cli_wrapper_;
std::unique_ptr<OutputParser> output_parser_;
int max_parallel_samples_;
rclcpp::Node * node_;
std::unique_ptr<ROS2CLIWrapper> cli_wrapper_;
std::unique_ptr<OutputParser> output_parser_;
int max_parallel_samples_;
};

} // namespace ros2_medkit_gateway
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,25 @@

#pragma once

#include <rclcpp/rclcpp.hpp>
#include <string>
#include <vector>

#include <rclcpp/rclcpp.hpp>

#include "ros2_medkit_gateway/models.hpp"

namespace ros2_medkit_gateway {

class DiscoveryManager {
public:
explicit DiscoveryManager(rclcpp::Node* node);
public:
explicit DiscoveryManager(rclcpp::Node * node);

std::vector<Area> discover_areas();
std::vector<Component> discover_components();
std::vector<Area> discover_areas();
std::vector<Component> discover_components();

private:
std::string extract_area_from_namespace(const std::string& ns);
private:
std::string extract_area_from_namespace(const std::string & ns);

rclcpp::Node* node_;
rclcpp::Node * node_;
};

} // namespace ros2_medkit_gateway
30 changes: 18 additions & 12 deletions src/ros2_medkit_gateway/include/ros2_medkit_gateway/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,32 @@ namespace ros2_medkit_gateway {

/// Exception thrown when a topic is not available or times out
class TopicNotAvailableException : public std::runtime_error {
public:
explicit TopicNotAvailableException(const std::string& topic)
: std::runtime_error("Topic not available: " + topic), topic_(topic) {}
public:
explicit TopicNotAvailableException(const std::string & topic)
: std::runtime_error("Topic not available: " + topic), topic_(topic) {
}

const std::string& topic() const noexcept { return topic_; }
const std::string & topic() const noexcept {
return topic_;
}

private:
std::string topic_;
private:
std::string topic_;
};

/// Exception thrown when a required command (e.g., ros2 CLI) is not available
class CommandNotAvailableException : public std::runtime_error {
public:
explicit CommandNotAvailableException(const std::string& command)
: std::runtime_error("Command not available: " + command), command_(command) {}
public:
explicit CommandNotAvailableException(const std::string & command)
: std::runtime_error("Command not available: " + command), command_(command) {
}

const std::string& command() const noexcept { return command_; }
const std::string & command() const noexcept {
return command_;
}

private:
std::string command_;
private:
std::string command_;
};

} // namespace ros2_medkit_gateway
Loading