WPILibC++ 2024.3.2
StructTopic.h
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
18#include <wpi/SmallVector.h>
19#include <wpi/json_fwd.h>
20#include <wpi/struct/Struct.h>
21
23#include "networktables/Topic.h"
24#include "ntcore_cpp.h"
25
26namespace nt {
27
28template <typename T, typename... I>
29 requires wpi::StructSerializable<T, I...>
30class StructTopic;
31
32/**
33 * NetworkTables struct-encoded value subscriber.
34 */
35template <typename T, typename... I>
36 requires wpi::StructSerializable<T, I...>
38 using S = wpi::Struct<T, I...>;
39
40 public:
41 using TopicType = StructTopic<T, I...>;
42 using ValueType = T;
43 using ParamType = const T&;
45
46 StructSubscriber() = default;
47
48 /**
49 * Construct from a subscriber handle; recommended to use
50 * StructTopic::Subscribe() instead.
51 *
52 * @param handle Native handle
53 * @param defaultValue Default value
54 * @param info optional struct type info
55 */
56 StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
57 : Subscriber{handle},
58 m_defaultValue{std::move(defaultValue)},
59 m_info{std::move(info)...} {}
60
61 /**
62 * Get the last published value.
63 * If no value has been published or the value cannot be unpacked, returns the
64 * stored default value.
65 *
66 * @return value
67 */
68 ValueType Get() const { return Get(m_defaultValue); }
69
70 /**
71 * Get the last published value.
72 * If no value has been published or the value cannot be unpacked, returns the
73 * passed defaultValue.
74 *
75 * @param defaultValue default value to return if no value has been published
76 * @return value
77 */
78 ValueType Get(const T& defaultValue) const {
79 return GetAtomic(defaultValue).value;
80 }
81
82 /**
83 * Get the last published value, replacing the contents in place of an
84 * existing object. If no value has been published or the value cannot be
85 * unpacked, does not replace the contents and returns false.
86 *
87 * @param[out] out object to replace contents of
88 * @return true if successful
89 */
90 bool GetInto(T* out) {
93 if (view.value.size() < std::apply(S::GetSize, m_info)) {
94 return false;
95 } else {
96 std::apply(
97 [&](const I&... info) {
98 wpi::UnpackStructInto(out, view.value, info...);
99 },
100 m_info);
101 return true;
102 }
103 }
104
105 /**
106 * Get the last published value along with its timestamp
107 * If no value has been published or the value cannot be unpacked, returns the
108 * stored default value and a timestamp of 0.
109 *
110 * @return timestamped value
111 */
112 TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
113
114 /**
115 * Get the last published value along with its timestamp.
116 * If no value has been published or the value cannot be unpacked, returns the
117 * passed defaultValue and a timestamp of 0.
118 *
119 * @param defaultValue default value to return if no value has been published
120 * @return timestamped value
121 */
122 TimestampedValueType GetAtomic(const T& defaultValue) const {
125 if (view.value.size() < std::apply(S::GetSize, m_info)) {
126 return {0, 0, defaultValue};
127 } else {
128 return {
129 view.time, view.serverTime,
130 std::apply(
131 [&](const I&... info) { return S::Unpack(view.value, info...); },
132 m_info)};
133 }
134 }
135
136 /**
137 * Get an array of all valid value changes since the last call to ReadQueue.
138 * Also provides a timestamp for each value. Values that cannot be unpacked
139 * are dropped.
140 *
141 * @note The "poll storage" subscribe option can be used to set the queue
142 * depth.
143 *
144 * @return Array of timestamped values; empty array if no valid new changes
145 * have been published since the previous call.
146 */
147 std::vector<TimestampedValueType> ReadQueue() {
149 std::vector<TimestampedValueType> rv;
150 rv.reserve(raw.size());
151 for (auto&& r : raw) {
152 if (r.value.size() < std::apply(S::GetSize, m_info)) {
153 continue;
154 } else {
155 std::apply(
156 [&](const I&... info) {
157 rv.emplace_back(
158 r.time, r.serverTime,
159 S::Unpack(std::span<const uint8_t>(r.value), info...));
160 },
161 m_info);
162 }
163 }
164 return rv;
165 }
166
167 /**
168 * Get the corresponding topic.
169 *
170 * @return Topic
171 */
173 return std::apply(
174 [&](const I&... info) {
176 info...};
177 },
178 m_info);
179 }
180
181 private:
182 ValueType m_defaultValue;
183 [[no_unique_address]] std::tuple<I...> m_info;
184};
185
186/**
187 * NetworkTables struct-encoded value publisher.
188 */
189template <typename T, typename... I>
190 requires wpi::StructSerializable<T, I...>
192 using S = wpi::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::is_constexpr([] { S::GetSize(); })) {
247 uint8_t buf[S::GetSize()];
248 S::Pack(buf, value);
249 ::nt::SetRaw(m_pubHandle, buf, time);
250 return;
251 }
252 }
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 ::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::is_constexpr([] { S::GetSize(); })) {
276 uint8_t buf[S::GetSize()];
277 S::Pack(buf, value);
279 return;
280 }
281 }
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) {
297 info...};
298 },
299 m_info);
300 }
301
302 private:
303 std::atomic_bool m_schemaPublished{false};
304 [[no_unique_address]] std::tuple<I...> m_info;
305};
306
307/**
308 * NetworkTables struct-encoded value entry.
309 *
310 * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
311 */
312template <typename T, typename... I>
313 requires wpi::StructSerializable<T, I...>
314class StructEntry final : public StructSubscriber<T, I...>,
315 public StructPublisher<T, I...> {
316 public:
319 using TopicType = StructTopic<T, I...>;
320 using ValueType = T;
321 using ParamType = const T&;
322
324
325 StructEntry() = default;
326
327 /**
328 * Construct from an entry handle; recommended to use
329 * StructTopic::GetEntry() instead.
330 *
331 * @param handle Native handle
332 * @param defaultValue Default value
333 * @param info optional struct type info
334 */
335 StructEntry(NT_Entry handle, T defaultValue, const I&... info)
336 : StructSubscriber<T, I...>{handle, std::move(defaultValue), info...},
337 StructPublisher<T, I...>{handle, info...} {}
338
339 /**
340 * Determines if the native handle is valid.
341 *
342 * @return True if the native handle is valid, false otherwise.
343 */
344 explicit operator bool() const { return this->m_subHandle != 0; }
345
346 /**
347 * Gets the native handle for the entry.
348 *
349 * @return Native handle
350 */
351 NT_Entry GetHandle() const { return this->m_subHandle; }
352
353 /**
354 * Get the corresponding topic.
355 *
356 * @return Topic
357 */
359
360 /**
361 * Stops publishing the entry if it's published.
362 */
364};
365
366/**
367 * NetworkTables struct-encoded value topic.
368 */
369template <typename T, typename... I>
370 requires wpi::StructSerializable<T, I...>
371class StructTopic final : public Topic {
372 public:
375 using EntryType = StructEntry<T, I...>;
376 using ValueType = T;
377 using ParamType = const T&;
379
380 StructTopic() = default;
381
382 /**
383 * Construct from a topic handle; recommended to use
384 * NetworkTableInstance::GetStructTopic() instead.
385 *
386 * @param handle Native handle
387 * @param info optional struct type info
388 */
389 explicit StructTopic(NT_Topic handle, I... info)
390 : Topic{handle}, m_info{std::move(info)...} {}
391
392 /**
393 * Construct from a generic topic.
394 *
395 * @param topic Topic
396 * @param info optional struct type info
397 */
398 explicit StructTopic(Topic topic, I... info)
399 : Topic{topic}, m_info{std::move(info)...} {}
400
401 /**
402 * Create a new subscriber to the topic.
403 *
404 * <p>The subscriber is only active as long as the returned object
405 * is not destroyed.
406 *
407 * @note Subscribers that do not match the published data type do not return
408 * any values. To determine if the data type matches, use the appropriate
409 * Topic functions.
410 *
411 * @param defaultValue default value used when a default is not provided to a
412 * getter function
413 * @param options subscribe options
414 * @return subscriber
415 */
416 [[nodiscard]]
418 T defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
419 return std::apply(
420 [&](const I&... info) {
421 return StructSubscriber<T, I...>{
423 wpi::GetStructTypeString<T, I...>(info...),
424 options),
425 std::move(defaultValue), info...};
426 },
427 m_info);
428 }
429
430 /**
431 * Create a new publisher to the topic.
432 *
433 * The publisher is only active as long as the returned object
434 * is not destroyed.
435 *
436 * @note It is not possible to publish two different data types to the same
437 * topic. Conflicts between publishers are typically resolved by the
438 * server on a first-come, first-served basis. Any published values that
439 * do not match the topic's data type are dropped (ignored). To determine
440 * if the data type matches, use the appropriate Topic functions.
441 *
442 * @param options publish options
443 * @return publisher
444 */
445 [[nodiscard]]
447 return std::apply(
448 [&](const I&... info) {
449 return StructPublisher<T, I...>{
451 wpi::GetStructTypeString<T, I...>(info...),
452 options),
453 info...};
454 },
455 m_info);
456 }
457
458 /**
459 * Create a new publisher to the topic, with type string and initial
460 * properties.
461 *
462 * The publisher is only active as long as the returned object
463 * is not destroyed.
464 *
465 * @note It is not possible to publish two different data types to the same
466 * topic. Conflicts between publishers are typically resolved by the
467 * server on a first-come, first-served basis. Any published values that
468 * do not match the topic's data type are dropped (ignored). To determine
469 * if the data type matches, use the appropriate Topic functions.
470 *
471 * @param properties JSON properties
472 * @param options publish options
473 * @return publisher
474 */
475 [[nodiscard]]
477 const wpi::json& properties,
478 const PubSubOptions& options = kDefaultPubSubOptions) {
479 return std::apply(
480 [&](const I&... info) {
481 return StructPublisher<T, I...>{
483 wpi::GetStructTypeString<T, I...>(info...),
484 properties, options),
485 info...};
486 },
487 m_info);
488 }
489
490 /**
491 * Create a new entry for the topic.
492 *
493 * Entries act as a combination of a subscriber and a weak publisher. The
494 * subscriber is active as long as the entry is not destroyed. The publisher
495 * is created when the entry is first written to, and remains active until
496 * either Unpublish() is called or the entry is destroyed.
497 *
498 * @note It is not possible to use two different data types with the same
499 * topic. Conflicts between publishers are typically resolved by the
500 * server on a first-come, first-served basis. Any published values that
501 * do not match the topic's data type are dropped (ignored), and the entry
502 * will show no new values if the data type does not match. To determine
503 * if the data type matches, use the appropriate Topic functions.
504 *
505 * @param defaultValue default value used when a default is not provided to a
506 * getter function
507 * @param options publish and/or subscribe options
508 * @return entry
509 */
510 [[nodiscard]]
511 EntryType GetEntry(T defaultValue,
512 const PubSubOptions& options = kDefaultPubSubOptions) {
513 return std::apply(
514 [&](const I&... info) {
515 return StructEntry<T, I...>{
517 wpi::GetStructTypeString<T, I...>(info...),
518 options),
519 std::move(defaultValue), info...};
520 },
521 m_info);
522 }
523
524 private:
525 [[no_unique_address]] std::tuple<I...> m_info;
526};
527
528} // namespace nt
This file defines the SmallVector class.
NetworkTables publisher.
Definition: Topic.h:364
Publisher & operator=(const Publisher &)=delete
NT_Publisher m_pubHandle
NetworkTables handle.
Definition: Topic.h:400
NetworkTables struct-encoded value entry.
Definition: StructTopic.h:315
T ValueType
Definition: StructTopic.h:320
TopicType GetTopic() const
Get the corresponding topic.
Definition: StructTopic.h:358
NT_Entry GetHandle() const
Gets the native handle for the entry.
Definition: StructTopic.h:351
StructEntry(NT_Entry handle, T defaultValue, const I &... info)
Construct from an entry handle; recommended to use StructTopic::GetEntry() instead.
Definition: StructTopic.h:335
const T & ParamType
Definition: StructTopic.h:321
void Unpublish()
Stops publishing the entry if it's published.
Definition: StructTopic.h:363
StructEntry()=default
NetworkTables struct-encoded value publisher.
Definition: StructTopic.h:191
StructPublisher(StructPublisher &&rhs)
Definition: StructTopic.h:206
StructPublisher()=default
StructPublisher & operator=(const StructPublisher &)=delete
StructPublisher(const StructPublisher &)=delete
T ValueType
Definition: StructTopic.h:196
void Set(const T &value, int64_t time=0)
Publish a new value.
Definition: StructTopic.h:237
StructPublisher(NT_Publisher handle, I... info)
Construct from a publisher handle; recommended to use StructTopic::Publish() instead.
Definition: StructTopic.h:228
StructPublisher & operator=(StructPublisher &&rhs)
Definition: StructTopic.h:212
const T & ParamType
Definition: StructTopic.h:197
TopicType GetTopic() const
Get the corresponding topic.
Definition: StructTopic.h:293
void SetDefault(const T &value)
Publish a default value.
Definition: StructTopic.h:266
NetworkTables struct-encoded value subscriber.
Definition: StructTopic.h:37
TimestampedValueType GetAtomic(const T &defaultValue) const
Get the last published value along with its timestamp.
Definition: StructTopic.h:122
TimestampedValueType GetAtomic() const
Get the last published value along with its timestamp If no value has been published or the value can...
Definition: StructTopic.h:112
TopicType GetTopic() const
Get the corresponding topic.
Definition: StructTopic.h:172
StructSubscriber(NT_Subscriber handle, T defaultValue, I... info)
Construct from a subscriber handle; recommended to use StructTopic::Subscribe() instead.
Definition: StructTopic.h:56
bool GetInto(T *out)
Get the last published value, replacing the contents in place of an existing object.
Definition: StructTopic.h:90
std::vector< TimestampedValueType > ReadQueue()
Get an array of all valid value changes since the last call to ReadQueue.
Definition: StructTopic.h:147
StructSubscriber()=default
ValueType Get(const T &defaultValue) const
Get the last published value.
Definition: StructTopic.h:78
T ValueType
Definition: StructTopic.h:42
ValueType Get() const
Get the last published value.
Definition: StructTopic.h:68
const T & ParamType
Definition: StructTopic.h:43
NetworkTables struct-encoded value topic.
Definition: StructTopic.h:371
const T & ParamType
Definition: StructTopic.h:377
PublisherType PublishEx(const wpi::json &properties, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new publisher to the topic, with type string and initial properties.
Definition: StructTopic.h:476
StructTopic()=default
T ValueType
Definition: StructTopic.h:376
SubscriberType Subscribe(T defaultValue, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new subscriber to the topic.
Definition: StructTopic.h:417
EntryType GetEntry(T defaultValue, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new entry for the topic.
Definition: StructTopic.h:511
PublisherType Publish(const PubSubOptions &options=kDefaultPubSubOptions)
Create a new publisher to the topic.
Definition: StructTopic.h:446
StructTopic(Topic topic, I... info)
Construct from a generic topic.
Definition: StructTopic.h:398
StructTopic(NT_Topic handle, I... info)
Construct from a topic handle; recommended to use NetworkTableInstance::GetStructTopic() instead.
Definition: StructTopic.h:389
NetworkTables subscriber.
Definition: Topic.h:309
NT_Subscriber m_subHandle
Definition: Topic.h:360
NetworkTables Topic.
Definition: Topic.h:28
NT_Topic m_handle
Definition: Topic.h:305
NetworkTableInstance GetInstance() const
Gets the instance for the topic.
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1202
void resize_for_overwrite(size_type N)
Like resize, but T is POD, the new values won't be initialized.
Definition: SmallVector.h:647
Specifies that a type is capable of raw struct serialization and deserialization.
Definition: Struct.h:68
bool SetDefaultRaw(NT_Handle pubentry, std::span< const uint8_t > defaultValue)
Publish a default value.
std::vector< TimestampedRaw > ReadQueueRaw(NT_Handle subentry)
Get an array of all value changes since the last call to ReadQueue.
bool SetRaw(NT_Handle pubentry, std::span< const uint8_t > value, int64_t time=0)
Publish a new value.
TimestampedRaw GetAtomicRaw(NT_Handle subentry, std::span< const uint8_t > defaultValue)
Get the last published value along with its timestamp.
NT_Handle NT_Topic
Definition: ntcore_c.h:40
NT_Handle NT_Subscriber
Definition: ntcore_c.h:41
NT_Handle NT_Publisher
Definition: ntcore_c.h:42
NT_Handle NT_Entry
Definition: ntcore_c.h:35
@ NT_RAW
Definition: ntcore_c.h:56
constexpr PubSubOptions kDefaultPubSubOptions
Default publish/subscribe options.
Definition: ntcore_cpp.h:390
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 PublishEx(NT_Topic topic, NT_Type type, std::string_view typeStr, const wpi::json &properties, const PubSubOptions &options=kDefaultPubSubOptions)
Creates a new publisher to a topic.
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_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.
NetworkTables (ntcore) namespace.
Definition: ntcore_cpp.h:36
Definition: array.h:89
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.h:200
constexpr bool is_constexpr(Lambda)
Definition: type_traits.h:81
NetworkTables publish/subscribe options.
Definition: ntcore_cpp.h:305
Timestamped value.
Definition: ntcore_cpp_types.h:30
int64_t time
Time in local time base.
Definition: ntcore_cpp_types.h:38
int64_t serverTime
Time in server time base.
Definition: ntcore_cpp_types.h:43
T value
Value.
Definition: ntcore_cpp_types.h:48
Struct serialization template.
Definition: Struct.h:38