-
Notifications
You must be signed in to change notification settings - Fork 634
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
[telemetry] Add new Telemetry implementation #7773
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
* @param typeString type string | ||
* @return False if type mismatch. | ||
*/ | ||
public boolean setType(String typeString) { |
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.
Would it make sense for the type setting and checking to be handled by the telemetry library instead of the telemetry implementation?
I'm thinking of doing the following:
@FunctionalInterface
public interface TelemetryLoggable {
void updateTelemetry(TelemetryTable table);
default String getType() {
return null;
}
}
public final class TelemetryTable {
// No need for setType() anymore
public <T> void log(String name, T value) {
if (value instanceof TelemetryLoggable v) {
TelemetryTable table = getTable(name);
String type = v.getType();
if (type != null) {
if (m_type != null) {
if (!m_type.equals(type)) {
return;
}
} else {
m_type = type;
log(".type", type);
}
}
v.updateTelemetry(table);
} else {
// ...
}
}
}
Unfortunately this makes it hard to implement the equivalent of publishConst()
- Maybe the type set could happen after the call to updateTelemetry()
?
It also means that you can't specify the type string of a lambda being logged, though I don't know how major of a use case that is.
It makes sense if these things mean we can't really do something like this- I was concerned about having more that implementations need to get right (especially since we don't have [[nodiscard]]
in Java) and that we would need to update, but it isn't the biggest issue.
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.
Yeah, I considered that approach but discarded it because of the publishConst() equivalent problem, although you're right that it could be addressed by setting the type after the call to updateTelemetry (although that may have its own issues with derived classes and super calls?). Definitely worth thinking about, it's still relatively easy to change at this point. The other downside of this approach is it's more expensive in C++ unless we make the interface function return a string_view instead of a string (which is admittedly probably okay, as it's very unlikely to be a dynamic type).
@@ -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)
Imported from https://github.com/nielsbasjes/prefixmap with the following changes: - Removed serialization - Removed case-insensitive matching - Formatting to placate spotbugs and spotless
90b409e
to
189ea22
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. Thelog(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).
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.TODO: