mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-22 01:11:42 +00:00
[epilogue] Support logging of protobuf-serializable types (#8229)
For parity with struct-serializable types. Change struct serialization to only apply to types with a public static final <type> struct field, instead of relying only on the marker interface (which is not always followed). Doing this allows fallthrough to the protobuf handler for types with dynamic structs but static protobuf serializers.
This commit is contained in:
@@ -6,8 +6,10 @@ package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.units.Measure;
|
||||
import edu.wpi.first.units.Unit;
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.Collection;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/** A backend is a generic interface for Epilogue to log discrete data points. */
|
||||
public interface EpilogueBackend {
|
||||
@@ -193,6 +195,17 @@ public interface EpilogueBackend {
|
||||
log(identifier, array, struct);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a protobuf-serializable object.
|
||||
*
|
||||
* @param identifier the identifier of the data point
|
||||
* @param value the value of the data point
|
||||
* @param proto the protobuf to use to serialize the data
|
||||
* @param <P> the protobuf-serializable type
|
||||
* @param <M> the protobuf message type
|
||||
*/
|
||||
<P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto);
|
||||
|
||||
/**
|
||||
* Logs a measurement's value in terms of its base unit.
|
||||
*
|
||||
|
||||
@@ -16,17 +16,20 @@ import edu.wpi.first.util.datalog.FloatArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.FloatLogEntry;
|
||||
import edu.wpi.first.util.datalog.IntegerArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.IntegerLogEntry;
|
||||
import edu.wpi.first.util.datalog.ProtobufLogEntry;
|
||||
import edu.wpi.first.util.datalog.RawLogEntry;
|
||||
import edu.wpi.first.util.datalog.StringArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.StringLogEntry;
|
||||
import edu.wpi.first.util.datalog.StructArrayLogEntry;
|
||||
import edu.wpi.first.util.datalog.StructLogEntry;
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/** A backend implementation that saves information to a WPILib {@link DataLog} file on disk. */
|
||||
public class FileBackend implements EpilogueBackend {
|
||||
@@ -34,6 +37,7 @@ public class FileBackend implements EpilogueBackend {
|
||||
private final Map<String, DataLogEntry> m_entries = new HashMap<>();
|
||||
private final Map<String, NestedBackend> m_subLoggers = new HashMap<>();
|
||||
private final Set<Struct<?>> m_seenSchemas = new HashSet<>();
|
||||
private final Set<Protobuf<?, ?>> m_seenProtos = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Creates a new file-based backend.
|
||||
@@ -166,4 +170,19 @@ public class FileBackend implements EpilogueBackend {
|
||||
|
||||
((StructArrayLogEntry<S>) m_entries.get(identifier)).append(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
// DataLog.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenProtos.add(proto)) {
|
||||
m_dataLog.addSchema(proto);
|
||||
}
|
||||
|
||||
if (!m_entries.containsKey(identifier)) {
|
||||
m_entries.put(identifier, ProtobufLogEntry.create(m_dataLog, identifier, proto));
|
||||
}
|
||||
|
||||
((ProtobufLogEntry<P>) m_entries.get(identifier)).append(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend implementation that only logs data when it changes. Useful for keeping bandwidth and
|
||||
@@ -243,4 +245,17 @@ public class LazyBackend implements EpilogueBackend {
|
||||
m_previousValues.put(identifier, value.clone());
|
||||
m_backend.log(identifier, value, struct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
var previous = m_previousValues.get(identifier);
|
||||
|
||||
if (Objects.equals(previous, value)) {
|
||||
// no change
|
||||
return;
|
||||
}
|
||||
|
||||
m_previousValues.put(identifier, value);
|
||||
m_backend.log(identifier, value, proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend implementation that delegates to other backends. Helpful for simultaneous logging to
|
||||
@@ -137,4 +139,11 @@ public class MultiBackend implements EpilogueBackend {
|
||||
backend.log(identifier, value, struct);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
for (EpilogueBackend backend : m_backends) {
|
||||
backend.log(identifier, value, proto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,18 +13,21 @@ import edu.wpi.first.networktables.FloatPublisher;
|
||||
import edu.wpi.first.networktables.IntegerArrayPublisher;
|
||||
import edu.wpi.first.networktables.IntegerPublisher;
|
||||
import edu.wpi.first.networktables.NetworkTableInstance;
|
||||
import edu.wpi.first.networktables.ProtobufPublisher;
|
||||
import edu.wpi.first.networktables.Publisher;
|
||||
import edu.wpi.first.networktables.RawPublisher;
|
||||
import edu.wpi.first.networktables.StringArrayPublisher;
|
||||
import edu.wpi.first.networktables.StringPublisher;
|
||||
import edu.wpi.first.networktables.StructArrayPublisher;
|
||||
import edu.wpi.first.networktables.StructPublisher;
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend implementation that sends data over network tables. Be careful when using this, since
|
||||
@@ -36,6 +39,7 @@ public class NTEpilogueBackend implements EpilogueBackend {
|
||||
private final Map<String, Publisher> m_publishers = new HashMap<>();
|
||||
private final Map<String, NestedBackend> m_nestedBackends = new HashMap<>();
|
||||
private final Set<Struct<?>> m_seenSchemas = new HashSet<>();
|
||||
private final Set<Protobuf<?, ?>> m_seenProtos = new HashSet<>();
|
||||
private final Function<String, IntegerPublisher> m_createIntPublisher;
|
||||
private final Function<String, FloatPublisher> m_createFloatPublisher;
|
||||
private final Function<String, DoublePublisher> m_createDoublePublisher;
|
||||
@@ -198,4 +202,21 @@ public class NTEpilogueBackend implements EpilogueBackend {
|
||||
publisher.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
// NetworkTableInstance.addSchema has checks that we're able to skip, avoiding allocations
|
||||
if (m_seenProtos.add(proto)) {
|
||||
m_nt.addSchema(proto);
|
||||
}
|
||||
|
||||
if (m_publishers.containsKey(identifier)) {
|
||||
((ProtobufPublisher<P>) m_publishers.get(identifier)).set(value);
|
||||
} else {
|
||||
ProtobufPublisher<P> publisher = m_nt.getProtobufTopic(identifier, proto).publish();
|
||||
m_publishers.put(identifier, publisher);
|
||||
publisher.set(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/**
|
||||
* A backend that logs to an underlying backend, prepending all logged data with a specific prefix.
|
||||
@@ -147,4 +149,9 @@ public class NestedBackend implements EpilogueBackend {
|
||||
public <S> void log(String identifier, S[] value, Struct<S> struct) {
|
||||
m_impl.log(withPrefix(identifier), value, struct);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(String identifier, P value, Protobuf<P, M> proto) {
|
||||
m_impl.log(m_prefix + identifier, value, proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
package edu.wpi.first.epilogue.logging;
|
||||
|
||||
import edu.wpi.first.util.protobuf.Protobuf;
|
||||
import edu.wpi.first.util.struct.Struct;
|
||||
import us.hebi.quickbuf.ProtoMessage;
|
||||
|
||||
/** Null backend implementation that logs nothing. */
|
||||
public class NullBackend implements EpilogueBackend {
|
||||
@@ -62,4 +64,8 @@ public class NullBackend implements EpilogueBackend {
|
||||
|
||||
@Override
|
||||
public <S> void log(String identifier, S[] value, Struct<S> struct) {}
|
||||
|
||||
@Override
|
||||
public <P, M extends ProtoMessage<M>> void log(
|
||||
String identifier, P value, Protobuf<P, M> proto) {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user