Valor 6800 1.0
Loading...
Searching...
No Matches
Loggable.h
1
2#pragma once
3#include <memory>
4#include <string>
5#include <tuple>
6#include <unordered_map>
7#include <utility>
8#include <vector>
9#include <algorithm>
10
11#include <magic_enum/magic_enum.hpp>
12#include <frc2/command/RunCommand.h>
13#include <networktables/BooleanArrayTopic.h>
14#include <networktables/BooleanTopic.h>
15#include <networktables/DoubleArrayTopic.h>
16#include <networktables/DoubleTopic.h>
17#include <networktables/FloatTopic.h>
18#include <networktables/ProtobufTopic.h>
19#include <networktables/IntegerArrayTopic.h>
20#include <networktables/IntegerTopic.h>
21#include <networktables/NetworkTableInstance.h>
22#include <networktables/StringArrayTopic.h>
23#include <networktables/StringTopic.h>
24#include <networktables/StructArrayTopic.h>
25#include <networktables/StructTopic.h>
26#include <units/base.h>
27#include <wpi/protobuf/Protobuf.h>
28#include <wpi/sendable/Sendable.h>
29#include <wpi/struct/Struct.h>
30#include <wpi/json.h>
31#include <units/angular_velocity.h>
32#include <units/acceleration.h>
33#include <units/angular_acceleration.h>
34#include <units/time.h>
35#include <units/temperature.h>
36#include <units/current.h>
37#include <frc/Timer.h>
38
39namespace valor {
40
41namespace detail {
42template <typename T>
43inline constexpr bool is_unit_vector = false;
44
45template <typename T>
46inline constexpr bool is_unit_vector<std::vector<T>> = units::traits::is_unit_t_v<T>;
47
48template <typename T>
49struct nt_traits {
50 static_assert(sizeof(T) == 0, "No valid NetworkTables trait found for the specified data type");
51};
52
53template <>
54struct nt_traits<bool> {
55 static constexpr auto func = &nt::NetworkTable::GetBooleanTopic;
56 static constexpr std::string_view type = "boolean";
57};
58
59template <>
60struct nt_traits<float> {
61 static constexpr auto func = &nt::NetworkTable::GetFloatTopic;
62 static constexpr std::string_view type = "float";
63};
64
65template <>
66struct nt_traits<double> {
67 static constexpr auto func = &nt::NetworkTable::GetDoubleTopic;
68 static constexpr std::string_view type = "double";
69};
70
71template <typename T>
72 requires units::traits::is_unit_t_v<T>
73struct nt_traits<T> : nt_traits<double> {};
74
75template <std::integral T>
76struct nt_traits<T> {
77 static constexpr auto func = &nt::NetworkTable::GetIntegerTopic;
78 static constexpr std::string_view type = "int";
79};
80
81template <typename T>
82 requires std::convertible_to<T, std::string_view>
83struct nt_traits<T> {
84 static constexpr auto func = &nt::NetworkTable::GetStringTopic;
85 static constexpr std::string_view type = "string";
86};
87
88template <typename T>
89 requires std::is_enum_v<T>
90struct nt_traits<T> : nt_traits<std::string_view> {};
91
92template <typename T>
93 requires wpi::StructSerializable<T> && (!std::is_arithmetic_v<T>)
94struct nt_traits<T> {
95 static constexpr auto func = &nt::NetworkTable::GetStructTopic<T>;
96 inline static std::string type = "struct:" + std::string{wpi::Struct<T>::GetTypeName()};
97};
98
99template <typename T>
100 requires wpi::ProtobufSerializable<T> && (!wpi::StructSerializable<T>)
101struct nt_traits<T> {
102 static constexpr auto func = &nt::NetworkTable::GetProtobufTopic<T>;
103 static constexpr std::string_view type = "proto:";
104};
105
106template <>
107struct nt_traits<std::vector<bool>> {
108 static constexpr auto func = &nt::NetworkTable::GetBooleanArrayTopic;
109 static constexpr std::string_view type = "boolean[]";
110};
111
112template <>
113struct nt_traits<std::vector<float>> {
114 static constexpr auto func = &nt::NetworkTable::GetFloatArrayTopic;
115 static constexpr std::string_view type = "float[]";
116};
117
118template <>
119struct nt_traits<std::vector<double>> {
120 static constexpr auto func = &nt::NetworkTable::GetDoubleArrayTopic;
121 static constexpr std::string_view type = "double[]";
122};
123
124template <typename T>
125 requires is_unit_vector<T>
126struct nt_traits<T> : nt_traits<std::vector<double>> {};
127
128template <std::integral T>
129struct nt_traits<std::vector<T>> {
130 static constexpr auto func = &nt::NetworkTable::GetIntegerArrayTopic;
131 static constexpr std::string_view type = "int64[]";
132};
133
134template <typename T>
135 requires std::convertible_to<T, std::string_view>
136struct nt_traits<std::vector<T>> {
137 static constexpr auto func = &nt::NetworkTable::GetStringArrayTopic;
138 static constexpr std::string_view type = "string[]";
139};
140
141template <typename T>
142 requires wpi::StructSerializable<T> && (!std::is_arithmetic_v<T>)
143struct nt_traits<std::vector<T>> {
144 static constexpr auto func = &nt::NetworkTable::GetStructArrayTopic<T>;
145 inline static std::string type = "struct:" + std::string{wpi::Struct<T>::GetTypeName()} + "[]";
146};
147
148template <typename T>
149inline constexpr std::string_view unit_string = "";
150
151#define UNIT_STRING(unit, str) \
152 template <> \
153 inline constexpr std::string_view unit_string<units::unit##_t> = str
154UNIT_STRING(turn, "rot");
155
156UNIT_STRING(radians_per_second, "rad per sec");
157UNIT_STRING(degrees_per_second, "deg per sec");
158UNIT_STRING(turns_per_second, "rps");
159
160UNIT_STRING(meters_per_second_squared, "mps2");
161UNIT_STRING(feet_per_second_squared, "fps2");
162UNIT_STRING(standard_gravity, "g");
163
164UNIT_STRING(radians_per_second_squared, "rad per sec2");
165UNIT_STRING(degrees_per_second_squared, "deg per sec2");
166UNIT_STRING(turns_per_second_squared, "rps2");
167
168UNIT_STRING(day, "day");
169
170UNIT_STRING(celsius, "c");
171UNIT_STRING(fahrenheit, "f");
172
173UNIT_STRING(ampere, "amp");
174#undef UNIT_STRING
175
177 using is_transparent = void;
178 size_t operator()(std::string_view sv) const { return std::hash<std::string_view>{}(sv); }
179 size_t operator()(const std::string& s) const { return std::hash<std::string>{}(s); }
180};
181
183 using is_transparent = void;
184 bool operator()(std::string_view lhs, std::string_view rhs) const { return lhs == rhs; }
185};
186
187} // namespace detail
188
204class Loggable {
205 std::unordered_map<std::string, std::unique_ptr<nt::Publisher>, detail::string_hash, detail::string_equal> publishers;
206 std::unordered_map<std::string, std::unique_ptr<nt::Subscriber>, detail::string_hash, detail::string_equal> subscribers;
207
208 public:
220 void LogChild(std::string_view name, Loggable* child);
221
237 void LogChild(std::string_view name, wpi::Sendable* child);
238
239 static units::millisecond_t GetLoggingTime();
240
251 void setLoggingPeriod(units::millisecond_t period) { loggingPeriod = period; }
252
253 protected:
265 explicit Loggable(std::string_view name);
266
275
279 virtual ~Loggable() = default;
280
287 virtual void OnLoggingStart() {}
288
296 virtual void LoggablePeriodic() {}
297
312 template <typename T>
313 inline T WriteLog(std::string_view field, const T& data) {
314 loggingProfiler.Start();
315 if (!table) {
316 loggingProfiler.Stop();
317 return data;
318 }
319 auto pub = publishers.find(field);
320 if (pub == publishers.end())
322 // else
323 // checkTopicType<T>(pub->second->GetTopic().GetTypeString(), field);
324 WriteLogImpl(pub->second.get(), data);
325 loggingProfiler.Stop();
326 return data;
327 }
328
342 template <typename T>
343 inline T ReadLog(std::string_view field, const T& defaultValue = {}) {
344 loggingProfiler.Start();
345 if (!table) {
346 loggingProfiler.Stop();
347 return defaultValue;
348 }
349 auto sub = subscribers.find(field);
350 if (sub == subscribers.end())
351 sub = addSubscriber(field, defaultValue);
352 // else
353 // checkTopicType<T>(sub->second->GetTopic().GetTypeString(), field);
354 T value = ReadLogImpl<T>(sub->second.get());
355 if (value == defaultValue && !publishers.contains(field))
357 loggingProfiler.Stop();
358 return value;
359 }
360
361 private:
362 template <typename T>
363 using TopicType = decltype((std::declval<nt::NetworkTable>().*detail::nt_traits<T>::func)(""));
364
375 void EnableLogging();
376
383 template <typename T>
384 inline void WriteLogImpl(nt::Publisher* pub, const T& data) {
385 static_cast<typename TopicType<T>::PublisherType*>(pub)->Set(data);
386 }
387
388 template <typename T>
389 requires units::traits::is_unit_t_v<T>
390 inline void WriteLogImpl(nt::Publisher* pub, const T& data) {
391 WriteLogImpl(pub, data.value());
392 }
393
394 template <typename T>
395 requires detail::is_unit_vector<T>
396 void WriteLogImpl(nt::Publisher* pub, const T& data) {
397 std::vector<double> castedData;
398 castedData.reserve(data.size());
399 std::transform(data.begin(), data.end(), std::back_inserter(castedData), [](auto val) { return val.value(); });
400 WriteLogImpl(pub, castedData);
401 }
402
403 template <typename T>
404 requires std::is_enum_v<T>
405 void WriteLogImpl(nt::Publisher* pub, const T& data) {
406 WriteLogImpl(pub, magic_enum::enum_name(data));
407 }
408
414 template <typename T>
415 inline T ReadLogImpl(nt::Subscriber* sub) {
416 return static_cast<TopicType<T>::SubscriberType*>(sub)->Get();
417 }
418
419 template <typename T>
420 requires units::traits::is_unit_t_v<T>
421 inline T ReadLogImpl(nt::Subscriber* sub) {
422 return T{ReadLogImpl<double>(sub)};
423 }
424
425 template <typename T>
426 requires detail::is_unit_vector<T>
427 T ReadLogImpl(nt::Subscriber* sub) {
430 castedData.reserve(rawData.size());
431 for (double data : rawData)
433 return castedData;
434 }
435
436 template <typename T>
437 requires std::is_enum_v<T>
438 T ReadLogImpl(nt::Subscriber* sub) {
439 return magic_enum::enum_cast<T>(ReadLogImpl<std::string_view>(sub)).value_or(T{});
440 }
441
447 template <typename T>
448 inline decltype(publishers)::iterator addPublisher(std::string_view field) {
449 return publishers
450 .emplace(field,
451 std::make_unique<typename TopicType<T>::PublisherType>((table.get()->*detail::nt_traits<T>::func)(field).Publish()))
452 .first;
453 }
454
455 template <typename T>
456 requires units::traits::is_unit_t_v<T>
457 decltype(publishers)::iterator addPublisher(std::string_view field) {
458 std::string unitValue{detail::unit_string<T>};
459 if (unitValue.empty())
460 unitValue = T{}.abbreviation();
461
462 return publishers
463 .emplace(field, std::make_unique<nt::DoublePublisher>(table->GetDoubleTopic(field).PublishEx("double", {{"unit", unitValue}})))
464 .first;
465 }
466
473 template <typename T>
474 inline decltype(subscribers)::iterator addSubscriber(std::string_view field, const T& defaultValue) {
475 return subscribers
476 .emplace(field, std::make_unique<typename TopicType<T>::SubscriberType>(
477 (table.get()->*detail::nt_traits<T>::func)(field).Subscribe(defaultValue)))
478 .first;
479 }
480
481 template <typename T>
482 requires units::traits::is_unit_t_v<T>
483 inline decltype(subscribers)::iterator addSubscriber(std::string_view field, const T& defaultValue) {
484 return addSubscriber(field, defaultValue.value());
485 }
486
487 template <typename T>
488 requires detail::is_unit_vector<T>
489 decltype(subscribers)::iterator addSubscriber(std::string_view field, const T& defaultValue) {
490 thread_local std::vector<double> defaultData;
491 defaultData.clear();
492 defaultData.reserve(defaultValue.size());
493 std::transform(defaultValue.begin(), defaultValue.end(), std::back_inserter(defaultData), [](auto val) { return val.value(); });
494 return addSubscriber(field, defaultData);
495 }
496
497 template <typename T>
498 requires std::is_enum_v<T>
499 decltype(subscribers)::iterator addSubscriber(std::string_view field, const T& defaultValue) {
500 return addSubscriber(field, magic_enum::enum_name(defaultValue));
501 }
502
513 template <typename T>
514 void checkTopicType(std::string_view topicType, std::string_view field) {
515 if (topicType.empty())
516 return;
517 FRC_AssertMessage(topicType == detail::nt_traits<T>::type,
518 "Current type of '{}' doesn't match original topic type (received '{}', expected '{}')", field,
519 detail::nt_traits<T>::type, topicType);
520 }
521
522 std::shared_ptr<nt::NetworkTable> table;
523 std::unordered_map<std::string, Loggable*> loggableChildren;
524 std::unordered_map<std::string, wpi::Sendable*> sendableChildren;
525 frc::Timer periodicProfiler;
526 frc::Timer loggingPeriodTimer;
527 units::millisecond_t loggingPeriod{0_ms};
528 static frc::Timer loggingProfiler;
529};
530
531template <>
532inline decltype(Loggable::subscribers)::iterator Loggable::addSubscriber(std::string_view field, const std::vector<bool>& defaultValue) {
533 return subscribers
534 .emplace(field, std::make_unique<nt::BooleanArraySubscriber>(
535 table->GetBooleanArrayTopic(field).Subscribe(std::vector<int>{defaultValue.begin(), defaultValue.end()})))
536 .first;
537}
538
539template <>
540inline void Loggable::WriteLogImpl(nt::Publisher* pub, const std::vector<bool>& data) {
541 static_cast<nt::BooleanArrayPublisher*>(pub)->Set(std::vector<int>{data.begin(), data.end()});
542}
543
544template <>
545inline std::vector<bool> Loggable::ReadLogImpl(nt::Subscriber* sub) {
546 auto data = static_cast<nt::BooleanArraySubscriber*>(sub)->Get();
547 return {data.begin(), data.end()};
548}
549
550} // namespace valor
Base helper for publishing and subscribing values to NetworkTables.
Definition Loggable.h:204
T ReadLog(std::string_view field, const T &defaultValue={})
Read a value from NetworkTables for the given field.
Definition Loggable.h:343
T WriteLog(std::string_view field, const T &data)
Publish a value to NetworkTables under the given field.
Definition Loggable.h:313
virtual void OnLoggingStart()
Hook invoked when logging is started for this object.
Definition Loggable.h:287
void LogChild(std::string_view name, Loggable *child)
Register a child Loggable under a named subtree.
void setLoggingPeriod(units::millisecond_t period)
Set the minimum period between LoggablePeriodic() invocations.
Definition Loggable.h:251
void LogChild(std::string_view name, wpi::Sendable *child)
Register a child Sendable under a named subtree.
virtual void LoggablePeriodic()
Periodic callback for logging updates.
Definition Loggable.h:296
Loggable()
Default construct an uninitialized Loggable.
virtual ~Loggable()=default
Virtual destructor.
Loggable(std::string_view name)
Construct a Loggable that registers a top-level table name.
Definition Loggable.h:49
Definition Loggable.h:182
Definition Loggable.h:176