Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation #1

Merged
merged 27 commits into from
May 2, 2024
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
40 changes: 40 additions & 0 deletions .github/workflows/industrial_ci_action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright (C) 2024 Nobleo Technology B.V.
#
# SPDX-License-Identifier: Apache-2.0

# This config uses industrial_ci (https://github.com/ros-industrial/industrial_ci.git).
# For troubleshooting, see README (https://github.com/ros-industrial/industrial_ci/blob/master/README.rst)

name: CI

on:
push:
pull_request:
workflow_dispatch:

jobs:
industrial_ci:
name: ROS ${{ matrix.ROS_DISTRO }} (${{ matrix.ROS_REPO }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ROS_DISTRO:
- humble
- iron
ROS_REPO:
- main
env:
CCACHE_DIR: "${{ github.workspace }}/.ccache"
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
with:
path: ${{ env.CCACHE_DIR }}
key: ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-${{github.run_id}}
restore-keys: |
ccache-${{ matrix.ROS_DISTRO }}-${{ matrix.ROS_REPO }}-
- uses: 'ros-industrial/industrial_ci@master'
env:
ROS_DISTRO: ${{ matrix.ROS_DISTRO }}
ROS_REPO: ${{ matrix.ROS_REPO }}
80 changes: 80 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Copyright (C) 2024 Nobleo Technology B.V.
#
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.8)
project(ros2_socketcan_bridge)

set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 20)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(ament_cmake_ros REQUIRED)
find_package(can_msgs REQUIRED)
find_package(fmt REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rclcpp_components REQUIRED)

