Class Frame<T>

java.lang.Object
com.reduxrobotics.frames.Frame<T>
Direct Known Subclasses:
ByteArrayFrame, ColorFrame, DoubleFrame, LongFrame, QuaternionFrame, ShortArrayFrame, Vec3Frame

public abstract class Frame<T> extends Object
Class representing periodic timestamped data received from CAN or other sources.

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:

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

    Nested Classes
    Modifier and Type
    Class
    Description
    static interface 
    Functional interface for Frame callbacks.
  • Constructor Summary

    Constructors
    Constructor
    Description
    Frame(double timestamp)
    Constructs a new Frame object.
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    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
    Returns the value of the data frame.
    abstract boolean
    Returns if this frame has data.
    static double
    Returns the max timestamp from a tuple of FrameData objects.
    boolean
    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.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • 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

      public boolean addCallback(Frame.FrameCallback<T> callback)
      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

      public boolean removeCallback(Frame.FrameCallback<T> callback)
      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

      public FrameData<T> getFrameData()
      Returns an immutable FrameData<T> class containing both value and timestamp. This bypasses the race condition possible via calling getValue() followed by getTimestamp() individually.
      Returns:
      frame data
    • getValue

      public abstract T 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

      public static FrameData<?>[] waitForFrames(double timeout, Frame<?>... frames)
      Waits for all Frames to have transmitted a value. Either returns an array of FrameData 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 up
      frames - 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

      public static double maxTimestamp(FrameData<?>[] data)
      Returns the max timestamp from a tuple of FrameData objects. Most useful for getting the "latest" CAN timestamp from a result of waitForFrames(double, com.reduxrobotics.frames.Frame<?>...).
      Parameters:
      data - frame data array from waitForFrames(double, com.reduxrobotics.frames.Frame<?>...)
      Returns:
      the maximum timestamp