Skip to content

Commit 22e000e

Browse files
committed
support ROS_TIME for create_timer
Signed-off-by: Koichi <koichi.imai.2@tier4.jp>
1 parent 7b6d54a commit 22e000e

File tree

6 files changed

+331
-44
lines changed

6 files changed

+331
-44
lines changed

src/agnocast_sample_application/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ target_include_directories(no_rclcpp_client PRIVATE
9090
${agnocastlib_INCLUDE_DIRS}
9191
)
9292

93+
add_executable(sim_time_timer src/sim_time_timer.cpp)
94+
ament_target_dependencies(sim_time_timer rclcpp agnocastlib)
95+
target_include_directories(sim_time_timer PRIVATE
96+
${agnocastlib_INCLUDE_DIRS}
97+
)
98+
9399
add_library(listener_component SHARED src/minimal_subscriber.cpp)
94100
ament_target_dependencies(listener_component rclcpp rclcpp_components agnocastlib agnocast_sample_interfaces)
95101
target_include_directories(listener_component PRIVATE ${agnocastlib_INCLUDE_DIRS})
@@ -151,6 +157,9 @@ install(TARGETS no_rclcpp_server
151157
install(TARGETS no_rclcpp_client
152158
DESTINATION lib/${PROJECT_NAME})
153159

160+
install(TARGETS sim_time_timer
161+
DESTINATION lib/${PROJECT_NAME})
162+
154163
install(DIRECTORY launch
155164
DESTINATION share/${PROJECT_NAME}/
156165
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* @brief Sample application to demonstrate create_timer with use_sim_time support
3+
*
4+
* This sample creates a timer using agnocast::Node::create_timer() which supports
5+
* ROS_TIME (simulation time). When use_sim_time:=true, the timer will use the
6+
* /clock topic time instead of wall clock time.
7+
*
8+
* Usage:
9+
* # Terminal 1: Run with use_sim_time enabled
10+
* ros2 run agnocast_sample_application sim_time_timer --ros-args -p use_sim_time:=true
11+
*
12+
* # Terminal 2: Publish clock messages
13+
* ros2 topic pub /clock rosgraph_msgs/msg/Clock "{clock: {sec: 0, nanosec: 0}}" --once
14+
* ros2 topic pub /clock rosgraph_msgs/msg/Clock "{clock: {sec: 1, nanosec: 0}}" --once
15+
* ros2 topic pub /clock rosgraph_msgs/msg/Clock "{clock: {sec: 2, nanosec: 0}}" --once
16+
*
17+
* # Or run without use_sim_time (uses wall clock)
18+
* ros2 run agnocast_sample_application sim_time_timer
19+
*/
20+
21+
#include "agnocast/agnocast.hpp"
22+
23+
#include <chrono>
24+
25+
using namespace std::chrono_literals;
26+
27+
class SimTimeTimerNode : public agnocast::Node
28+
{
29+
public:
30+
SimTimeTimerNode() : agnocast::Node("sim_time_timer_node")
31+
{
32+
// Log whether we're using sim time
33+
const bool use_sim_time = this->get_parameter("use_sim_time").as_bool();
34+
RCLCPP_INFO(
35+
this->get_logger(), "Starting timer node (use_sim_time: %s)",
36+
use_sim_time ? "true" : "false");
37+
38+
// Create timer using create_timer() which supports ROS_TIME
39+
// This timer will respect use_sim_time parameter
40+
timer_ = this->create_timer(500ms, std::bind(&SimTimeTimerNode::timer_callback, this));
41+
42+
RCLCPP_INFO(
43+
this->get_logger(), "Timer created with period 500ms, clock type: %s",
44+
timer_->is_steady() ? "STEADY_TIME" : "ROS_TIME");
45+
}
46+
47+
private:
48+
void timer_callback()
49+
{
50+
const auto now = this->now();
51+
const int64_t sec = now.seconds();
52+
const int64_t nsec = now.nanoseconds() % 1000000000;
53+
54+
RCLCPP_INFO(
55+
this->get_logger(), "Timer callback! Current time: %ld.%09ld (count: %d)", sec, nsec,
56+
callback_count_++);
57+
}
58+
59+
agnocast::TimerBase::SharedPtr timer_;
60+
int callback_count_ = 0;
61+
};
62+
63+
int main(int argc, char * argv[])
64+
{
65+
agnocast::init(argc, argv);
66+
67+
agnocast::AgnocastOnlySingleThreadedExecutor executor;
68+
auto node = std::make_shared<SimTimeTimerNode>();
69+
executor.add_node(node);
70+
executor.spin();
71+
72+
return 0;
73+
}

src/agnocastlib/include/agnocast/agnocast_epoll.hpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ struct AgnocastExecutable;
1616
extern std::atomic<bool> need_epoll_updates;
1717

1818
constexpr uint32_t TIMER_EVENT_FLAG = 0x80000000;
19-
constexpr uint32_t SHUTDOWN_EVENT_FLAG = 0x40000000;
19+
constexpr uint32_t CLOCK_EVENT_FLAG = 0x40000000; // For clock_eventfd events (ROS_TIME timers)
20+
constexpr uint32_t SHUTDOWN_EVENT_FLAG = 0x20000000; // For shutdown events (AgnocastOnlyExecutor)
2021

2122
/// @return true if shutdown event detected, false otherwise
2223
bool wait_and_handle_epoll_event(
@@ -81,14 +82,34 @@ void prepare_epoll_impl(
8182
continue;
8283
}
8384

84-
struct epoll_event ev = {};
85-
ev.events = EPOLLIN;
86-
ev.data.u32 = timer_id | TIMER_EVENT_FLAG;
85+
// Register timerfd (wall clock based firing)
86+
if (timer_info.timer_fd >= 0) {
87+
struct epoll_event ev = {};
88+
ev.events = EPOLLIN;
89+
ev.data.u32 = timer_id | TIMER_EVENT_FLAG;
90+
91+
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_info.timer_fd, &ev) == -1) {
92+
if (errno != EEXIST) { // EEXIST means already registered, which is fine
93+
RCLCPP_ERROR(logger, "epoll_ctl failed for timer: %s", strerror(errno));
94+
close(agnocast_fd);
95+
exit(EXIT_FAILURE);
96+
}
97+
}
98+
}
8799

88-
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_info.timer_fd, &ev) == -1) {
89-
RCLCPP_ERROR(logger, "epoll_ctl failed for timer: %s", strerror(errno));
90-
close(agnocast_fd);
91-
exit(EXIT_FAILURE);
100+
// Register clock_eventfd for ROS_TIME timers (simulation time support)
101+
if (timer_info.clock_eventfd >= 0) {
102+
struct epoll_event clock_ev = {};
103+
clock_ev.events = EPOLLIN;
104+
clock_ev.data.u32 = timer_id | CLOCK_EVENT_FLAG;
105+
106+
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, timer_info.clock_eventfd, &clock_ev) == -1) {
107+
if (errno != EEXIST) { // EEXIST means already registered, which is fine
108+
RCLCPP_ERROR(logger, "epoll_ctl failed for clock_eventfd: %s", strerror(errno));
109+
close(agnocast_fd);
110+
exit(EXIT_FAILURE);
111+
}
112+
}
92113
}
93114

