WPILibC++ 2027.0.0-alpha-5
Loading...
Searching...
No Matches
StructTopic.hpp
Go to the documentation of this file.
1// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#pragma once
6
7#include <stdint.h>
8
9#include <atomic>
10#include <concepts>
11#include <functional>
12#include <span>
13#include <string_view>
14#include <tuple>
15#include <utility>
16#include <vector>
17
19#include "wpi/nt/Topic.hpp"
20#include "wpi/nt/ntcore_cpp.hpp"
21#include "wpi/util/SmallVector.hpp"
23
24namespace wpi::util {
25class json;
26} // namespace wpi::util
27
28namespace wpi::nt {
29
30template <typename T, typename... I>
31 requires wpi::util::StructSerializable<T, I...>
32class StructTopic;
33
34/**
35 * NetworkTables struct-encoded value subscriber.
36 */
37template <typename T, typename... I>
38 requires wpi::util::StructSerializable<T, I...>
40 using S = wpi::util::Struct<T, I...>;
41
42 public:
43 using TopicType = StructTopic<T, I...>;
44 using ValueType = T;
45 using ParamType = const T&;
47
48 StructSubscriber() = default;
49
50 /**
51 * Construct from a subscriber handle; recommended to use
52 * StructTopic::Subscribe() instead.
53 *
54 * @param handle Native handle
55 * @param defaultValue Default value
56 * @param info optional struct type info
57 */
58 StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
59 : Subscriber{handle},
60 m_defaultValue{std::move(defaultValue)},
61 m_info{std::move(info)...} {}
62
63 /**
64 * Get the last published value.
65 * If no value has been published or the value cannot be unpacked, returns the
66 * stored default value.
67 *
68 * @return value
69 */
70 ValueType Get() const { return Get(m_defaultValue); }
71
72 /**
73 * Get the last published value.
74 * If no value has been published or the value cannot be unpacked, returns the
75 * passed defaultValue.
76 *
77 * @param defaultValue default value to return if no value has been published
78 * @return value
79 */
80 ValueType Get(const T& defaultValue) const {
81 return GetAtomic(defaultValue).value;
82 }
83
84 /**
85 * Get the last published value, replacing the contents in place of an
86 * existing object. If no value has been published or the value cannot be
87 * unpacked, does not replace the contents and returns false.
88 *
89 * @param[out] out object to replace contents of
90 * @return true if successful
91 */
92 bool GetInto(T* out) {
93 wpi::util::SmallVector<uint8_t, 128> buf;
95 if (view.value.size() < std::apply(S::GetSize, m_info)) {
96 return false;
97 } else {
98 std::apply(
99 [&](const I&... info) {
100 wpi::util::UnpackStructInto(out, view.value, info...);
101 },
102 m_info);
103 return true;
104 }
105 }
106
107 /**
108 * Get the last published value along with its timestamp
109 * If no value has been published or the value cannot be unpacked, returns the
110 * stored default value and a timestamp of 0.
111 *
112 * @return timestamped value
113 */
114 TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
115
116 /**
117 * Get the last published value along with its timestamp.
118 * If no value has been published or the value cannot be unpacked, returns the
119 * passed defaultValue and a timestamp of 0.
120 *
121 * @param defaultValue default value to return if no value has been published
122 * @return timestamped value
123 */
124 TimestampedValueType GetAtomic(const T& defaultValue) const {
125 wpi::util::SmallVector<uint8_t, 128> buf;
127 if (view.value.size() < std::apply(S::GetSize, m_info)) {
128 return {0, 0, defaultValue};
129 } else {
130 return {
131 view.time, view.serverTime,
132 std::apply(
133 [&](const I&... info) { return S::Unpack(view.value, info...); },
134 m_info)};
135 }
136 }
137
138 /**
139 * Get an array of all valid value changes since the last call to ReadQueue.
140 * Also provides a timestamp for each value. Values that cannot be unpacked
141 * are dropped.
142 *
143 * @note The "poll storage" subscribe option can be used to set the queue
144 * depth.
145 *
146 * @return Array of timestamped values; empty array if no valid new changes
147 * have been published since the previous call.
148 */
149 std::vector<TimestampedValueType> ReadQueue() {
151 std::vector<TimestampedValueType> rv;
152 rv.reserve(raw.size());
153 for (auto&& r : raw) {
154 if (r.value.size() < std::apply(S::GetSize, m_info)) {
155 continue;
156 } else {
157 std::apply(
158 [&](const I&... info) {
159 rv.emplace_back(
160 r.time, r.serverTime,
161 S::Unpack(std::span<const uint8_t>(r.value), info...));
162 },
163 m_info);
164 }
165 }
166 return rv;
167 }
168
169 /**
170 * Get the corresponding topic.
171 *
172 * @return Topic
173 */
175 return std::apply(
176 [&](const I&... info) {
177 return StructTopic<T, I...>{
179 },
180 m_info);
181 }
182
183 private:
184 ValueType m_defaultValue;
185 [[no_unique_address]]
186 std::tuple<I...> m_info;
187};
188
189/**
190 * NetworkTables struct-encoded value publisher.
191 */
192template <typename T, typename... I>
193 requires wpi::util::StructSerializable<T, I...>
195 using S = wpi::util::Struct<T, I...>;
196
197 public:
198 using TopicType = StructTopic<T, I...>;
199 using ValueType = T;
200 using ParamType = const T&;
201
203
204 StructPublisher() = default;
205
208
210 : Publisher{std::move(rhs)},
211 m_schemaPublished{
212 rhs.m_schemaPublished.load(std::memory_order_relaxed)},
213 m_info{std::move(rhs.m_info)} {}
214
216 Publisher::operator=(std::move(rhs));
217 m_schemaPublished.store(
218 rhs.m_schemaPublished.load(std::memory_order_relaxed),
219 std::memory_order_relaxed);
220 m_info = std::move(rhs.m_info);
221 return *this;
222 }
223
224 /**
225 * Construct from a publisher handle; recommended to use
226 * StructTopic::Publish() instead.
227 *
228 * @param handle Native handle
229 * @param info optional struct type info
230 */
231 explicit StructPublisher(NT_Publisher handle, I... info)
232 : Publisher{handle}, m_info{std::move(info)...} {}
233
234 /**
235 * Publish a new value.
236 *
237 * @param value value to publish
238 * @param time timestamp; 0 indicates current NT time should be used
239 */
240 void Set(const T& value, int64_t time = 0) {
241 if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
242 std::apply(
243 [&](const I&... info) {
244 GetTopic().GetInstance().template AddStructSchema<T>(info...);
245 },
246 m_info);
247 }
248 if constexpr (sizeof...(I) == 0) {
249 if constexpr (wpi::util::is_constexpr([] { S::GetSize(); })) {
250 uint8_t buf[S::GetSize()];
251 S::Pack(buf, value);
252 ::wpi::nt::SetRaw(m_pubHandle, buf, time);
253 return;
254 }
255 }
256 wpi::util::SmallVector<uint8_t, 128> buf;
257 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
258 std::apply([&](const I&... info) { S::Pack(buf, value, info...); }, m_info);
259 ::wpi::nt::SetRaw(m_pubHandle, buf, time);
260 }
261
262 /**
263 * Publish a default value.
264 * On reconnect, a default value will never be used in preference to a
265 * published value.
266 *
267 * @param value value
268 */
269 void SetDefault(const T& value) {
270 if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
271 std::apply(
272 [&](const I&... info) {
273 GetTopic().GetInstance().template AddStructSchema<T>(info...);
274 },
275 m_info);
276 }
277 if constexpr (sizeof...(I) == 0) {
278 if constexpr (wpi::util::is_constexpr([] { S::GetSize(); })) {
279 uint8_t buf[S::GetSize()];
280 S::Pack(buf, value);
282 return;
283 }
284 }
285 wpi::util::SmallVector<uint8_t, 128> buf;
286 buf.resize_for_overwrite(std::apply(S::GetSize, m_info));
287 std::apply([&](const I&... info) { S::Pack(buf, value, info...); }, m_info);
289 }
290
291 /**
292 * Get the corresponding topic.
293 *
294 * @return Topic
295 */
297 return std::apply(
298 [&](const I&... info) {
299 return StructTopic<T, I...>{
301 },
302 m_info);
303 }
304
305 private:
306 std::atomic_bool m_schemaPublished{false};
307 [[no_unique_address]]
308 std::tuple<I...> m_info;
309};
310
311/**
312 * NetworkTables struct-encoded value entry.
313 *
314 * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
315 */
316template <typename T, typename... I>
317 requires wpi::util::StructSerializable<T, I...>
318class StructEntry final : public StructSubscriber<T, I...>,
319 public StructPublisher<T, I...> {
320 public:
323 using TopicType = StructTopic<T, I...>;
324 using ValueType = T;
325 using ParamType = const T&;
326
328
329 StructEntry() = default;
330
331 /**
332 * Construct from an entry handle; recommended to use
333 * StructTopic::GetEntry() instead.
334 *
335 * @param handle Native handle
336 * @param defaultValue Default value
337 * @param info optional struct type info
338 */
339 StructEntry(NT_Entry handle, T defaultValue, const I&... info)
340 : StructSubscriber<T, I...>{handle, std::move(defaultValue), info...},
341 StructPublisher<T, I...>{handle, info...} {}
342
343 /**
344 * Determines if the native handle is valid.
345 *
346 * @return True if the native handle is valid, false otherwise.
347 */
348 explicit operator bool() const { return this->m_subHandle != 0; }
349
350 /**
351 * Gets the native handle for the entry.
352 *
353 * @return Native handle
354 */
355 NT_Entry GetHandle() const { return this->m_subHandle; }
356
357 /**
358 * Get the corresponding topic.
359 *
360 * @return Topic
361 */
363
364 /**
365 * Stops publishing the entry if it's published.
366 */
368};
369
370/**
371 * NetworkTables struct-encoded value topic.
372 */
373template <typename T, typename... I>
374 requires wpi::util::StructSerializable<T, I...>
375class StructTopic final : public Topic {
376 public:
379 using EntryType = StructEntry<T, I...>;
380 using ValueType = T;
381 using ParamType = const T&;
383
384 StructTopic() = default;
385
386 /**
387 * Construct from a topic handle; recommended to use
388 * NetworkTableInstance::GetStructTopic() instead.
389 *
390 * @param handle Native handle
391 * @param info optional struct type info
392 */
393 explicit StructTopic(NT_Topic handle, I... info)
394 : Topic{handle}, m_info{std::move(info)...} {}
395
396 /**
397 * Construct from a generic topic.
398 *
399 * @param topic Topic
400 * @param info optional struct type info
401 */
402 explicit StructTopic(Topic topic, I... info)
403 : Topic{topic}, m_info{std::move(info)...} {}
404
405 /**
406 * Create a new subscriber to the topic.
407 *
408 * <p>The subscriber is only active as long as the returned object
409 * is not destroyed.
410 *
411 * @note Subscribers that do not match the published data type do not return
412 * any values. To determine if the data type matches, use the appropriate
413 * Topic functions.
414 *
415 * @param defaultValue default value used when a default is not provided to a
416 * getter function
417 * @param options subscribe options
418 * @return subscriber
419 */
420 [[nodiscard]]
422 T defaultValue, const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) {
423 return std::apply(
424 [&](const I&... info) {
425 return StructSubscriber<T, I...>{
429 std::move(defaultValue), info...};
430 },
431 m_info);
432 }
433
434 /**
435 * Create a new publisher to the topic.
436 *
437 * The publisher is only active as long as the returned object
438 * is not destroyed.
439 *
440 * @note It is not possible to publish two different data types to the same
441 * topic. Conflicts between publishers are typically resolved by the
442 * server on a first-come, first-served basis. Any published values that
443 * do not match the topic's data type are dropped (ignored). To determine
444 * if the data type matches, use the appropriate Topic functions.
445 *
446 * @param options publish options
447 * @return publisher
448 */
449 [[nodiscard]]
451 const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) {
452 return std::apply(
453 [&](const I&... info) {
454 return StructPublisher<T, I...>{
458 info...};
459 },
460 m_info);
461 }
462
463 /**
464 * Create a new publisher to the topic, with type string and initial
465 * properties.
466 *
467 * The publisher is only active as long as the returned object
468 * is not destroyed.
469 *
470 * @note It is not possible to publish two different data types to the same
471 * topic. Conflicts between publishers are typically resolved by the
472 * server on a first-come, first-served basis. Any published values that
473 * do not match the topic's data type are dropped (ignored). To determine
474 * if the data type matches, use the appropriate Topic functions.
475 *
476 * @param properties JSON properties
477 * @param options publish options
478 * @return publisher
479 */
480 [[nodiscard]]
482 const wpi::util::json& properties,
483 const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) {
484 return std::apply(
485 [&](const I&... info) {
486 return StructPublisher<T, I...>{
489 wpi::util::GetStructTypeString<T, I...>(info...), properties,
490 options),
491 info...};
492 },
493 m_info);
494 }
495
496 /**
497 * Create a new entry for the topic.
498 *
499 * Entries act as a combination of a subscriber and a weak publisher. The
500 * subscriber is active as long as the entry is not destroyed. The publisher
501 * is created when the entry is first written to, and remains active until
502 * either Unpublish() is called or the entry is destroyed.
503 *
504 * @note It is not possible to use two different data types with the same
505 * topic. Conflicts between publishers are typically resolved by the
506 * server on a first-come, first-served basis. Any published values that
507 * do not match the topic's data type are dropped (ignored), and the entry
508 * will show no new values if the data type does not match. To determine
509 * if the data type matches, use the appropriate Topic functions.
510 *
511 * @param defaultValue default value used when a default is not provided to a
512 * getter function
513 * @param options publish and/or subscribe options
514 * @return entry
515 */
516 [[nodiscard]]
517 EntryType GetEntry(T defaultValue,
518 const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) {
519 return std::apply(
520 [&](const I&... info) {
521 return StructEntry<T, I...>{
525 std::move(defaultValue), info...};
526 },
527 m_info);
528 }
529
530 private:
531 [[no_unique_address]]
532 std::tuple<I...> m_info;
533};
534
535} // namespace wpi::nt
Publisher(const Publisher &)=delete
Publisher & operator=(const Publisher &)=delete
NT_Publisher m_pubHandle
NetworkTables handle.
Definition Topic.hpp:443
NetworkTables struct-encoded value entry.
Definition StructTopic.hpp:319
T ValueType
Definition StructTopic.hpp:324
void Unpublish()
Stops publishing the entry if it's published.
Definition StructTopic.hpp:367
NT_Entry GetHandle() const
Gets the native handle for the entry.
Definition StructTopic.hpp:355
TopicType GetTopic() const
Get the corresponding topic.
Definition StructTopic.hpp:362
StructTopic< T, I... > TopicType
Definition StructTopic.hpp:323
const T & ParamType
Definition StructTopic.hpp:325
StructEntry(NT_Entry handle, T defaultValue, const I &... info)
Construct from an entry handle; recommended to use StructTopic::GetEntry() instead.
Definition StructTopic.hpp:339
Timestamped< T > TimestampedValueType
Definition StructTopic.hpp:327
StructPublisher< T, I... > PublisherType
Definition StructTopic.hpp:322
StructEntry()=default
StructSubscriber< T, I... > SubscriberType
Definition StructTopic.hpp:321
NetworkTables struct-encoded value publisher.
Definition StructTopic.hpp:194
StructPublisher(StructPublisher &&rhs)
Definition StructTopic.hpp:209
void Set(const T &value, int64_t time=0)
Publish a new value.
Definition StructTopic.hpp:240
T ValueType
Definition StructTopic.hpp:199
void SetDefault(const T &value)
Publish a default value.
Definition StructTopic.hpp:269
StructPublisher & operator=(const StructPublisher &)=delete
const T & ParamType
Definition StructTopic.hpp:200
StructTopic< T, I... > TopicType
Definition StructTopic.hpp:198
Timestamped< T > TimestampedValueType
Definition StructTopic.hpp:202
StructPublisher(NT_Publisher handle, I... info)
Construct from a publisher handle; recommended to use StructTopic::Publish() instead.
Definition StructTopic.hpp:231
StructPublisher & operator=(StructPublisher &&rhs)
Definition StructTopic.hpp:215
StructPublisher(const StructPublisher &)=delete
TopicType GetTopic() const
Get the corresponding topic.
Definition StructTopic.hpp:296
NetworkTables struct-encoded value subscriber.
Definition StructTopic.hpp:39
StructTopic< T, I... > TopicType
Definition StructTopic.hpp:43
const T & ParamType
Definition StructTopic.hpp:45
bool GetInto(T *out)
Get the last published value, replacing the contents in place of an existing object.
Definition StructTopic.hpp:92
std::vector< TimestampedValueType > ReadQueue()
Get an array of all valid value changes since the last call to ReadQueue.
Definition StructTopic.hpp:149
Timestamped< T > TimestampedValueType
Definition StructTopic.hpp:46
TimestampedValueType GetAtomic(const T &defaultValue) const
Get the last published value along with its timestamp.
Definition StructTopic.hpp:124
ValueType Get(const T &defaultValue) const
Get the last published value.
Definition StructTopic.hpp:80
T ValueType
Definition StructTopic.hpp:44
TimestampedValueType GetAtomic() const
Get the last published value along with its timestamp If no value has been published or the value can...
Definition StructTopic.hpp:114
TopicType GetTopic() const
Get the corresponding topic.
Definition StructTopic.hpp:174
StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
Construct from a subscriber handle; recommended to use StructTopic::Subscribe() instead.
Definition StructTopic.hpp:58
ValueType Get() const
Get the last published value.
Definition StructTopic.hpp:70
NetworkTables struct-encoded value topic.
Definition StructTopic.hpp:375
Timestamped< T > TimestampedValueType
Definition StructTopic.hpp:382
PublisherType Publish(const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Create a new publisher to the topic.
Definition StructTopic.hpp:450
StructTopic(NT_Topic handle, I... info)
Construct from a topic handle; recommended to use NetworkTableInstance::GetStructTopic() instead.
Definition StructTopic.hpp:393
EntryType GetEntry(T defaultValue, const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Create a new entry for the topic.
Definition StructTopic.hpp:517
StructEntry< T, I... > EntryType
Definition StructTopic.hpp:379
T ValueType
Definition StructTopic.hpp:380
StructTopic(Topic topic, I... info)
Construct from a generic topic.
Definition StructTopic.hpp:402
const T & ParamType
Definition StructTopic.hpp:381
StructPublisher< T, I... > PublisherType
Definition StructTopic.hpp:378
PublisherType PublishEx(const wpi::util::json &properties, const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Create a new publisher to the topic, with type string and initial properties.
Definition StructTopic.hpp:481
StructSubscriber< T, I... > SubscriberType
Definition StructTopic.hpp:377
SubscriberType Subscribe(T defaultValue, const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Create a new subscriber to the topic.
Definition StructTopic.hpp:421
Subscriber(const Subscriber &)=delete
NT_Subscriber m_subHandle
Definition Topic.hpp:388
NT_Topic m_handle
Definition Topic.hpp:319
NetworkTableInstance GetInstance() const
Gets the instance for the topic.
Topic()=default
Definition json.hpp:85
Specifies that a type is capable of raw struct serialization and deserialization.
Definition Struct.hpp:69
bool SetRaw(NT_Handle pubentry, std::span< const uint8_t > value, int64_t time=0)
Publish a new value.
std::vector< TimestampedRaw > ReadQueueRaw(NT_Handle subentry)
Get an array of all value changes since the last call to ReadQueue.
TimestampedRaw GetAtomicRaw(NT_Handle subentry, std::span< const uint8_t > defaultValue)
Get the last published value along with its timestamp.
bool SetDefaultRaw(NT_Handle pubentry, std::span< const uint8_t > defaultValue)
Publish a default value.
NT_Handle NT_Topic
Definition ntcore_c.h:42
NT_Handle NT_Subscriber
Definition ntcore_c.h:43
NT_Handle NT_Publisher
Definition ntcore_c.h:44
NT_Handle NT_Entry
Definition ntcore_c.h:37
@ NT_RAW
Definition ntcore_c.h:55
Timestamped< std::span< uint8_t > > TimestampedRawView
Timestamped Raw view (for wpi::util::SmallVector-taking functions).
Definition ntcore_cpp_types.hpp:469
constexpr PubSubOptions DEFAULT_PUB_SUB_OPTIONS
Default publish/subscribe options.
Definition ntcore_cpp.hpp:388
NT_Entry GetEntry(NT_Inst inst, std::string_view name)
Get Entry Handle.
NT_Topic GetTopicFromHandle(NT_Handle pubsubentry)
Gets the topic handle from an entry/subscriber/publisher handle.
NT_Subscriber Subscribe(NT_Topic topic, NT_Type type, std::string_view typeStr, const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Creates a new subscriber to value changes on a topic.
void Unpublish(NT_Handle pubentry)
Stops publisher.
NT_Publisher PublishEx(NT_Topic topic, NT_Type type, std::string_view typeStr, const wpi::util::json &properties, const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Creates a new publisher to a topic.
NT_Publisher Publish(NT_Topic topic, NT_Type type, std::string_view typeStr, const PubSubOptions &options=DEFAULT_PUB_SUB_OPTIONS)
Creates a new publisher to a topic.
Definition StringMap.hpp:773
NetworkTables (ntcore) namespace.
Definition NTSendable.hpp:9
Definition raw_os_ostream.hpp:19
void UnpackStructInto(T *out, std::span< const uint8_t > data, const I &... info)
Unpack a serialized struct into an existing object, overwriting its contents.
Definition Struct.hpp:259
constexpr auto GetStructTypeString(const I &... info)
Get the type string for a raw struct serializable type.
Definition Struct.hpp:314
constexpr bool is_constexpr(Lambda)
Definition type_traits.hpp:81
NetworkTables publish/subscribe options.
Definition ntcore_cpp.hpp:303
Timestamped value.
Definition ntcore_cpp_types.hpp:30
int64_t serverTime
Time in server time base.
Definition ntcore_cpp_types.hpp:43
T value
Value.
Definition ntcore_cpp_types.hpp:48
int64_t time
Time in local time base.
Definition ntcore_cpp_types.hpp:38
Struct serialization template.
Definition Struct.hpp:39