nros C++ API
Lightweight ROS 2 client for embedded real-time systems (C++ headers)
Loading...
Searching...
No Matches
stream.hpp
Go to the documentation of this file.
1// nros-cpp: Stream<T> -- multi-shot message receiver
2// Freestanding C++ -- no exceptions, no STL required
3
9
10#ifndef NROS_RCLCPP_STREAM_HPP
11#define NROS_RCLCPP_STREAM_HPP
12
13#include <cstdint>
14#include <cstddef>
15
16#include "rclcpp/result.hpp"
17#include "rclcpp/size_bound.hpp" // rclcpp::rx_buffer_capacity<T> — the receive-buffer size
18
19// FFI declarations
20extern "C" {
21typedef int nros_cpp_ret_t;
22nros_cpp_ret_t nros_cpp_spin_once(void* handle, int32_t timeout_ms);
26uint64_t nros_cpp_time_ns(void);
27}
28
29// phase-428 (RFC-0089): the entity types that reach into this one's private
30// constructor are DEFINED in the upstream namespace now, so the friend
31// declarations below name them there. A qualified friend cannot introduce a
32// name, hence these declarations.
33namespace rclcpp {
34template <typename S> class Client;
35} // namespace rclcpp
36
37// phase-456 W2b — the subscription that BINDS a stream is the poll one: a
38// stream reads through `nros_cpp_subscription_take_serialized` on a
39// caller-owned `RmwSubscriber`, which a dispatch registration does not have.
40namespace rclcpp {
41template <typename M> class PollSubscription;
42} // namespace rclcpp
43
44namespace rclcpp_action {
45template <typename A> class Client;
46} // namespace rclcpp_action
47
48namespace rclcpp {
49
62template <typename T> class Stream {
63 public:
73
76 template <size_t Cap> Result try_next_sized(T& out) {
77 if (!take_fn_) return Result(ErrorCode::NotInitialized);
78 uint8_t buf[Cap];
79 size_t len = 0;
80 nros_cpp_ret_t ret = take_fn_(storage_, buf, sizeof(buf), &len);
81 if (ret != 0) return Result(ret);
82 if (len == 0) return Result(ErrorCode::TryAgain);
83 if (T::ffi_deserialize(buf, len, &out) != 0) return Result(ErrorCode::Error);
84 return Result::success();
85 }
86
97 Result wait_next(void* executor_handle, uint32_t timeout_ms, T& out, uint32_t poll_ms = 10) {
98 return wait_next_sized<::rclcpp::rx_buffer_capacity<T>::value>(executor_handle, timeout_ms,
99 out, poll_ms);
100 }
101
104 template <size_t Cap>
105 Result wait_next_sized(void* executor_handle, uint32_t timeout_ms, T& out,
106 uint32_t poll_ms = 10) {
107 if (!take_fn_) return Result(ErrorCode::NotInitialized);
108 if (poll_ms == 0) poll_ms = 1;
109 // Phase 118.C: budget by wall-clock. Accumulating `step` per
110 // iteration breaks when `zpico_spin_once` returns early on a
111 // signaled condvar (keep-alives, discovery gossip) — the
112 // iteration loop collapses into milliseconds and returns
113 // Timeout before the message has a chance to land. Mirrors the
114 // Phase 89.2 fix on Future::wait().
115 const uint64_t start_ns = nros_cpp_time_ns();
116 const uint64_t budget_ns = static_cast<uint64_t>(timeout_ms) * 1000000ULL;
117 while (true) {
118 nros_cpp_ret_t ret = nros_cpp_spin_once(executor_handle, static_cast<int32_t>(poll_ms));
119 // Transient conditions: keep polling. Anything else propagates.
120 if (ret != 0 && ret != static_cast<nros_cpp_ret_t>(ErrorCode::Timeout) &&
121 ret != static_cast<nros_cpp_ret_t>(ErrorCode::TryAgain)) {
122 return Result(ret);
123 }
124 Result rn = try_next_sized<Cap>(out);
125 if (rn.ok()) return Result::success();
126 // TryAgain / NotInitialized / Error from try_next: keep polling
127 // unless we've hit a hard error that's not "no data yet".
129 return rn;
130 }
131 const uint64_t now_ns = nros_cpp_time_ns();
132 if (now_ns - start_ns >= budget_ns) break;
133 }
135 }
136
138 bool is_valid() const { return take_fn_ != nullptr; }
139
140 // Move semantics (non-copyable)
141 Stream(Stream&& other) noexcept : storage_(other.storage_), take_fn_(other.take_fn_) {
142 other.storage_ = nullptr;
143 other.take_fn_ = nullptr;
144 }
145
146 Stream& operator=(Stream&& other) noexcept {
147 if (this != &other) {
148 storage_ = other.storage_;
149 take_fn_ = other.take_fn_;
150 other.storage_ = nullptr;
151 other.take_fn_ = nullptr;
152 }
153 return *this;
154 }
155
157 Stream() : storage_(nullptr), take_fn_(nullptr) {}
158
159 private:
160 Stream(const Stream&) = delete;
161 Stream& operator=(const Stream&) = delete;
162
163 template <typename M> friend class ::rclcpp::PollSubscription;
164 template <typename A> friend class ::rclcpp_action::Client;
165
166 using TakeFn = nros_cpp_ret_t (*)(void*, uint8_t*, size_t, size_t*);
167
168 Stream(void* storage, TakeFn fn) : storage_(storage), take_fn_(fn) {}
169
170 void bind(void* storage, TakeFn fn) {
171 storage_ = storage;
172 take_fn_ = fn;
173 }
174
175 void* storage_;
176 TakeFn take_fn_;
177};
178
179} // namespace rclcpp
180#endif // NROS_RCLCPP_STREAM_HPP
Definition node.hpp:197
Definition node.hpp:214
bool ok() const
Returns true if the operation succeeded.
Definition result.hpp:227
ErrorCode code() const
Get the underlying error code.
Definition result.hpp:233
static constexpr ResultOf success()
Named constructors.
Definition result.hpp:239
Definition stream.hpp:62
Stream()
Default constructor – creates an unbound stream.
Definition stream.hpp:157
Stream(Stream &&other) noexcept
Definition stream.hpp:141
Result wait_next_sized(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition stream.hpp:105
bool is_valid() const
Check if the stream is connected to a valid source.
Definition stream.hpp:138
Stream & operator=(Stream &&other) noexcept
Definition stream.hpp:146
Result try_next(T &out)
Definition stream.hpp:72
Result try_next_sized(T &out)
Definition stream.hpp:76
Result wait_next(void *executor_handle, uint32_t timeout_ms, T &out, uint32_t poll_ms=10)
Definition stream.hpp:97
Definition client.hpp:256
int nros_cpp_ret_t
Definition future.hpp:25
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)
Definition node.hpp:203
Definition rclcpp.hpp:75
@ Error
Generic failure not covered by a more specific code.
Definition result.hpp:118
@ NotInitialized
Definition result.hpp:131
@ Timeout
Operation deadline elapsed before completion.
Definition result.hpp:120
@ TryAgain
Transient — no data ready yet (non-blocking take). Retry later.
Definition result.hpp:148
ResultOf< void > Result
Definition result.hpp:247
**auto handle
Definition client.hpp:127
rclcpp::Result, rclcpp::ErrorCode, and the RCLCPP_TRY macro.
nros_cpp_ret_t nros_cpp_spin_once(void *handle, int32_t timeout_ms)
uint64_t nros_cpp_time_ns(void)