diff --git a/README.md b/README.md index ca0624b..51365c4 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,21 @@ my_can_socket->send(frame); ``` +### Time Stamps and `socketcan_adapter` + +Each message carries `receive_time` (`std::chrono::steady_clock`) and `bus_time` (`std::chrono::system_clock`), both default to the epoch. + +Constructing a message from a [`polymath::socketcan::CanFrame`](https://github.com/polymathrobotics/socketcan_adapter) decodes the frame and copies both time stamps. + +```c++ +polymath::socketcan::CanFrame frame = ...; +my_can_library_name::MessageName message{frame}; +message.receive_time; +message.bus_time; +``` + +This makes `socketcan_adapter` a dependency of `dbc_gen_cpp`, installed by `rosdep install`. + ### Signal Value Maps (Enums) DBC value tables (`VAL_` lines, and the global `VAL_TABLE_`) map raw signal values diff --git a/dbc_gen_cpp/CMakeLists.txt b/dbc_gen_cpp/CMakeLists.txt index fe23ed3..a197a24 100644 --- a/dbc_gen_cpp/CMakeLists.txt +++ b/dbc_gen_cpp/CMakeLists.txt @@ -13,6 +13,7 @@ endif() find_package(ament_cmake REQUIRED) find_package(ament_cmake_python REQUIRED) +find_package(socketcan_adapter REQUIRED) ament_python_install_package(${PROJECT_NAME} SCRIPTS_DESTINATION bin @@ -23,6 +24,7 @@ target_include_directories(${PROJECT_NAME} INTERFACE $ $ ) +target_link_libraries(${PROJECT_NAME} INTERFACE socketcan_adapter::socketcan_adapter) install( TARGETS ${PROJECT_NAME} @@ -50,6 +52,7 @@ if(BUILD_TESTING) endif() ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET) +ament_export_dependencies(socketcan_adapter) ament_package( CONFIG_EXTRAS "dbc_gen_cpp-extras.cmake" ) diff --git a/dbc_gen_cpp/dbc_gen_cpp/templates/can.hpp.j2 b/dbc_gen_cpp/dbc_gen_cpp/templates/can.hpp.j2 index 718068d..97074cd 100644 --- a/dbc_gen_cpp/dbc_gen_cpp/templates/can.hpp.j2 +++ b/dbc_gen_cpp/dbc_gen_cpp/templates/can.hpp.j2 @@ -5,9 +5,11 @@ #include +#include #include #include +#include "socketcan_adapter/can_frame.hpp" #include "{{ library_name }}/{{ c_header }}" namespace {{ library_name }} @@ -54,6 +56,10 @@ struct {{ message.name }} } {% endif %} + // Time stamps + std::chrono::steady_clock::time_point receive_time{}; + std::chrono::system_clock::time_point bus_time{}; + // Signals {% for signal in message.signals %} double {{ signal.name }}; @@ -104,6 +110,14 @@ struct {{ message.name }} {% endif %} } + // Constructor from socketcan CanFrame, keeps its time stamps + explicit {{ message.name }}(const polymath::socketcan::CanFrame & frame) + : {{ message.name }}(frame.get_frame()) + { + receive_time = frame.get_receive_time(); + bus_time = frame.get_bus_time(); + } + // Conversion operator to can_frame operator can_frame() const { diff --git a/dbc_gen_cpp/package.xml b/dbc_gen_cpp/package.xml index 3649ff8..78db7a7 100644 --- a/dbc_gen_cpp/package.xml +++ b/dbc_gen_cpp/package.xml @@ -10,6 +10,8 @@ ament_cmake ament_cmake_python + socketcan_adapter + python3-cantools-pip python3-jinja2 diff --git a/test_dbc_gen_cpp/test/test_dbc_cpp.cpp b/test_dbc_gen_cpp/test/test_dbc_cpp.cpp index 825a7fb..60e04e1 100644 --- a/test_dbc_gen_cpp/test/test_dbc_cpp.cpp +++ b/test_dbc_gen_cpp/test/test_dbc_cpp.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2026 Polymath Robotics, Inc. // SPDX-License-Identifier: Apache-2.0 +#include #include #include @@ -368,6 +369,37 @@ TEST_CASE("Standard CAN ID mismatch throws") REQUIRE_THROWS(fake_vehicle_can::SteeringFeedback{frame}); } +TEST_CASE("Construct from socketcan CanFrame keeps time stamps") +{ + can_frame raw; + memset(&raw, 0, sizeof(raw)); + raw.can_id = fake_vehicle_can::SteeringFeedback::Id; + raw.can_dlc = fake_vehicle_can::SteeringFeedback::DataLength; + raw.data[0] = 25; + + polymath::socketcan::CanFrame frame{raw}; + const auto receive_time = std::chrono::steady_clock::time_point{std::chrono::seconds{5}}; + const auto bus_time = std::chrono::system_clock::time_point{std::chrono::seconds{7}}; + frame.set_receive_timestamp(receive_time); + frame.set_bus_timestamp(bus_time); + + const fake_vehicle_can::SteeringFeedback msg{frame}; + + REQUIRE(msg.receive_time == receive_time); + REQUIRE(msg.bus_time == bus_time); + REQUIRE(msg.angle == 25); +} + +TEST_CASE("Construct from socketcan CanFrame with wrong ID throws") +{ + can_frame raw; + memset(&raw, 0, sizeof(raw)); + raw.can_id = 100; + raw.can_dlc = 1; + + REQUIRE_THROWS(fake_vehicle_can::SteeringFeedback{polymath::socketcan::CanFrame{raw}}); +} + // ============================================================================ // J1939 Protocol Parsing Tests // ============================================================================