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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ my_can_socket->send(frame);

```

### Time Stamps

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 `can_frame` and both time stamps decodes the frame and stores the time stamps.

```c++
my_can_library_name::MessageName message{frame, receive_time, bus_time};
message.receive_time;
message.bus_time;
```

### Signal Value Maps (Enums)

DBC value tables (`VAL_` lines, and the global `VAL_TABLE_`) map raw signal values
Expand Down
16 changes: 16 additions & 0 deletions dbc_gen_cpp/dbc_gen_cpp/templates/can.hpp.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <linux/can.h>

#include <chrono>
#include <cstdint>
#include <stdexcept>

Expand Down Expand Up @@ -54,6 +55,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 }};
Expand Down Expand Up @@ -104,6 +109,17 @@ struct {{ message.name }}
{% endif %}
}

// Constructor from can_frame with time stamps
{{ message.name }}(
const can_frame & frame,
std::chrono::steady_clock::time_point receive_time,
std::chrono::system_clock::time_point bus_time)
: {{ message.name }}(frame)
{
this->receive_time = receive_time;
this->bus_time = bus_time;
}

// Conversion operator to can_frame
operator can_frame() const
{
Expand Down
22 changes: 16 additions & 6 deletions dbc_gen_cpp/include/dbc_gen_cpp/can_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <linux/can.h>

#include <chrono>
#include <cstdint>
#include <functional>
#include <memory>
Expand Down Expand Up @@ -40,13 +41,18 @@ class CANHandler

/// @brief Route a can frame to the appropriate handler, if registered
/// @param frame
/// @param receive_time Copied to the message; defaults to the epoch.
/// @param bus_time Copied to the message; defaults to the epoch.
/// @return true if there was a registered handler that was called
bool handle(const can_frame & frame) const
bool handle(
const can_frame & frame,
std::chrono::steady_clock::time_point receive_time = {},
std::chrono::system_clock::time_point bus_time = {}) const
{
// Standard CAN: exact ID match
auto it = handlers_.find(frame.can_id);
if (it != handlers_.end()) {
it->second->invoke(frame);
it->second->invoke(frame, receive_time, bus_time);
return true;
}

Expand All @@ -55,7 +61,7 @@ class CANHandler
uint32_t pgn = extractPgn(frame.can_id);
auto j1939_it = j1939_handlers_.find(pgn);
if (j1939_it != j1939_handlers_.end()) {
j1939_it->second->invoke(frame);
j1939_it->second->invoke(frame, receive_time, bus_time);
return true;
}
}
Expand Down Expand Up @@ -83,7 +89,8 @@ class CANHandler
struct ICanMessageHandler
{
virtual ~ICanMessageHandler() = default;
virtual void invoke(const can_frame &) const = 0;
virtual void invoke(
const can_frame &, std::chrono::steady_clock::time_point, std::chrono::system_clock::time_point) const = 0;
};

/// @brief Concrete instantiation of the handler interface for a given message type
Expand All @@ -97,9 +104,12 @@ class CANHandler
: callback(std::move(cb))
{}

void invoke(const can_frame & frame) const override
void invoke(
const can_frame & frame,
std::chrono::steady_clock::time_point receive_time,
std::chrono::system_clock::time_point bus_time) const override
{
callback(T(frame));
callback(T(frame, receive_time, bus_time));
}
};

Expand Down
50 changes: 50 additions & 0 deletions test_dbc_gen_cpp/test/test_dbc_cpp.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2026 Polymath Robotics, Inc.
// SPDX-License-Identifier: Apache-2.0

#include <chrono>
#include <cstring>
#include <type_traits>

Expand Down Expand Up @@ -368,6 +369,34 @@ TEST_CASE("Standard CAN ID mismatch throws")
REQUIRE_THROWS(fake_vehicle_can::SteeringFeedback{frame});
}

TEST_CASE("Construct with time stamps keeps time stamps")
{
can_frame frame;
memset(&frame, 0, sizeof(frame));
frame.can_id = fake_vehicle_can::SteeringFeedback::Id;
frame.can_dlc = fake_vehicle_can::SteeringFeedback::DataLength;
frame.data[0] = 25;

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}};

const fake_vehicle_can::SteeringFeedback msg{frame, receive_time, bus_time};

REQUIRE(msg.receive_time == receive_time);
REQUIRE(msg.bus_time == bus_time);
REQUIRE(msg.angle == 25);
}

TEST_CASE("Construct with time stamps and wrong ID throws")
{
can_frame frame;
memset(&frame, 0, sizeof(frame));
frame.can_id = 100;
frame.can_dlc = 1;

REQUIRE_THROWS(fake_vehicle_can::SteeringFeedback{frame, {}, {}});
}

// ============================================================================
// J1939 Protocol Parsing Tests
// ============================================================================
Expand Down Expand Up @@ -560,6 +589,27 @@ TEST_CASE("CANHandler Standard CAN - Basic routing")
REQUIRE(received_value == 42);
}

TEST_CASE("CANHandler Standard CAN - Passes time stamps")
{
using fake_vehicle_can::SteeringFeedback;

dbc_gen_cpp::CANHandler handler;
std::chrono::steady_clock::time_point received_receive_time{};
std::chrono::system_clock::time_point received_bus_time{};

handler.set_handler<SteeringFeedback>([&](const SteeringFeedback & msg) {
received_receive_time = msg.receive_time;
received_bus_time = msg.bus_time;
});

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}};

REQUIRE(handler.handle(SteeringFeedback{42}, receive_time, bus_time));
REQUIRE(received_receive_time == receive_time);
REQUIRE(received_bus_time == bus_time);
}

TEST_CASE("CANHandler Standard CAN - Multiple handlers")
{
using fake_vehicle_can::Heartbeat;
Expand Down
Loading