WPILibC++ 2025.0.0-alpha-1-14-g3b6f38d
ProtobufTopic.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 <span>
12#include <string_view>
13#include <utility>
14#include <vector>
15
16#include <wpi/SmallVector.h>
17#include <wpi/json_fwd.h>
18#include <wpi/mutex.h>
20
22#include "networktables/Topic.h"
23#include "ntcore_cpp.h"
24
25namespace nt {
26
27template <wpi::ProtobufSerializable T>
28class ProtobufTopic;
29
30/**
31 * NetworkTables protobuf-encoded value subscriber.
32 */
33template <wpi::ProtobufSerializable T>
35 public:
37 using ValueType = T;
38 using ParamType = const T&;
40
41 ProtobufSubscriber() = default;
42
43 /**
44 * Construct from a subscriber handle; recommended to use
45 * ProtobufTopic::Subscribe() instead.
46 *
47 * @param handle Native handle
48 * @param msg Protobuf message
49 * @param defaultValue Default value
50 */
52 T defaultValue)
53 : Subscriber{handle},
54 m_msg{std::move(msg)},
55 m_defaultValue{std::move(defaultValue)} {}
56
59
61 : Subscriber{std::move(rhs)},
62 m_msg{std::move(rhs.m_msg)},
63 m_defaultValue{std::move(rhs.m_defaultValue)} {}
64
66 Subscriber::operator=(std::move(rhs));
67 m_msg = std::move(rhs.m_msg);
68 m_defaultValue = std::move(rhs.m_defaultValue);
69 return *this;
70 }
71
72 /**
73 * Get the last published value.
74 * If no value has been published or the value cannot be unpacked, returns the
75 * stored default value.
76 *
77 * @return value
78 */
79 ValueType Get() const { return Get(m_defaultValue); }
80
81 /**
82 * Get the last published value.
83 * If no value has been published or the value cannot be unpacked, returns the
84 * passed defaultValue.
85 *
86 * @param defaultValue default value to return if no value has been published
87 * @return value
88 */
89 ValueType Get(const T& defaultValue) const {
90 return GetAtomic(defaultValue).value;
91 }
92
93 /**
94 * Get the last published value, replacing the contents in place of an
95 * existing object. If no value has been published or the value cannot be
96 * unpacked, does not replace the contents and returns false.
97 *
98 * @param[out] out object to replace contents of
99 * @return true if successful
100 */
101 bool GetInto(T* out) {
104 if (view.value.empty()) {
105 return false;
106 } else {
107 std::scoped_lock lock{m_mutex};
108 return m_msg.UnpackInto(out, view.value);
109 }
110 }
111
112 /**
113 * Get the last published value along with its timestamp
114 * If no value has been published or the value cannot be unpacked, returns the
115 * stored default value and a timestamp of 0.
116 *
117 * @return timestamped value
118 */
119 TimestampedValueType GetAtomic() const { return GetAtomic(m_defaultValue); }
120
121 /**
122 * Get the last published value along with its timestamp.
123 * If no value has been published or the value cannot be unpacked, returns the
124 * passed defaultValue and a timestamp of 0.
125 *
126 * @param defaultValue default value to return if no value has been published
127 * @return timestamped value
128 */
129 TimestampedValueType GetAtomic(const T& defaultValue) const {
132 if (!view.value.empty()) {
133 std::scoped_lock lock{m_mutex};
134 if (auto optval = m_msg.Unpack(view.value)) {
135 return {view.time, view.serverTime, *optval};
136 }
137 }
138 return {0, 0, defaultValue};
139 }
140
141 /**
142 * Get an array of all valid value changes since the last call to ReadQueue.
143 * Also provides a timestamp for each value. Values that cannot be unpacked
144 * are dropped.
145 *
146 * @note The "poll storage" subscribe option can be used to set the queue
147 * depth.
148 *
149 * @return Array of timestamped values; empty array if no valid new changes
150 * have been published since the previous call.
151 */
152 std::vector<TimestampedValueType> ReadQueue() {
154 std::vector<TimestampedValueType> rv;
155 rv.reserve(raw.size());
156 std::scoped_lock lock{m_mutex};
157 for (auto&& r : raw) {
158 if (auto optval = m_msg.Unpack(r.value)) {
159 rv.emplace_back(r.time, r.serverTime, *optval);
160 }
161 }
162 return rv;
163 }
164
165 /**
166 * Get the corresponding topic.
167 *
168 * @return Topic
169 */
172 }
173
174 private:
175 mutable wpi::mutex m_mutex;
176 mutable wpi::ProtobufMessage<T> m_msg;
177 ValueType m_defaultValue;
178};
179
180/**
181 * NetworkTables protobuf-encoded value publisher.
182 */
183template <wpi::ProtobufSerializable T>
185 public:
187 using ValueType = T;
188 using ParamType = const T&;
189
191
192 ProtobufPublisher() = default;
193
194 /**
195 * Construct from a publisher handle; recommended to use
196 * ProtobufTopic::Publish() instead.
197 *
198 * @param handle Native handle
199 * @param msg Protobuf message
200 */
202 : Publisher{handle}, m_msg{std::move(msg)} {}
203
206
208 : Publisher{std::move(rhs)},
209 m_msg{std::move(rhs.m_msg)},
210 m_schemaPublished{rhs.m_schemaPublished} {}
211
213 Publisher::operator=(std::move(rhs));
214 m_msg = std::move(rhs.m_msg);
215 m_schemaPublished.store(
216 rhs.m_schemaPublished.load(std::memory_order_relaxed),
217 std::memory_order_relaxed);
218 return *this;
219 }
220
221 /**
222 * Publish a new value.
223 *
224 * @param value value to publish
225 * @param time timestamp; 0 indicates current NT time should be used
226 */
227 void Set(const T& value, int64_t time = 0) {
229 {
230 std::scoped_lock lock{m_mutex};
231 if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
232 GetTopic().GetInstance().template AddProtobufSchema<T>(m_msg);
233 }
234 m_msg.Pack(buf, value);
235 }
236 ::nt::SetRaw(m_pubHandle, buf, time);
237 }
238
239 /**
240 * Publish a default value.
241 * On reconnect, a default value will never be used in preference to a
242 * published value.
243 *
244 * @param value value
245 */
246 void SetDefault(const T& value) {
248 {
249 std::scoped_lock lock{m_mutex};
250 if (!m_schemaPublished.exchange(true, std::memory_order_relaxed)) {
251 GetTopic().GetInstance().template AddProtobufSchema<T>(m_msg);
252 }
253 m_msg.Pack(buf, value);
254 }
256 }
257
258 /**
259 * Get the corresponding topic.
260 *
261 * @return Topic
262 */
265 }
266
267 private:
268 wpi::mutex m_mutex;
270 std::atomic_bool m_schemaPublished{false};
271};
272
273/**
274 * NetworkTables protobuf-encoded value entry.
275 *
276 * @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
277 */
278template <wpi::ProtobufSerializable T>
279class ProtobufEntry final : public ProtobufSubscriber<T>,
280 public ProtobufPublisher<T> {
281 public:
285 using ValueType = T;
286 using ParamType = const T&;
287
289
290 ProtobufEntry() = default;
291
292 /**
293 * Construct from an entry handle; recommended to use
294 * ProtobufTopic::GetEntry() instead.
295 *
296 * @param handle Native handle
297 * @param msg Protobuf message
298 * @param defaultValue Default value
299 */
300 ProtobufEntry(NT_Entry handle, wpi::ProtobufMessage<T> msg, T defaultValue)
301 : ProtobufSubscriber<T>{handle, std::move(msg), std::move(defaultValue)},
303
304 /**
305 * Determines if the native handle is valid.
306 *
307 * @return True if the native handle is valid, false otherwise.
308 */
309 explicit operator bool() const { return this->m_subHandle != 0; }
310
311 /**
312 * Gets the native handle for the entry.
313 *
314 * @return Native handle
315 */
316 NT_Entry GetHandle() const { return this->m_subHandle; }
317
318 /**
319 * Get the corresponding topic.
320 *
321 * @return Topic
322 */
325 }
326
327 /**
328 * Stops publishing the entry if it's published.
329 */
331};
332
333/**
334 * NetworkTables protobuf-encoded value topic.
335 */
336template <wpi::ProtobufSerializable T>
337class ProtobufTopic final : public Topic {
338 public:
342 using ValueType = T;
343 using ParamType = const T&;
345
346 ProtobufTopic() = default;
347
348 /**
349 * Construct from a topic handle; recommended to use
350 * NetworkTableInstance::GetProtobufTopic() instead.
351 *
352 * @param handle Native handle
353 */
354 explicit ProtobufTopic(NT_Topic handle) : Topic{handle} {}
355
356 /**
357 * Construct from a generic topic.
358 *
359 * @param topic Topic
360 */
361 explicit ProtobufTopic(Topic topic) : Topic{topic} {}
362
363 /**
364 * Create a new subscriber to the topic.
365 *
366 * <p>The subscriber is only active as long as the returned object
367 * is not destroyed.
368 *
369 * @note Subscribers that do not match the published data type do not return
370 * any values. To determine if the data type matches, use the appropriate
371 * Topic functions.
372 *
373 * @param defaultValue default value used when a default is not provided to a
374 * getter function
375 * @param options subscribe options
376 * @return subscriber
377 */
378 [[nodiscard]]
380 T defaultValue, const PubSubOptions& options = kDefaultPubSubOptions) {
382 auto typeString = msg.GetTypeString();
384 ::nt::Subscribe(m_handle, NT_RAW, typeString, options), std::move(msg),
385 std::move(defaultValue)};
386 }
387
388 /**
389 * Create a new publisher to the topic.
390 *
391 * The publisher is only active as long as the returned object
392 * is not destroyed.
393 *
394 * @note It is not possible to publish two different data types to the same
395 * topic. Conflicts between publishers are typically resolved by the
396 * server on a first-come, first-served basis. Any published values that
397 * do not match the topic's data type are dropped (ignored). To determine
398 * if the data type matches, use the appropriate Topic functions.
399 *
400 * @param options publish options
401 * @return publisher
402 */
403 [[nodiscard]]
406 auto typeString = msg.GetTypeString();
408 ::nt::Publish(m_handle, NT_RAW, typeString, options), std::move(msg)};
409 }
410
411 /**
412 * Create a new publisher to the topic, with type string and initial
413 * properties.
414 *
415 * The publisher is only active as long as the returned object
416 * is not destroyed.
417 *
418 * @note It is not possible to publish two different data types to the same
419 * topic. Conflicts between publishers are typically resolved by the
420 * server on a first-come, first-served basis. Any published values that
421 * do not match the topic's data type are dropped (ignored). To determine
422 * if the data type matches, use the appropriate Topic functions.
423 *
424 * @param properties JSON properties
425 * @param options publish options
426 * @return publisher
427 */
428 [[nodiscard]]
430 const wpi::json& properties,
431 const PubSubOptions& options = kDefaultPubSubOptions) {
433 auto typeString = msg.GetTypeString();
435 ::nt::PublishEx(m_handle, NT_RAW, typeString, properties, options),
436 std::move(msg)};
437 }
438
439 /**
440 * Create a new entry for the topic.
441 *
442 * Entries act as a combination of a subscriber and a weak publisher. The
443 * subscriber is active as long as the entry is not destroyed. The publisher
444 * is created when the entry is first written to, and remains active until
445 * either Unpublish() is called or the entry is destroyed.
446 *
447 * @note It is not possible to use two different data types with the same
448 * topic. Conflicts between publishers are typically resolved by the
449 * server on a first-come, first-served basis. Any published values that
450 * do not match the topic's data type are dropped (ignored), and the entry
451 * will show no new values if the data type does not match. To determine
452 * if the data type matches, use the appropriate Topic functions.
453 *
454 * @param defaultValue default value used when a default is not provided to a
455 * getter function
456 * @param options publish and/or subscribe options
457 * @return entry
458 */
459 [[nodiscard]]
460 EntryType GetEntry(T defaultValue,
461 const PubSubOptions& options = kDefaultPubSubOptions) {
463 auto typeString = msg.GetTypeString();
464 return ProtobufEntry<T>{
465 ::nt::GetEntry(m_handle, NT_RAW, typeString, options), std::move(msg),
466 std::move(defaultValue)};
467 }
468};
469
470} // namespace nt
This file defines the SmallVector class.
NetworkTables protobuf-encoded value entry.
Definition: ProtobufTopic.h:280
ProtobufEntry()=default
NT_Entry GetHandle() const
Gets the native handle for the entry.
Definition: ProtobufTopic.h:316
void Unpublish()
Stops publishing the entry if it's published.
Definition: ProtobufTopic.h:330
TopicType GetTopic() const
Get the corresponding topic.
Definition: ProtobufTopic.h:323
ProtobufEntry(NT_Entry handle, wpi::ProtobufMessage< T > msg, T defaultValue)
Construct from an entry handle; recommended to use ProtobufTopic::GetEntry() instead.
Definition: ProtobufTopic.h:300
NetworkTables protobuf-encoded value publisher.
Definition: ProtobufTopic.h:184
T ValueType
Definition: ProtobufTopic.h:187
ProtobufPublisher(const ProtobufPublisher &)=delete
ProtobufPublisher()=default
void SetDefault(const T &value)
Publish a default value.
Definition: ProtobufTopic.h:246
ProtobufPublisher(NT_Publisher handle, wpi::ProtobufMessage< T > msg)
Construct from a publisher handle; recommended to use ProtobufTopic::Publish() instead.
Definition: ProtobufTopic.h:201
void Set(const T &value, int64_t time=0)
Publish a new value.
Definition: ProtobufTopic.h:227
TopicType GetTopic() const
Get the corresponding topic.
Definition: ProtobufTopic.h:263
ProtobufPublisher(ProtobufPublisher &&rhs)
Definition: ProtobufTopic.h:207
const T & ParamType
Definition: ProtobufTopic.h:188
ProtobufPublisher & operator=(ProtobufPublisher &&rhs)
Definition: ProtobufTopic.h:212
ProtobufPublisher & operator=(const ProtobufPublisher &)=delete
NetworkTables protobuf-encoded value subscriber.
Definition: ProtobufTopic.h:34
TimestampedValueType GetAtomic(const T &defaultValue) const
Get the last published value along with its timestamp.
Definition: ProtobufTopic.h:129
bool GetInto(T *out)
Get the last published value, replacing the contents in place of an existing object.
Definition: ProtobufTopic.h:101
ValueType Get(const T &defaultValue) const
Get the last published value.
Definition: ProtobufTopic.h:89
TimestampedValueType GetAtomic() const
Get the last published value along with its timestamp If no value has been published or the value can...
Definition: ProtobufTopic.h:119
std::vector< TimestampedValueType > ReadQueue()
Get an array of all valid value changes since the last call to ReadQueue.
Definition: ProtobufTopic.h:152
ProtobufSubscriber & operator=(const ProtobufSubscriber &)=delete
TopicType GetTopic() const
Get the corresponding topic.
Definition: ProtobufTopic.h:170
const T & ParamType
Definition: ProtobufTopic.h:38
ProtobufSubscriber & operator=(ProtobufSubscriber &&rhs)
Definition: ProtobufTopic.h:65
ProtobufSubscriber(const ProtobufSubscriber &)=delete
ProtobufSubscriber(ProtobufSubscriber &&rhs)
Definition: ProtobufTopic.h:60
ProtobufSubscriber(NT_Subscriber handle, wpi::ProtobufMessage< T > msg, T defaultValue)
Construct from a subscriber handle; recommended to use ProtobufTopic::Subscribe() instead.
Definition: ProtobufTopic.h:51
ProtobufSubscriber()=default
T ValueType
Definition: ProtobufTopic.h:37
ValueType Get() const
Get the last published value.
Definition: ProtobufTopic.h:79
NetworkTables protobuf-encoded value topic.
Definition: ProtobufTopic.h:337
ProtobufTopic()=default
EntryType GetEntry(T defaultValue, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new entry for the topic.
Definition: ProtobufTopic.h:460
ProtobufTopic(Topic topic)
Construct from a generic topic.
Definition: ProtobufTopic.h:361
SubscriberType Subscribe(T defaultValue, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new subscriber to the topic.
Definition: ProtobufTopic.h:379
PublisherType PublishEx(const wpi::json &properties, const PubSubOptions &options=kDefaultPubSubOptions)
Create a new publisher to the topic, with type string and initial properties.
Definition: ProtobufTopic.h:429
ProtobufTopic(NT_Topic handle)
Construct from a topic handle; recommended to use NetworkTableInstance::GetProtobufTopic() instead.
Definition: ProtobufTopic.h:354
T ValueType
Definition: ProtobufTopic.h:342
PublisherType Publish(const PubSubOptions &options=kDefaultPubSubOptions)
Create a new publisher to the topic.
Definition: ProtobufTopic.h:404
const T & ParamType
Definition: ProtobufTopic.h:343
NetworkTables publisher.
Definition: Topic.h:364
Publisher & operator=(const Publisher &)=delete
NT_Publisher m_pubHandle
NetworkTables handle.
Definition: Topic.h:400
NetworkTables subscriber.
Definition: Topic.h:309
Subscriber & operator=(const Subscriber &)=delete
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.
Owning wrapper (ala std::unique_ptr) for google::protobuf::Message* that does not require the protobu...
Definition: Protobuf.h:158
std::string GetTypeString() const
Gets the type string for the message.
Definition: Protobuf.h:238
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1211
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: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:58
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: NetworkTableListener.inc:19
Implement std::hash so that hash_code can be used in STL containers.
Definition: array.h:89
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
::std::mutex mutex
Definition: mutex.h:17