add_library(socketcan_bridge_component
src/socketcan_bridge.cpp
src/socketcan_bridge_node.cpp
)
target_include_directories(socketcan_bridge_component PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
ament_target_dependencies(socketcan_bridge_component
can_msgs
rclcpp
rclcpp_components
)
target_link_libraries(socketcan_bridge_component fmt::fmt)

rclcpp_components_register_node(
socketcan_bridge_component
PLUGIN "ros2_socketcan_bridge::SocketCanBridgeNode"
EXECUTABLE socketcan_bridge
)

install(
TARGETS socketcan_bridge_component
EXPORT export_${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)

# EventsExecutor is supported from rclcpp version 21 or greater (iron)
if(rclcpp_VERSION_MAJOR GREATER_EQUAL 21)
message(STATUS "EventsExecutor support enabled")
add_executable(socketcan_bridge_ee src/socketcan_bridge_ee.cpp)
target_link_libraries(socketcan_bridge_ee socketcan_bridge_component)

install(TARGETS socketcan_bridge_ee
DESTINATION lib/${PROJECT_NAME}
)
else()
message(STATUS "EventsExecutor support disabled")
endif()

if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
endif()

ament_export_include_directories(
include
)
ament_export_libraries(
socketcan_bridge_component
)
ament_export_targets(
export_${PROJECT_NAME}
)

ament_package()
51 changes: 49 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,49 @@
# nobleo_socketcan_bridge
Simple wrapper around SocketCAN
<!--
Copyright (C) 2024 Nobleo Technology B.V.

SPDX-License-Identifier: Apache-2.0
-->

# ros2_socketcan_bridge

This package provides functionality to expose CAN frames from SocketCAN to ROS2 topics.

## Overview

This is a from-scratch re-implementation of [socketcan_bridge] from ROS1.
There is a different ROS2 package [ros2_socketcan] which is similar to this package.
The differences between this package and [ros2_socketcan] are:

- No loopback, i.e. CAN frames that are send are not received by the same node
- No lifecycle management, just run it
- Less CPU usage

## Nodes

The main node (`socketcan_bridge`) is also available as dynamically loadable component.
The node `socketcan_bridge_ee` is also provided that uses the `EventsExecutor` that runs a bit more efficient.

### socketcan_bridge

#### Subscribed Topics

* `~/tx` ([can_msgs/Frame])
Messages received here will be sent to the SocketCAN device.

#### Published Topics

* `~/rx` ([can_msgs/Frame])
Frames received on the SocketCAN device are published on this topic.

#### Parameters

* `interface`
Name of the SocketCAN device, by default these devices are named can0 and upwards.
* `read_timeout`
Maximum duration to wait for data on the file descriptor

[can_msgs/Frame]: https://github.com/ros-industrial/ros_canopen/blob/dashing-devel/can_msgs/msg/Frame.msg

[socketcan_bridge]: https://wiki.ros.org/socketcan_bridge

[ros2_socketcan]: https://github.com/autowarefoundation/ros2_socketcan
52 changes: 52 additions & 0 deletions include/ros2_socketcan_bridge/socketcan_bridge.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (C) 2024 Nobleo Technology B.V.
//
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <string>
#include <thread>

#include "can_msgs/msg/frame.hpp"
#include "rclcpp/clock.hpp"
#include "rclcpp/logger.hpp"

namespace rclcpp
{
class Node;
class Clock;
} // namespace rclcpp

namespace ros2_socketcan_bridge
{
class SocketCanBridge
{
public:
using CanCallback = std::function<void(const can_msgs::msg::Frame &)>;

SocketCanBridge(
const rclcpp::Logger & logger, rclcpp::Clock::SharedPtr clock, const std::string & interface,
double read_timeout, const CanCallback & receive_callback);

~SocketCanBridge() { close(); }

SocketCanBridge(const SocketCanBridge &) = delete;
SocketCanBridge(SocketCanBridge &&) noexcept = delete;
SocketCanBridge & operator=(const SocketCanBridge & other) = delete;
SocketCanBridge & operator=(SocketCanBridge && other) noexcept = delete;

void send(const can_msgs::msg::Frame & msg);

void close();

private:
void receive_loop(std::stop_token stoken);

rclcpp::Logger logger_;
rclcpp::Clock::SharedPtr clock_;
int socket_;
CanCallback receive_callback_;
std::jthread receive_thread_;
};

} // namespace ros2_socketcan_bridge
22 changes: 22 additions & 0 deletions include/ros2_socketcan_bridge/socketcan_bridge_node.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (C) 2024 Nobleo Technology B.V.
//
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "rclcpp/node.hpp"
#include "ros2_socketcan_bridge/socketcan_bridge.hpp"

namespace ros2_socketcan_bridge
{
class SocketCanBridgeNode : public rclcpp::Node
{
public:
explicit SocketCanBridgeNode(const rclcpp::NodeOptions & options);

private:
rclcpp::Publisher<can_msgs::msg::Frame>::SharedPtr can_pub;
SocketCanBridge bridge;
rclcpp::Subscription<can_msgs::msg::Frame>::SharedPtr can_sub;
};
} // namespace ros2_socketcan_bridge
40 changes: 40 additions & 0 deletions package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>

<!--
Copyright (C) 2024 Nobleo Technology B.V.

SPDX-License-Identifier: Apache-2.0
-->

<package format="3">
<name>ros2_socketcan_bridge</name>
<version>0.0.0</version>
<description>Simple wrapper around SocketCAN</description>
<maintainer email="[email protected]">Ramon Wijnands</maintainer>
<license>TODO: License declaration</license>
<author email="[email protected]">Ramon Wijnands</author>

<buildtool_depend>ament_cmake</buildtool_depend>

<depend>ament_cmake_ros</depend>
<depend>can_msgs</depend>
<depend>fmt</depend>
<depend>rclcpp</depend>
<depend>rclcpp_components</depend>

<test_depend>ament_lint_auto</test_depend>

<!-- Linters -->
<test_depend>ament_cmake_clang_format</test_depend>
<!-- <test_depend>ament_cmake_clang_tidy</test_depend> -->
<!-- <test_depend>ament_cmake_copyright</test_depend> -->
<test_depend>ament_cmake_cppcheck</test_depend>
<test_depend>ament_cmake_cpplint</test_depend>
<test_depend>ament_cmake_lint_cmake</test_depend>
<test_depend>ament_cmake_xmllint</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
Loading
Loading