View on GitHub

highfrequencyrobots

A framework to get lots of data for wifi-based robots or IoT devices.

Codex High Frequency Data Framework

Latest version: 2022.4.20

What it does, and why

Get the Artifact

The latest stable release is available via jitpack.io, and is also available via this repository’s releases tab:

repositories {
     jcenter()
     maven { url "https://jitpack.io" }
}
dependencies {
      implementation 'com.github.flybotix:highfrequencyrobots:2022.4.20'
}

Get Coding with RobotCodex, using a simple CSV logger

First, create an enumeration that describes your data while implementing the CodexOf interface. Both sides of the comms link will need this enumeration at compile time. Since enumerations are lists of static objects, you can also do anything else you want with your enumeration (descriptions, short descriptions, inheritance, etc).

public enum ElevatorState{UP, DOWN}

public enum RobotData {
  pdb0,
  pdb1,
  pdb2,
  pdb3,
  vrm0,
  totalcurrent,
  gyro, 
    
  // This piece of data represents a Boolean flag, true/false.
  @RobotCodex.FlagCodex
  isAutonomous,
    
  // This represents a state machine. The logger will automtically convert this from a number to text in order to make it easier to read. However, if you want to graph this, then do not put this Annotation.  
  @RobotCodex.StateCodex(stateEnum = ElevatorState.class)
  elevatorState;
}

Second, during robot initialization, create a Codex and pass its reference where it’s needed. Try not to create a ‘new’ Codex for the same enumeration, as that may cause Java’s garbage collection to pause the robot.

// During robot init
RobotCodex<RobotData> data = new RobotCodex(RobotData.class);
data.createSimpleBooleanConverter(RobotData.isAutonomous);
data.createSimpleEnumConverter(RobotData.elevatorState, ElevatorState.class);

// Reset the codex at the beginning of each cycle.  This effectively sets each value to 'Double.NaN'.  Fill out data throughout each cycle.
data.reset(); // beginning of the cycle
data.put(RobotData.pdb2, pdp.getCurrentForChannel(2));
data.put(RobotData.gyro, mxp.getGyroRelative());
data.put(RobotData.isAutonomous, false);
data.put(RobotData.elevatorState, ElevatorState.DOWN);

// Use the data throughout the robot cycle after it's gathered
double degrees = data.get(RobotData.gyro);
ElevatorState state = data.get(RobotData.elvatorState, ElevatorState.class);
boolean isAutonomous = data.isSet(RobotData.isAutonomous);

// Log this to a file
String csvline = data.toFormattedCSV();

The Codex: an enumerated array

(WIP) This project is based upon two simple principles:

  1. A piece of data in an array actually has two pieces of information: the data value, and the position of the data in the array. If a piece of data does not exist at a particular position in an array, it is null (or Double.NaN) - which also tells us something about that data.
  2. We’d rather see compiler errors instead of weird data when the robot is running. Compiler errors are easy to debug and fix. Weird data is not. Therefore a Codex is based upon a static data structure defined at compile time: the Java enumeration.

Enumerations in Java are just that: a compile-time reference of objects in a static array. To get the position of the enumeration in the array, we call enum.ordinal(). To get the static information of the enumeration, we can call name(), toString(), or any other implemented method. Enumerations can even implement interfaces, making them (effectively) static lambdas.

Think of this array as the ‘columns’ of a database or spreadsheet. Every time reset() is called, the codex has effectively become a new row. This is the power of the data structure itself, and it doesn’t require much effort from someone using the library to make it happen. Inserting new columns? No problem! Just add the column to the enumeration, set its value from your code, and any existing logging or display work that was based upon the loop of the enumeration will automatically handle the new column.

The fact that a Codex must represent data of the same type is simultaneously this project’s biggest advantage and disadvantage. It means this project will never represent complex types (e.g. like what JSON can do), but it also means that communicating the data can be extremely efficient (unlike JSON) - and therefore be executed at a higher frequency. In FRC robots (and many IoT scenarios), the data is all of the same type. If the data isn’t sent over a comms protocol, then the type of the codex can be a String, array of arrays, complex POJOs, etc, without worry of data corruption.

Future Work (in order)

  1. Bandwidth monitoring on the client side
  2. Further characterization of NT with a live robot
  3. End-to-end test TCP protocol with a live robot
  4. Heuristics & warnings about limting quantities of fields, compression vs non-compression, etc.

Change Log

2020.2.8

2020.2.15a

2020.2.19

2020.3.9

2022.4.20 (ish)