94115
timer_info.need_epoll_update = false;

src/agnocastlib/include/agnocast/agnocast_timer_info.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,33 @@ struct TimerInfo
1919
{
2020
~TimerInfo();
2121

22+
uint32_t timer_id = 0;
2223
int timer_fd = -1;
24+
int clock_eventfd = -1; // eventfd to wake epoll on clock updates (ROS_TIME only)
2325
std::weak_ptr<TimerBase> timer;
2426
rclcpp::CallbackGroup::SharedPtr callback_group;
2527
std::atomic<int64_t> last_call_time_ns;
2628
std::atomic<int64_t> next_call_time_ns;
29+
std::atomic<int64_t> time_credit{
30+
0}; // Credit for time elapsed before ROS time is activated/deactivated
2731
std::chrono::nanoseconds period;
2832
bool need_epoll_update = true;
2933

3034
rclcpp::Clock::SharedPtr clock;
35+
rclcpp::JumpHandler::SharedPtr jump_handler;
3136
};
3237

3338
extern std::mutex id2_timer_info_mtx;
3439
extern std::unordered_map<uint32_t, std::shared_ptr<TimerInfo>> id2_timer_info;
3540
extern std::atomic<uint32_t> next_timer_id;
3641

37-
int create_timer_fd(uint32_t timer_id, std::chrono::nanoseconds period);
42+
int create_timer_fd(
43+
uint32_t timer_id, std::chrono::nanoseconds period, rcl_clock_type_t clock_type);
3844

3945
void handle_timer_event(TimerInfo & timer_info);
4046

47+
void handle_clock_event(TimerInfo & timer_info);
48+
4149
uint32_t allocate_timer_id();
4250

4351
void register_timer_info(

src/agnocastlib/src/agnocast_epoll.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ bool wait_and_handle_epoll_event(
4040
}
4141

4242
if ((event_id & TIMER_EVENT_FLAG) != 0U) {
43-
// Timer event
43+
// Timer event (timerfd fired)
4444
const uint32_t timer_id = event_id & ~TIMER_EVENT_FLAG;
4545
rclcpp::CallbackGroup::SharedPtr callback_group;
4646

@@ -64,6 +64,35 @@ bool wait_and_handle_epoll_event(
6464
const std::shared_ptr<std::function<void()>> callable =
6565
std::make_shared<std::function<void()>>([timer_info]() { handle_timer_event(*timer_info); });
6666

67+
{
68+
std::lock_guard<std::mutex> ready_lock{ready_agnocast_executables_mutex};
69+
ready_agnocast_executables.emplace_back(AgnocastExecutable{callable, callback_group});
70+
}
71+
} else if ((event_id & CLOCK_EVENT_FLAG) != 0U) {
72+
// Clock event (ROS_TIME clock updated via time jump callback)
73+
const uint32_t timer_id = event_id & ~CLOCK_EVENT_FLAG;
74+
rclcpp::CallbackGroup::SharedPtr callback_group;
75+
76+
std::shared_ptr<TimerInfo> timer_info;
77+
{
78+
std::lock_guard<std::mutex> lock(id2_timer_info_mtx);
79+
const auto it = id2_timer_info.find(timer_id);
80+
if (it == id2_timer_info.end()) {
81+
RCLCPP_ERROR(logger, "Agnocast internal implementation error: timer info cannot be found");
82+
close(agnocast_fd);
83+
exit(EXIT_FAILURE);
84+
}
85+
timer_info = it->second;
86+
if (!timer_info->timer.lock()) {
87+
return; // Timer object has been destroyed
88+
}
89+
callback_group = timer_info->callback_group;
90+
}
91+
92+
// Create a callable that handles the clock event
93+
const std::shared_ptr<std::function<void()>> callable =
94+
std::make_shared<std::function<void()>>([timer_info]() { handle_clock_event(*timer_info); });
95+
6796
{
6897
std::lock_guard<std::mutex> ready_lock{ready_agnocast_executables_mutex};
6998
ready_agnocast_executables.emplace_back(AgnocastExecutable{callable, callback_group});

0 commit comments

Comments
 (0)