257 explicit Loggable(std::shared_ptr<nt::NetworkTable> table);
296 void LogChild(std::string_view name, wpi::Sendable* child);
298 static units::millisecond_t GetLoggingTime();
325 template <
typename T>
326 inline T
WriteLog(std::string_view field,
const T& data, LogLevel level = LogLevel::Normal) {
327 loggingProfiler.Start();
329 loggingProfiler.Stop();
332 auto pub = publishers.find(field);
333 if (pub == publishers.end())
334 pub = addPublisher<T>(field);
335 else if (!inPeriodic && frc::Timer::GetTimestamp() - pub->second.second <
336 units::millisecond_t{static_cast<double>(level)} - RATE_LIMIT_TIME_TOLERANCE) {
337 loggingProfiler.Stop();
342 WriteLogImpl(pub->second.first.get(), data);
343 pub->second.second = frc::Timer::GetTimestamp();
344 loggingProfiler.Stop();
361 template <
typename T>
362 inline T
ReadLog(std::string_view field,
const T& defaultValue = {}) {
363 loggingProfiler.Start();
365 loggingProfiler.Stop();
368 auto sub = subscribers.find(field);
369 if (sub == subscribers.end())
370 sub = addSubscriber(field, defaultValue);
373 T value = ReadLogImpl<T>(sub->second.get());
374 if (value == defaultValue && !publishers.contains(field))
376 loggingProfiler.Stop();
380 static constexpr Loggable& GetRoot() {
return rootLoggable; }
401 template <
typename T>
410 static constexpr units::second_t RATE_LIMIT_TIME_TOLERANCE = 3_ms;
422 void EnableLogging();
430 template <
typename T>
431 inline void WriteLogImpl(nt::Publisher* pub,
const T& data) {
432 static_cast<typename TopicType<T>::PublisherType*
>(pub)->Set(data);
435 template <
typename T>
436 requires units::traits::is_unit_t_v<T>
437 inline void WriteLogImpl(nt::Publisher* pub,
const T& data) {
438 WriteLogImpl(pub, data.value());
441 template <
typename T>
442 requires detail::is_unit_vector<T>
443 void WriteLogImpl(nt::Publisher* pub,
const T& data) {
444 std::vector<double> castedData;
445 castedData.reserve(data.size());
446 std::transform(data.begin(), data.end(), std::back_inserter(castedData), [](
auto val) { return val.value(); });
447 WriteLogImpl(pub, castedData);
450 template <
typename T>
451 requires std::is_enum_v<T>
452 void WriteLogImpl(nt::Publisher* pub,
const T& data) {
453 WriteLogImpl(pub, magic_enum::enum_name(data));
456 template <
typename T>
457 requires std::is_enum_v<T>
458 void WriteLogImpl(nt::Publisher* pub,
const std::vector<T>& data) {
459 std::vector<std::string> castedData;
460 castedData.reserve(data.size());
462 castedData.emplace_back(magic_enum::enum_name(val));
463 WriteLogImpl(pub, castedData);
471 template <
typename T>
472 inline T ReadLogImpl(nt::Subscriber* sub) {
473 return static_cast<TopicType<T>::SubscriberType*
>(sub)->Get();
476 template <
typename T>
477 requires units::traits::is_unit_t_v<T>
478 inline T ReadLogImpl(nt::Subscriber* sub) {
479 return T{ReadLogImpl<double>(sub)};
482 template <
typename T>
483 requires detail::is_unit_vector<T>
484 T ReadLogImpl(nt::Subscriber* sub) {
485 auto rawData = ReadLogImpl<std::vector<double>>(sub);
487 castedData.reserve(rawData.size());
488 for (
double data : rawData)
489 castedData.emplace_back(data);
493 template <
typename T>
494 requires std::is_enum_v<T>
495 inline T ReadLogImpl(nt::Subscriber* sub) {
496 return magic_enum::enum_cast<T>(ReadLogImpl<std::string_view>(sub)).value_or(T{});
504 template <
typename T>
505 inline decltype(publishers)::iterator addPublisher(std::string_view field) {
509 std::make_unique<typename TopicType<T>::PublisherType>((table.get()->*detail::nt_traits<T>::func)(field).Publish()), 0_s)
513 template <
typename T>
514 requires units::traits::is_unit_t_v<T>
515 decltype(publishers)::iterator addPublisher(std::string_view field) {
516 std::string unitValue{detail::unit_string<T>};
517 if (unitValue.empty())
518 unitValue = T{}.abbreviation();
521 .try_emplace(std::string{field},
522 std::make_unique<nt::DoublePublisher>(table->GetDoubleTopic(field).PublishEx(
"double", {{
"unit", unitValue}})),
533 template <
typename T>
534 inline decltype(subscribers)::iterator addSubscriber(std::string_view field,
const T& defaultValue) {
536 .emplace(field, std::make_unique<
typename TopicType<T>::SubscriberType>(
541 template <
typename T>
542 requires units::traits::is_unit_t_v<T>
543 inline decltype(subscribers)::iterator addSubscriber(std::string_view field,
const T& defaultValue) {
544 return addSubscriber(field, defaultValue.value());
547 template <
typename T>
548 requires detail::is_unit_vector<T>
549 decltype(subscribers)::iterator addSubscriber(std::string_view field,
const T& defaultValue) {
550 thread_local std::vector<double> defaultData;
552 defaultData.reserve(defaultValue.size());
553 std::transform(defaultValue.begin(), defaultValue.end(), std::back_inserter(defaultData), [](
auto val) { return val.value(); });
554 return addSubscriber(field, defaultData);
557 template <
typename T>
558 requires std::is_enum_v<T>
559 decltype(subscribers)::iterator addSubscriber(std::string_view field,
const T& defaultValue) {
560 return addSubscriber(field, magic_enum::enum_name(defaultValue));
573 template <
typename T>
574 void checkTopicType(std::string_view topicType, std::string_view field) {
575 if (topicType.empty())
578 "Current type of '{}' doesn't match original topic type (received '{}', expected '{}')", field,
582 std::shared_ptr<nt::NetworkTable> table;
583 std::unordered_map<std::string, Loggable*> loggableChildren;
584 std::unordered_map<std::string, wpi::Sendable*> sendableChildren;
585 frc::Timer periodicProfiler;
586 bool inPeriodic =
false;
587 LogLevel periodicLevel = LogLevel::Normal;
588 units::second_t lastPeriodicTime = 0_s;
589 static frc::Timer loggingProfiler;