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