-
Notifications
You must be signed in to change notification settings - Fork 658
[telemetry] Add new Telemetry implementation #7773
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 2027
Are you sure you want to change the base?
Conversation
wpiutil/src/main/java/edu/wpi/first/util/telemetry/TelemetryTable.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see any locking to make sure this works from multiple threads.
I also don't understand why there are a lot of individual functions with things like "log" naming.
What do you mean by this? Are you asking why everything is called |
I wasn't clear here. This is in reference to TelemetryBackEnd.java |
I haven't finished implementing the backends, but basically all the backends have type safety and type-specific functionality. So writing a double to a datalog file or NetworkTables is different than writing a string (or an integer). If you're asking why they are uniquely named instead of overloaded, overloading is bad practice with virtual functions (particularly in C++). A bunch of overloaded "log" functions on the user-facing side makes sense for ease of use, but is a potential footgun on the backend. |
It should be thread safe as written already. The backends will have locking/atomics included as needed (there are some synchronized blocks there already in the start of the DataLogSendableBackend implementation). We use ConcurrentMap etc to avoid explicit locking on the frontend (e.g. TelemetryTable caching uses ConcurrentMap), at least in Java--in C++, we'll use explicit mutexes. |
6eb5395
to
405791b
Compare
2fba581
to
aaff88f
Compare
telemetry/src/main/java/edu/wpi/first/telemetry/TelemetryLoggable.java
Outdated
Show resolved
Hide resolved
wpilibj/src/main/java/edu/wpi/first/wpilibj/smartdashboard/MechanismObject2d.java
Outdated
Show resolved
Hide resolved
@@ -158,3 +158,20 @@ class ADXL345_I2C : public nt::NTSendable, | |||
}; | |||
|
|||
} // namespace frc | |||
|
|||
template <> | |||
struct wpi::Struct<frc::ADXL345_I2C::AllAxes> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this go into a separate file to match the other struct definitions?
wpilibc/src/main/native/include/frc/smartdashboard/MechanismRoot2d.h
Outdated
Show resolved
Hide resolved
|
||
TEST(Mechanism2dTest, Canvas) { | ||
void TearDown() override { wpi::TelemetryRegistry::Reset(); } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
template <typename T> | |
void ExpectLastValue(std::string_view path, const T& expected) { | |
auto value = mock->GetLastValue<T>(path); | |
ASSERT_TRUE(value); | |
EXPECT_EQ(expected, *value); | |
} | |
This should work and would reduce boilerplate, though it doesn't work for the types that gtest won't nicely format if the assertion fails. (If it doesn't recognize the type it outputs the hexademical of the raw memory contents- I remember encountering that for failing quaternion equality checks)
90b409e
to
189ea22
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A log(name, measure)
overload which includes unit metadata also be nice
Unit support will be implemented in a different way--the registry supports registering global type-specific handlers so we don't have to have telemetry depend on every library we want to provide overloads for. |
telemetry/src/main/java/edu/wpi/first/telemetry/TelemetryLoggable.java
Outdated
Show resolved
Hide resolved
wpilibj/src/main/java/edu/wpi/first/wpilibj/motorcontrol/PWMMotorController.java
Outdated
Show resolved
Hide resolved
Units support has been added. In C++, it's integrated into units/base.h: inline void LogEntry(wpi::TelemetryTable& table, std::string_view name, const nameSingular ## _t& value)
{
table.SetProperty(name, "unit", " "#nameSingular);
table.Log(name, value.value());
} This will make it automatically work for all use cases. In Java, it needs to be registered as a type handler, which is done in wpilibj RobotBase: TelemetryRegistry.registerTypeEntry(Measure.class, (v, entry) -> {
entry.setProperty("unit", v.unit().name());
entry.logDouble(v.magnitude());
}); For Java, this means that user unit tests which want to log unit values in this way will need to similarly register the type entry. Unit tests will still compile and run without doing so, they'll just log a string value (via Currently, the callback is also a little different between C++ and Java; C++ gets the table and name, while Java gets the Entry. The reason for this is that we want to avoid referencing the virtual functions in Entry in C++, but it also adds a bit more flexibility and will let us unify the entry and table handler callbacks, so I'm considering changing the Java callback to also take the Table and name instead of the entry. |
Before I forget, I think for mp-units we can use Hard to say whether this or mp-units will happen first, but I just wanted to note this down before I forgot. |
@@ -122,6 +122,10 @@ | |||
<Bug pattern="UC_USELESS_VOID_METHOD" /> | |||
<Class name="edu.wpi.first.wpilibj.templates.timesliceskeleton.Robot" /> | |||
</Match> | |||
<Match> | |||
<Bug pattern="UCF_USELESS_CONTROL_FLOW" /> | |||
<Class name="edu.wpi.first.wpilibj.examples.rapidreactcommandbot.subsystems.IntakeLogger" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class doesn't seem to exist?
|
||
/** Global registry for telemetry handlers (type handlers and telemetry backends). */ | ||
public final class TelemetryRegistry { | ||
/** Handler for logging objects of specific type. Typically only one handler is specified, the */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI that this is cut off.
* | ||
* @return value | ||
*/ | ||
T get(); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
Imported from https://github.com/nielsbasjes/prefixmap with the following changes: - Removed serialization - Removed case-insensitive matching - Formatting to placate spotbugs and spotless
96b47d1
to
2ff7c2d
Compare
Significantly simplified version of #6453. The design approach here is to make Telemetry purely imperative/immediate and write only. Read capabilities would be added as a separate Tunable implementation.
Telemetry
is a utility class with only static functions to allow simple use such asTelemetry.log("name", value);
(alaSystem.out.println()
), and is intended as the primary user-facing class. Nested (structured) telemetry is available viaTelemetryTable
, instances of which can be gotten by callingTelemetry.getTable()
(orTelemetryTable.getTable()
for further nesting).The
Sendable
concept equivalent isTelemetryLoggable
(not a great name, but naming is hard). Unlike the currentSendable
/SmartDashboard.putData()
, this is designed to be immediate, not callback-based. TheupdateTelemetry(TelemetryTable)
function of aTelemetryLoggable
should immediately log the desired data to the providedTelemetryTable
and not store the table for later use.While we aim to fast-path most types through specific overloads, we do have a generic
Object
-taking overload to avoid potential overload ambiguity (particularly betweenStructSerializable
andProtobufSerializable
, where many types implement both) and provide a fallback toString path. There's a type registry mechanism to register specific type handlers; the intent here is to use this for things like Unit types, where you might want to both set a property (indicating the unit type) and provide the value as a double.Backends can be configured at any level; the most specific backend is used based on longest prefix match of the telemetry path (this allows for e.g. setting up NT logging at the top level, but making some tables DataLog-only). Backends are provided for both NetworkTables and DataLog, and there's also a discard backend (that throws away any logged data) as well as a mock backend (for unit testing).
The initial backend implementation takes the type of the first
log()
call as the "forever" type for that particular name. Trying to later log to the same name with a different type is ignored and emits a warning. Changing types dynamically both significantly increases implementation complexity and will likely result in difficult-to-debug behavior in downstream tooling; it's hard to see the user benefits to supporting this.Dependency wise, the telemetry library only depends on wpiutil.
TODO:
Fixes #5513
Closes #5912
Closes #5481
Closes #5413