Class Frame<T>
- Direct Known Subclasses:
ByteArrayFrame
,ColorFrame
,DoubleFrame
,LongFrame
,QuaternionFrame
,ShortArrayFrame
,Vec3Frame
For applications like latency compensation, we often need both sensor/device data and a timestamp
of when the data was received.
Frames provide this by holding the most recently received raw data
and the timestamps they were received at and allowing retrieval of both data and timestamps
in one FrameData
object via getFrameData()
, avoiding race conditions
involving reading data and timestamp separately.
Additionally, they allow for:
- synchronous reads through
waitForFrames(double, Frame...)
by notifying when new data has been received. - asynchronous callbacks through
addCallback(FrameCallback)
that giveFrameData
directly to callback functions.
In Java, non-primitive data types cannot be easily passed by value -- instead the language
prefers immutable record type objects for compound data structures.
However, in an FRC application, this would mean a ton of objects getting thrown to the garbage
collector from every time a Frame gets updated rendering the previous value object stale.
To get around this, implementing subclasses such as DoubleFrame
, LongFrame
, and
ByteArrayFrame
on update copy the new value to a primitive field (or in the case of
ByteArrayFrame, an array of primitives).
As primitives in Java have value semantics, this avoids instantiation of new objects on every
update, and the final object value is only instantiated on a getValue()
call via a
constructor-supplied raw-to-finished data conversion function.
Often, the conversion function is simply the constructor of the final object.
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic interface
Functional interface for Frame callbacks. -
Constructor Summary
-
Method Summary
Modifier and TypeMethodDescriptionboolean
addCallback
(Frame.FrameCallback<T> callback) Add a callback that will be run whenever this Frame gets updated.Returns an immutable FrameData<T> class containing both value and timestamp.double
Gets the timestamp in seconds of when this value was updated.abstract T
getValue()
Returns the value of the data frame.abstract boolean
hasData()
Returns if this frame has data.static double
maxTimestamp
(FrameData<?>[] data) Returns the max timestamp from a tuple ofFrameData
objects.boolean
removeCallback
(Frame.FrameCallback<T> callback) Remove a registered callback by the handle.protected void
update
(double timestamp) Updates the Frame's value, notifying any listeners of new data.static FrameData<?>[]
waitForFrames
(double timeout, Frame<?>... frames) Waits for all Frames to have transmitted a value.
-
Constructor Details
-
Frame
public Frame(double timestamp) Constructs a new Frame object.- Parameters:
timestamp
- The initial timestamp at which the value was received in seconds.
-
-
Method Details
-
update
protected void update(double timestamp) Updates the Frame's value, notifying any listeners of new data. Implementing classes should call this function in a synch block in their own update methods.- Parameters:
timestamp
- the new timestamp of the received data, in seconds
-
addCallback
Add a callback that will be run whenever this Frame gets updated. Example application:// Log Canandmag position FrameData. List<FrameData<Double>> data = new ArrayList<>(); Canandmag enc0 = new Canandmag(0); enc0.getPositionFrame().addCallback(frame -> { data.add(frame.getFrameData()); }); // Timestamped data is now streamed into the List.
- Parameters:
callback
- the callback function, taking a FrameData- Returns:
- true on success, false if the callback has already been added.
-
removeCallback
Remove a registered callback by the handle.- Parameters:
callback
- the callback function, taking a FrameData- Returns:
- true on success, false if the callback has already been added.
-
getFrameData
Returns an immutable FrameData<T> class containing both value and timestamp. This bypasses the race condition possible via callinggetValue()
followed bygetTimestamp()
individually.- Returns:
- frame data
-
getValue
Returns the value of the data frame.- Returns:
- the value the data frame holds.
-
hasData
public abstract boolean hasData()Returns if this frame has data.- Returns:
- if this frame's data can be considered valid
-
getTimestamp
public double getTimestamp()Gets the timestamp in seconds of when this value was updated. The time base is relative to the FPGA timestamp.- Returns:
- the timestamp in seconds.
-
waitForFrames
Waits for all Frames to have transmitted a value. Either returns an array ofFrameData
representing the data from corresponding frames passed in (in the order they are passed in) or null if timeout or interrupt is hit.// Keep in mind this code sample will likely cause timing overruns // if on the main thread of your robot code. // some definitions for reference: Canandmag enc = new Canandmag(0); Canandmag enc1 = new Canandmag(1); // in your side thread function: // wait for up to 40 ms for position and velocity to come in from two Canandmags FrameData<?>[] data = Frame.waitForFrames(0.040, enc.getPositionFrame(), enc.getVelocityFrame(), enc1.getPositionFrame()); if (data == null) { System.out.printf("waitForFrames timed out before receiving all data\n"); } else { // blind casts are needed to pull the data out of the array FrameData<Double> posFrame = (FrameData<Double>) data[0]; FrameData<Double> velFrame = (FrameData<Double>) data[1]; FrameData<Double> posFram1 = (FrameData<Double>) data[2]; // fetches the maximum timestamp across all received timestamps (the "latest" value) double latest = Frame.maxTimestamp(data); // prints the received frame value and how far behind the latest received CAN timestamp it was System.out.printf("posFrame: %.3f, %.3f ms\n", posFrame.getValue(), (latest - posFrame.getTimestamp()) * 1000); System.out.printf("velFrame: %.3f, %.3f ms\n", velFrame.getValue(), (latest - velFrame.getTimestamp()) * 1000); System.out.printf("posFram1: %.3f, %.3f ms\n", posFram1.getValue(), (latest - posFram1.getTimestamp()) * 1000); }
- Parameters:
timeout
- maximum seconds to wait for before giving upframes
-Frame
handles to wait on. Position in argument list corresponds to position in the returned FrameData array.- Returns:
- an array of
FrameData
; representing the data from corresponding frames passed in or null if timeout or interrupt is hit.
-
maxTimestamp
Returns the max timestamp from a tuple ofFrameData
objects. Most useful for getting the "latest" CAN timestamp from a result ofwaitForFrames(double, com.reduxrobotics.frames.Frame<?>...)
.- Parameters:
data
- frame data array fromwaitForFrames(double, com.reduxrobotics.frames.Frame<?>...)
- Returns:
- the maximum timestamp
-