mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-25 01:41:43 +00:00
[ntcore] Add UnitTopic<T> (C++ only) (#4497)
This avoids the need for explicit value() calls (as compared to using DoubleTopic). The unit name is published as the "unit" property. Implementation note: the test needs to be in wpilibc because ntcore does not depend on wpimath.
This commit is contained in:
411
ntcore/src/main/native/include/networktables/UnitTopic.h
Normal file
411
ntcore/src/main/native/include/networktables/UnitTopic.h
Normal file
@@ -0,0 +1,411 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "networktables/Topic.h"
|
||||
|
||||
namespace wpi {
|
||||
class json;
|
||||
} // namespace wpi
|
||||
|
||||
namespace nt {
|
||||
|
||||
template <typename T>
|
||||
class UnitTopic;
|
||||
|
||||
/**
|
||||
* Timestamped unit.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
struct TimestampedUnit {
|
||||
TimestampedUnit() = default;
|
||||
TimestampedUnit(int64_t time, int64_t serverTime, T value)
|
||||
: time{time}, serverTime{serverTime}, value{value} {}
|
||||
|
||||
/**
|
||||
* Time in local time base.
|
||||
*/
|
||||
int64_t time = 0;
|
||||
|
||||
/**
|
||||
* Time in server time base. May be 0 or 1 for locally set values.
|
||||
*/
|
||||
int64_t serverTime = 0;
|
||||
|
||||
/**
|
||||
* Value.
|
||||
*/
|
||||
T value = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed subscriber.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitSubscriber : public Subscriber {
|
||||
public:
|
||||
using TopicType = UnitTopic<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
|
||||
UnitSubscriber() = default;
|
||||
|
||||
/**
|
||||
* Construct from a subscriber handle; recommended to use
|
||||
* UnitTopic::Subscribe() instead.
|
||||
*
|
||||
* @param handle Native handle
|
||||
* @param defaultValue Default value
|
||||
*/
|
||||
UnitSubscriber(NT_Subscriber handle, ParamType defaultValue);
|
||||
|
||||
/**
|
||||
* Get the last published value.
|
||||
* If no value has been published, returns the stored default value.
|
||||
*
|
||||
* @return value
|
||||
*/
|
||||
ValueType Get() const;
|
||||
|
||||
/**
|
||||
* Get the last published value.
|
||||
* If no value has been published, returns the passed defaultValue.
|
||||
*
|
||||
* @param defaultValue default value to return if no value has been published
|
||||
* @return value
|
||||
*/
|
||||
ValueType Get(ParamType defaultValue) const;
|
||||
|
||||
/**
|
||||
* Get the last published value along with its timestamp
|
||||
* If no value has been published, returns the stored default value and a
|
||||
* timestamp of 0.
|
||||
*
|
||||
* @return timestamped value
|
||||
*/
|
||||
TimestampedValueType GetAtomic() const;
|
||||
|
||||
/**
|
||||
* Get the last published value along with its timestamp.
|
||||
* If no value has been published, returns the passed defaultValue and a
|
||||
* timestamp of 0.
|
||||
*
|
||||
* @param defaultValue default value to return if no value has been published
|
||||
* @return timestamped value
|
||||
*/
|
||||
TimestampedValueType GetAtomic(ParamType defaultValue) const;
|
||||
|
||||
/**
|
||||
* Get an array of all value changes since the last call to ReadQueue.
|
||||
* Also provides a timestamp for each value.
|
||||
*
|
||||
* @note The "poll storage" subscribe option can be used to set the queue
|
||||
* depth.
|
||||
*
|
||||
* @return Array of timestamped values; empty array if no new changes have
|
||||
* been published since the previous call.
|
||||
*/
|
||||
std::vector<TimestampedValueType> ReadQueue();
|
||||
|
||||
/**
|
||||
* Get the corresponding topic.
|
||||
*
|
||||
* @return Topic
|
||||
*/
|
||||
TopicType GetTopic() const;
|
||||
|
||||
private:
|
||||
ValueType m_defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed publisher.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitPublisher : public Publisher {
|
||||
public:
|
||||
using TopicType = UnitTopic<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
|
||||
UnitPublisher() = default;
|
||||
|
||||
/**
|
||||
* Construct from a publisher handle; recommended to use
|
||||
* UnitTopic::Publish() instead.
|
||||
*
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit UnitPublisher(NT_Publisher handle);
|
||||
|
||||
/**
|
||||
* Publish a new value.
|
||||
*
|
||||
* @param value value to publish
|
||||
* @param time timestamp; 0 indicates current NT time should be used
|
||||
*/
|
||||
void Set(ParamType value, int64_t time = 0);
|
||||
|
||||
/**
|
||||
* Publish a default value.
|
||||
* On reconnect, a default value will never be used in preference to a
|
||||
* published value.
|
||||
*
|
||||
* @param value value
|
||||
*/
|
||||
void SetDefault(ParamType value);
|
||||
|
||||
/**
|
||||
* Get the corresponding topic.
|
||||
*
|
||||
* @return Topic
|
||||
*/
|
||||
TopicType GetTopic() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed entry.
|
||||
*
|
||||
* @note Unlike NetworkTableEntry, the entry goes away when this is destroyed.
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitEntry final : public UnitSubscriber<T>, public UnitPublisher<T> {
|
||||
public:
|
||||
using SubscriberType = UnitSubscriber<T>;
|
||||
using PublisherType = UnitPublisher<T>;
|
||||
using TopicType = UnitTopic<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
|
||||
UnitEntry() = default;
|
||||
|
||||
/**
|
||||
* Construct from an entry handle; recommended to use
|
||||
* UnitTopic::GetEntry() instead.
|
||||
*
|
||||
* @param handle Native handle
|
||||
* @param defaultValue Default value
|
||||
*/
|
||||
UnitEntry(NT_Entry handle, ParamType defaultValue);
|
||||
|
||||
/**
|
||||
* Determines if the native handle is valid.
|
||||
*
|
||||
* @return True if the native handle is valid, false otherwise.
|
||||
*/
|
||||
explicit operator bool() const { return this->m_subHandle != 0; }
|
||||
|
||||
/**
|
||||
* Gets the native handle for the entry.
|
||||
*
|
||||
* @return Native handle
|
||||
*/
|
||||
NT_Entry GetHandle() const { return this->m_subHandle; }
|
||||
|
||||
/**
|
||||
* Get the corresponding topic.
|
||||
*
|
||||
* @return Topic
|
||||
*/
|
||||
TopicType GetTopic() const;
|
||||
|
||||
/**
|
||||
* Stops publishing the entry if it's published.
|
||||
*/
|
||||
void Unpublish();
|
||||
};
|
||||
|
||||
/**
|
||||
* NetworkTables unit-typed topic. Publishers publish the type name (e.g.
|
||||
* "meter") as the "unit" property. Type conversions are not performed--for
|
||||
* correct behavior the publisher and subscriber must use the same unit type,
|
||||
* but this can be checked at runtime using IsMatchingUnit().
|
||||
*
|
||||
* @tparam T unit type, e.g. units::meter_t
|
||||
*/
|
||||
template <typename T>
|
||||
class UnitTopic final : public Topic {
|
||||
public:
|
||||
using SubscriberType = UnitSubscriber<T>;
|
||||
using PublisherType = UnitPublisher<T>;
|
||||
using EntryType = UnitEntry<T>;
|
||||
using ValueType = T;
|
||||
using ParamType = T;
|
||||
using TimestampedValueType = TimestampedUnit<T>;
|
||||
/** The default type string for this topic type. */
|
||||
static constexpr std::string_view kTypeString = "double";
|
||||
|
||||
UnitTopic() = default;
|
||||
|
||||
/**
|
||||
* Construct from a topic handle.
|
||||
*
|
||||
* @param handle Native handle
|
||||
*/
|
||||
explicit UnitTopic(NT_Topic handle) : Topic{handle} {}
|
||||
|
||||
/**
|
||||
* Construct from a generic topic.
|
||||
*
|
||||
* @param topic Topic
|
||||
*/
|
||||
explicit UnitTopic(Topic topic) : Topic{topic} {}
|
||||
|
||||
/**
|
||||
* Verify the topic has a matching unit type (if the topic is published).
|
||||
*
|
||||
* @return True if unit matches, false if not matching or topic not published.
|
||||
*/
|
||||
bool IsMatchingUnit() const;
|
||||
|
||||
/**
|
||||
* Create a new subscriber to the topic.
|
||||
*
|
||||
* <p>The subscriber is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note Subscribers that do not match the published data type do not return
|
||||
* any values. To determine if the data type matches, use the appropriate
|
||||
* Topic functions.
|
||||
*
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options subscribe options
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] SubscriberType Subscribe(
|
||||
ParamType defaultValue, std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new subscriber to the topic, with specific type string.
|
||||
*
|
||||
* <p>The subscriber is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note Subscribers that do not match the published data type do not return
|
||||
* any values. To determine if the data type matches, use the appropriate
|
||||
* Topic functions.
|
||||
*
|
||||
* @param typeString type string
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options subscribe options
|
||||
* @return subscriber
|
||||
*/
|
||||
[[nodiscard]] SubscriberType SubscribeEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic.
|
||||
*
|
||||
* The publisher is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note It is not possible to publish two different data types to the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored). To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param options publish options
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]] PublisherType Publish(
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new publisher to the topic, with type string and initial
|
||||
* properties.
|
||||
*
|
||||
* The publisher is only active as long as the returned object
|
||||
* is not destroyed.
|
||||
*
|
||||
* @note It is not possible to publish two different data types to the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored). To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param typeString type string
|
||||
* @param properties JSON properties
|
||||
* @param options publish options
|
||||
* @return publisher
|
||||
*/
|
||||
[[nodiscard]] PublisherType PublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic.
|
||||
*
|
||||
* Entries act as a combination of a subscriber and a weak publisher. The
|
||||
* subscriber is active as long as the entry is not destroyed. The publisher
|
||||
* is created when the entry is first written to, and remains active until
|
||||
* either Unpublish() is called or the entry is destroyed.
|
||||
*
|
||||
* @note It is not possible to use two different data types with the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored), and the entry
|
||||
* will show no new values if the data type does not match. To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options publish and/or subscribe options
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] EntryType GetEntry(ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
|
||||
/**
|
||||
* Create a new entry for the topic, with specific type string.
|
||||
*
|
||||
* Entries act as a combination of a subscriber and a weak publisher. The
|
||||
* subscriber is active as long as the entry is not destroyed. The publisher
|
||||
* is created when the entry is first written to, and remains active until
|
||||
* either Unpublish() is called or the entry is destroyed.
|
||||
*
|
||||
* @note It is not possible to use two different data types with the same
|
||||
* topic. Conflicts between publishers are typically resolved by the
|
||||
* server on a first-come, first-served basis. Any published values that
|
||||
* do not match the topic's data type are dropped (ignored), and the entry
|
||||
* will show no new values if the data type does not match. To determine
|
||||
* if the data type matches, use the appropriate Topic functions.
|
||||
*
|
||||
* @param typeString type string
|
||||
* @param defaultValue default value used when a default is not provided to a
|
||||
* getter function
|
||||
* @param options publish and/or subscribe options
|
||||
* @return entry
|
||||
*/
|
||||
[[nodiscard]] EntryType GetEntryEx(
|
||||
std::string_view typeString, ParamType defaultValue,
|
||||
std::span<const PubSubOption> options = {});
|
||||
};
|
||||
|
||||
} // namespace nt
|
||||
|
||||
#include "networktables/UnitTopic.inc"
|
||||
142
ntcore/src/main/native/include/networktables/UnitTopic.inc
Normal file
142
ntcore/src/main/native/include/networktables/UnitTopic.inc
Normal file
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/json.h>
|
||||
|
||||
#include "networktables/NetworkTableType.h"
|
||||
#include "networktables/UnitTopic.h"
|
||||
#include "ntcore_cpp.h"
|
||||
|
||||
namespace nt {
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T>::UnitSubscriber(NT_Subscriber handle, T defaultValue)
|
||||
: Subscriber{handle}, m_defaultValue{defaultValue} {}
|
||||
|
||||
template <typename T>
|
||||
inline T UnitSubscriber<T>::Get() const {
|
||||
return Get(m_defaultValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T UnitSubscriber<T>::Get(T defaultValue) const {
|
||||
return T{::nt::GetDouble(m_subHandle, defaultValue.value())};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TimestampedUnit<T> UnitSubscriber<T>::GetAtomic() const {
|
||||
return GetAtomic(m_defaultValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline TimestampedUnit<T> UnitSubscriber<T>::GetAtomic(T defaultValue) const {
|
||||
auto doubleVal = ::nt::GetAtomicDouble(m_subHandle, defaultValue.value());
|
||||
return {doubleVal.time, doubleVal.serverTime, doubleVal.value};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::vector<TimestampedUnit<T>> UnitSubscriber<T>::ReadQueue() {
|
||||
std::vector<TimestampedUnit<T>> vals;
|
||||
auto doubleVals = ::nt::ReadQueueDouble(m_subHandle);
|
||||
vals.reserve(doubleVals.size());
|
||||
for (auto&& val : doubleVals) {
|
||||
vals.emplace_back(val.time, val.serverTime, val.value);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitTopic<T> UnitSubscriber<T>::GetTopic() const {
|
||||
return UnitTopic<T>{::nt::GetTopicFromHandle(m_subHandle)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T>::UnitPublisher(NT_Publisher handle)
|
||||
: Publisher{handle} {}
|
||||
|
||||
template <typename T>
|
||||
inline void UnitPublisher<T>::Set(T value, int64_t time) {
|
||||
::nt::SetDouble(m_pubHandle, value.value(), time);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void UnitPublisher<T>::SetDefault(T value) {
|
||||
::nt::SetDefaultDouble(m_pubHandle, value.value());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitTopic<T> UnitPublisher<T>::GetTopic() const {
|
||||
return UnitTopic<T>{::nt::GetTopicFromHandle(m_pubHandle)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T>::UnitEntry(NT_Entry handle, T defaultValue)
|
||||
: UnitSubscriber<T>{handle, defaultValue}, UnitPublisher<T>{handle} {}
|
||||
|
||||
template <typename T>
|
||||
inline UnitTopic<T> UnitEntry<T>::GetTopic() const {
|
||||
return UnitTopic<T>{::nt::GetTopicFromHandle(this->m_subHandle)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void UnitEntry<T>::Unpublish() {
|
||||
::nt::Unpublish(this->m_pubHandle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool UnitTopic<T>::IsMatchingUnit() const {
|
||||
return GetProperty("unit") == T{}.name();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T> UnitTopic<T>::Subscribe(
|
||||
T defaultValue, std::span<const PubSubOption> options) {
|
||||
return UnitSubscriber<T>{
|
||||
::nt::Subscribe(m_handle, NT_DOUBLE, "double", options), defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitSubscriber<T> UnitTopic<T>::SubscribeEx(
|
||||
std::string_view typeString, T defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
return UnitSubscriber<T>{
|
||||
::nt::Subscribe(m_handle, NT_DOUBLE, typeString, options), defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T> UnitTopic<T>::Publish(
|
||||
std::span<const PubSubOption> options) {
|
||||
return UnitPublisher<T>{::nt::PublishEx(m_handle, NT_DOUBLE, "double",
|
||||
{{"unit", T{}.name()}}, options)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitPublisher<T> UnitTopic<T>::PublishEx(
|
||||
std::string_view typeString, const wpi::json& properties,
|
||||
std::span<const PubSubOption> options) {
|
||||
wpi::json props = properties;
|
||||
props["unit"] = T{}.name();
|
||||
return UnitPublisher<T>{
|
||||
::nt::PublishEx(m_handle, NT_DOUBLE, typeString, props, options)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntry(
|
||||
T defaultValue, std::span<const PubSubOption> options) {
|
||||
return UnitEntry<T>{::nt::GetEntry(m_handle, NT_DOUBLE, "double", options),
|
||||
defaultValue};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline UnitEntry<T> UnitTopic<T>::GetEntryEx(
|
||||
std::string_view typeString, T defaultValue,
|
||||
std::span<const PubSubOption> options) {
|
||||
return UnitEntry<T>{::nt::GetEntry(m_handle, NT_DOUBLE, typeString, options),
|
||||
defaultValue};
|
||||
}
|
||||
|
||||
} // namespace nt
|
||||
Reference in New Issue
Block a user