YAMS
Yet Another Mechanism System
Loading...
Searching...
No Matches
SmartMotorControllerTelemetry.hpp
Go to the documentation of this file.
1// Copyright (c) 2026 Yet Another Software Suite
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
4#pragma once
5
6#include <memory>
7#include <optional>
8#include <string>
9#include <unordered_map>
10#include <utility>
11#include <vector>
12#include <wpi/datalog/DataLog.hpp>
13#include <wpi/nt/BooleanTopic.hpp>
14#include <wpi/nt/DoubleTopic.hpp>
15#include <wpi/nt/NetworkTable.hpp>
16#include <wpi/system/DataLogManager.hpp>
17#include <wpi/util/json.hpp>
18
19// DoubleTelemetry<F>::TransformUnit() calls SmartMotorControllerConfig methods directly in its
20// (header-only, template) body; since SmartMotorControllerConfig is not a dependent type there,
21// two-phase lookup requires it to be complete at this point, not just forward-declared.
23
24namespace yams::motorcontrollers {
25class SmartMotorController;
26} // namespace yams::motorcontrollers
27
28namespace yams::telemetry {
29
30// ---- Field enums ------------------------------------------------------------
31
82
98
99// ---- UnsupportedTelemetryFields ---------------------------------------------
100
108 std::optional<std::vector<BooleanTelemetryField>> boolFields;
109 std::optional<std::vector<DoubleTelemetryField>> doubleFields;
110};
111
112// ---- DoubleTelemetry --------------------------------------------------------
113
127template <typename F>
129 public:
139 DoubleTelemetry(std::string key, double defaultVal, F field, bool tunable, std::string unit)
140 : m_field{field},
141 m_key{std::move(key)},
142 m_tunable{tunable},
143 m_unit{std::move(unit)},
144 m_defaultValue{defaultVal},
145 m_cachedValue{defaultVal} {}
146
152 void SetDefaultValue(double value) { m_cachedValue = m_defaultValue = value; }
153
160 void SetupNetworkTables(std::shared_ptr<wpi::nt::NetworkTable> dataTable,
161 std::shared_ptr<wpi::nt::NetworkTable> tuningTable) {
162 m_dataTable = dataTable;
163 m_tuningTable = tuningTable;
164 if (!m_enabled) return;
165
166 if (tuningTable && m_tunable) {
167 auto topic = tuningTable->GetDoubleTopic(m_key);
168 if (m_unit != "none") {
169 topic.SetProperties(wpi::util::json{{"units", m_unit}});
170 }
171 m_subPublisher = topic.Publish();
172 m_subPublisher->SetDefault(m_defaultValue);
173 m_subscriber = topic.Subscribe(m_defaultValue);
174 } else if (dataTable) {
175 auto topic = dataTable->GetDoubleTopic(m_key);
176 if (m_unit != "none") {
177 topic.SetProperties(wpi::util::json{{"units", m_unit}});
178 }
179 m_publisher = topic.Publish();
180 m_publisher->SetDefault(m_defaultValue);
181 }
182 }
183
189 void SetupNetworkTable(std::shared_ptr<wpi::nt::NetworkTable> dataTable) {
190 SetupNetworkTables(dataTable, nullptr);
191 }
192
198 void SetupDataLog(const std::string& prefix) {
199 if (m_tunable) return;
200 std::string path = prefix;
201 if (!path.empty() && path.back() != '/') path += '/';
202 path += m_unit + '/' + m_key;
203 m_dataLogEntry = wpi::log::DoubleLogEntry{wpi::DataLogManager::GetLog(), path};
204 }
205
213 bool linear = cfg.GetLinearClosedLoopControllerUse();
214 if (m_unit == "tunable_position") {
215 m_unit = linear ? "meter" : "degrees";
216 } else if (m_unit == "position") {
217 m_unit = linear ? "meter" : "rotations";
218 } else if (m_unit == "tunable_velocity") {
219 m_unit = linear ? "meter_per_second" : "rotations_per_minute";
220 } else if (m_unit == "velocity") {
221 m_unit = linear ? "meter_per_second" : "rotation_per_second";
222 } else if (m_unit == "tunable_acceleration") {
223 m_unit = linear ? "meter_per_second_per_second" : "rotations_per_minute_per_second";
224 } else if (m_unit == "acceleration") {
225 m_unit = linear ? "meter_per_second_per_second" : "rotation_per_second_per_second";
226 }
227 }
228
236 void ForceSet(double value) {
237 if (m_subPublisher) {
238 m_subPublisher->Set(value);
239 } else if (m_publisher) {
240 m_publisher->Set(value);
241 }
242 }
243
250 bool Set(double value) {
251 if (!m_enabled) return false;
252 if (m_dataLogEntry) {
253 m_dataLogEntry->Append(value);
254 }
255 if (m_subscriber) {
256 double tuningValue = m_subscriber->Get(m_defaultValue);
257 if (tuningValue != value) return false;
258 }
259 if (m_publisher) {
260 m_publisher->Set(value);
261 }
262 return true;
263 }
264
270 double Get() const {
271 if (!m_enabled) return m_defaultValue;
272 if (m_subscriber) {
273 return m_subscriber->Get(m_defaultValue);
274 }
275 throw std::runtime_error("Tuning table not configured for " + m_key + "!");
276 }
277
283 bool IsTunable() {
284 if (m_subscriber && m_tunable && m_enabled) {
285 double current = m_subscriber->Get(m_defaultValue);
286 if (current != m_cachedValue) {
287 m_cachedValue = current;
288 return true;
289 }
290 }
291 return false;
292 }
293
295 void Enable() { m_enabled = true; }
297 void Disable() { m_enabled = false; }
303 void Display(bool state) { m_enabled = state; }
305 F GetField() const { return m_field; }
307 bool IsEnabled() const { return m_enabled; }
308
310 void Close() {
311 m_subscriber.reset();
312 m_subPublisher.reset();
313 m_publisher.reset();
314 if (m_dataTable) {
315 m_dataTable->GetEntry(m_key).Unpublish();
316 }
317 if (m_tuningTable) {
318 m_tuningTable->GetEntry(m_key).Unpublish();
319 }
320 }
321
322 private:
323 F m_field;
324 std::string m_key;
325 bool m_tunable;
326 bool m_enabled{false};
327 std::string m_unit;
328 double m_defaultValue;
329 double m_cachedValue;
330
331 std::optional<wpi::nt::DoublePublisher> m_publisher;
332 std::optional<wpi::nt::DoubleSubscriber> m_subscriber;
333 std::optional<wpi::nt::DoublePublisher> m_subPublisher; // tunable: publish + subscribe
334 std::optional<wpi::log::DoubleLogEntry> m_dataLogEntry;
335
336 std::shared_ptr<wpi::nt::NetworkTable> m_tuningTable;
337 std::shared_ptr<wpi::nt::NetworkTable> m_dataTable;
338};
339
340// ---- BooleanTelemetry -------------------------------------------------------
341
352template <typename F>
354 public:
363 BooleanTelemetry(std::string key, bool defaultVal, F field, bool tunable)
364 : m_field{field},
365 m_key{std::move(key)},
366 m_tunable{tunable},
367 m_defaultValue{defaultVal},
368 m_cachedValue{defaultVal} {}
369
375 void SetDefaultValue(bool value) { m_cachedValue = m_defaultValue = value; }
376
383 void SetupNetworkTables(std::shared_ptr<wpi::nt::NetworkTable> dataTable,
384 std::shared_ptr<wpi::nt::NetworkTable> tuningTable) {
385 m_dataTable = dataTable;
386 m_tuningTable = tuningTable;
387 if (!m_enabled) return;
388
389 if (tuningTable && m_tunable) {
390 auto topic = tuningTable->GetBooleanTopic(m_key);
391 m_pubSub = topic.Publish();
392 m_pubSub->SetDefault(m_defaultValue);
393 m_subscriber = topic.Subscribe(m_defaultValue);
394 } else if (dataTable) {
395 auto topic = dataTable->GetBooleanTopic(m_key);
396 m_publisher = topic.Publish();
397 m_publisher->SetDefault(m_defaultValue);
398 }
399 }
400
406 void SetupNetworkTable(std::shared_ptr<wpi::nt::NetworkTable> dataTable) {
407 SetupNetworkTables(dataTable, nullptr);
408 }
409
415 void SetupDataLog(const std::string& prefix) {
416 if (m_tunable) return;
417 std::string path = prefix;
418 if (!path.empty() && path.back() != '/') path += '/';
419 path += m_key;
420 m_dataLogEntry = wpi::log::BooleanLogEntry{wpi::DataLogManager::GetLog(), path};
421 }
422
429 bool Set(bool value) {
430 if (!m_enabled) return false;
431 if (m_dataLogEntry) {
432 m_dataLogEntry->Append(value);
433 }
434 if (m_subscriber) {
435 bool tuningValue = m_subscriber->Get(m_defaultValue);
436 if (tuningValue != value) return false;
437 }
438 if (m_publisher) {
439 m_publisher->Set(value);
440 }
441 return true;
442 }
443
449 bool Get() const {
450 if (!m_enabled) return m_defaultValue;
451 if (m_subscriber) {
452 return m_subscriber->Get(m_defaultValue);
453 }
454 throw std::runtime_error("Tuning table not configured for " + m_key + "!");
455 }
456
465 void ForceSet(bool value) {
466 if (m_pubSub) {
467 m_pubSub->Set(value);
468 } else if (m_publisher) {
469 m_publisher->Set(value);
470 }
471 }
472
478 bool IsTunable() {
479 if (m_subscriber && m_tunable && m_enabled) {
480 bool current = m_subscriber->Get(m_defaultValue);
481 if (current != m_cachedValue) {
482 m_cachedValue = current;
483 return true;
484 }
485 }
486 return false;
487 }
488
490 void Enable() { m_enabled = true; }
492 void Disable() { m_enabled = false; }
498 void Display(bool state) { m_enabled = state; }
500 F GetField() const { return m_field; }
502 bool IsEnabled() const { return m_enabled; }
503
505 void Close() {
506 m_subscriber.reset();
507 m_pubSub.reset();
508 m_publisher.reset();
509 if (m_dataTable) {
510 m_dataTable->GetEntry(m_key).Unpublish();
511 }
512 if (m_tuningTable) {
513 m_tuningTable->GetEntry(m_key).Unpublish();
514 }
515 }
516
517 private:
518 F m_field;
519 std::string m_key;
520 bool m_tunable;
521 bool m_enabled{false};
522 bool m_defaultValue;
523 bool m_cachedValue;
524
525 std::optional<wpi::nt::BooleanPublisher> m_publisher;
526 std::optional<wpi::nt::BooleanSubscriber> m_subscriber;
527 std::optional<wpi::nt::BooleanPublisher> m_pubSub; // tunable: publish + subscribe
528 std::optional<wpi::log::BooleanLogEntry> m_dataLogEntry;
529
530 std::shared_ptr<wpi::nt::NetworkTable> m_tuningTable;
531 std::shared_ptr<wpi::nt::NetworkTable> m_dataTable;
532};
533
534// ---- SmartMotorControllerTelemetry ------------------------------------------
535
544 public:
558 std::shared_ptr<wpi::nt::NetworkTable> publishTable,
559 std::shared_ptr<wpi::nt::NetworkTable> tuningTable,
560 std::unordered_map<DoubleTelemetryField, DoubleTelemetry<DoubleTelemetryField>>& doubleFields,
562 boolFields,
563 bool nt4Enabled, std::optional<std::string> dataLogName);
564
571
578
587
589 bool TuningEnabled() const;
590
592 void Close();
593
594 private:
595 std::shared_ptr<wpi::nt::NetworkTable> m_dataTable;
596 std::shared_ptr<wpi::nt::NetworkTable> m_tuningTable;
597
598 std::unordered_map<DoubleTelemetryField, DoubleTelemetry<DoubleTelemetryField>>* m_doubleFields{
599 nullptr};
600 std::unordered_map<BooleanTelemetryField, BooleanTelemetry<BooleanTelemetryField>>* m_boolFields{
601 nullptr};
602};
603
604} // namespace yams::telemetry
Definition SmartMotorControllerConfig.hpp:65
Definition SmartMotorController.hpp:48
Definition SmartMotorControllerTelemetry.hpp:353
void SetDefaultValue(bool value)
Definition SmartMotorControllerTelemetry.hpp:375
void ForceSet(bool value)
Definition SmartMotorControllerTelemetry.hpp:465
bool Get() const
Definition SmartMotorControllerTelemetry.hpp:449
bool Set(bool value)
Definition SmartMotorControllerTelemetry.hpp:429
void SetupDataLog(const std::string &prefix)
Definition SmartMotorControllerTelemetry.hpp:415
void Close()
Definition SmartMotorControllerTelemetry.hpp:505
bool IsEnabled() const
Definition SmartMotorControllerTelemetry.hpp:502
F GetField() const
Definition SmartMotorControllerTelemetry.hpp:500
void Enable()
Definition SmartMotorControllerTelemetry.hpp:490
void SetupNetworkTables(std::shared_ptr< wpi::nt::NetworkTable > dataTable, std::shared_ptr< wpi::nt::NetworkTable > tuningTable)
Definition SmartMotorControllerTelemetry.hpp:383
void Disable()
Definition SmartMotorControllerTelemetry.hpp:492
void Display(bool state)
Definition SmartMotorControllerTelemetry.hpp:498
bool IsTunable()
Definition SmartMotorControllerTelemetry.hpp:478
void SetupNetworkTable(std::shared_ptr< wpi::nt::NetworkTable > dataTable)
Definition SmartMotorControllerTelemetry.hpp:406
BooleanTelemetry(std::string key, bool defaultVal, F field, bool tunable)
Definition SmartMotorControllerTelemetry.hpp:363
Definition SmartMotorControllerTelemetry.hpp:128
void SetupDataLog(const std::string &prefix)
Definition SmartMotorControllerTelemetry.hpp:198
double Get() const
Definition SmartMotorControllerTelemetry.hpp:270
bool IsTunable()
Definition SmartMotorControllerTelemetry.hpp:283
F GetField() const
Definition SmartMotorControllerTelemetry.hpp:305
void TransformUnit(const motorcontrollers::SmartMotorControllerConfig &cfg)
Definition SmartMotorControllerTelemetry.hpp:212
void Enable()
Definition SmartMotorControllerTelemetry.hpp:295
void SetDefaultValue(double value)
Definition SmartMotorControllerTelemetry.hpp:152
void SetupNetworkTables(std::shared_ptr< wpi::nt::NetworkTable > dataTable, std::shared_ptr< wpi::nt::NetworkTable > tuningTable)
Definition SmartMotorControllerTelemetry.hpp:160
void Disable()
Definition SmartMotorControllerTelemetry.hpp:297
bool Set(double value)
Definition SmartMotorControllerTelemetry.hpp:250
void Display(bool state)
Definition SmartMotorControllerTelemetry.hpp:303
DoubleTelemetry(std::string key, double defaultVal, F field, bool tunable, std::string unit)
Definition SmartMotorControllerTelemetry.hpp:139
void SetupNetworkTable(std::shared_ptr< wpi::nt::NetworkTable > dataTable)
Definition SmartMotorControllerTelemetry.hpp:189
void ForceSet(double value)
Definition SmartMotorControllerTelemetry.hpp:236
bool IsEnabled() const
Definition SmartMotorControllerTelemetry.hpp:307
void Close()
Definition SmartMotorControllerTelemetry.hpp:310
Definition SmartMotorControllerTelemetry.hpp:543
void Publish(motorcontrollers::SmartMotorController &smc)
void SetupTelemetry(motorcontrollers::SmartMotorController &smc, std::shared_ptr< wpi::nt::NetworkTable > publishTable, std::shared_ptr< wpi::nt::NetworkTable > tuningTable, std::unordered_map< DoubleTelemetryField, DoubleTelemetry< DoubleTelemetryField > > &doubleFields, std::unordered_map< BooleanTelemetryField, BooleanTelemetry< BooleanTelemetryField > > &boolFields, bool nt4Enabled, std::optional< std::string > dataLogName)
void StopTuning(motorcontrollers::SmartMotorController &smc)
void ApplyTuningValues(motorcontrollers::SmartMotorController &smc)
Definition SimSensorConfig.hpp:15
Definition SwerveDrive.hpp:864
DoubleTelemetryField
Definition SmartMotorControllerTelemetry.hpp:33
@ kG
Gravity feedforward gain kG (tunable).
@ kI
Integral feedback gain kI (tunable).
@ MechanismUpperLimit
Configured upper angular soft limit (degrees, tunable).
@ MechanismAcceleration
Mechanism acceleration in rotations per second squared.
@ ExternalEncoderVelocity
External encoder's mechanism velocity in rotations per second, only with an external encoder.
@ kA
Acceleration feedforward gain kA (tunable).
@ TunableClosedLoopControllerSlot
Live-tunable closed-loop gain slot selector.
@ kP
Proportional feedback gain kP (tunable).
@ SupplyCurrent
Supply (input) current draw (A).
@ StatorCurrent
Stator (output) current draw (A).
@ OutputVoltage
Voltage currently applied to the motor (V).
@ VelocityMeasurementDelay
How far behind the measured velocity is, when known (s).
@ ExponentialProfileKV
Exponential profile kV constant (tunable).
@ SetpointForce
Feedforward force applied on the velocity setpoint (N).
@ SupplyCurrentLimit
Configured supply current limit (A, tunable).
@ MeasurementVelocity
Linear measurement velocity (m/s).
@ kV
Velocity feedforward gain kV (tunable).
@ kD
Derivative feedback gain kD (tunable).
@ SetpointPosition
Read-only position setpoint rotations or meters.
@ MeasurementUpperLimit
Configured upper linear soft limit (m, tunable).
@ ClosedLoopPeriod
Period the closed-loop controller runs at (s).
@ ExternalEncoderPosition
External encoder's mechanism position in rotations, only with an external encoder.
@ TunableSetpointPosition
Live-tunable position setpoint degrees or meters.
@ MechanismPosition
Mechanism position in rotations.
@ RotorVelocity
Raw rotor velocity in rotations per second.
@ MeasurementPosition
Linear measurement position (m).
@ MeasurementAcceleration
Linear measurement acceleration (m/s²).
@ OpenloopRampRate
Open-loop duty-cycle ramp rate (s, tunable).
@ TrapezoidalProfileMaxVelocity
Trapezoidal profile max velocity RPM or m/s (tunable).
@ ExponentialProfileKA
Exponential profile kA constant (tunable).
@ TunableSetpointVelocity
Live-tunable velocity setpoint RPM or m/s.
@ MeasurementLowerLimit
Configured lower linear soft limit (m, tunable).
@ SetpointVelocity
Read-only velocity setpoint RPS or m/s.
@ ClosedloopRampRate
Closed-loop duty-cycle ramp rate (s, tunable).
@ kS
Static friction feedforward gain kS (tunable).
@ StatorCurrentLimit
Configured stator current limit (A, tunable).
@ ExponentialProfileMaxInput
Exponential profile maximum input voltage (tunable).
@ ActiveClosedLoopControllerSlot
Currently active closed-loop gain slot (read-only).
@ MotorTemperature
Motor controller temperature (°F).
@ MechanismLowerLimit
Configured lower angular soft limit (degrees, tunable).
@ RotorPosition
Raw rotor position in rotations.
@ TrapezoidalProfileMaxJerk
Trapezoidal profile max jerk RPM/s² (tunable).
@ MechanismVelocity
Mechanism velocity in rotations per second.
BooleanTelemetryField
Definition SmartMotorControllerTelemetry.hpp:84
@ EncoderInversion
true when the encoder count direction is inverted (tunable).
@ TemperatureLimit
true when the motor temperature has reached the cutoff threshold.
@ SimpleMotorFeedForward
true when a simple motor feedforward model is configured.
@ MotorInversion
true when the motor output direction is inverted (tunable).
@ ElevatorFeedForward
true when an elevator feedforward model is configured.
@ MotionProfile
true when a motion profile (trapezoidal or exponential) is active.
@ VelocityControl
true when a velocity setpoint is active.
@ ArmFeedForward
true when an arm feedforward model is configured.
Definition SmartMotorControllerTelemetry.hpp:107
std::optional< std::vector< DoubleTelemetryField > > doubleFields
Unsupported double fields.
Definition SmartMotorControllerTelemetry.hpp:109
std::optional< std::vector< BooleanTelemetryField > > boolFields
Unsupported boolean fields.
Definition SmartMotorControllerTelemetry.hpp:108