We were able to accomplish this in C++ by representing the input in a struct:
Code:
typedef struct{
float analog[NUM_ANALOG];
bool digital[NUM_DIGITAL];
} Gamepad;
typedef struct{
Gamepad gamepads[2];
} Input;
Then we wrote the input struct to a file:
Code:
Input input = {0};
int file = open(filename, O_RDWR, S_IWRITE | S_IREAD);
//start of control loop (eg. TeleopPeriodic)
//do stuff with the input for that loop (get input from driver station)
input = GetInputOrSomethingSimilar();
write(file, &input, sizeof(input));
//end loop
close(file);
Now essentially we flushed the data of the input struct to the file once per loop, so the state of the input is recorded in the order it appears. To playback, we just read the file once per loop, moving the read cursor of the file by the size of the input, and gave the data to the functions that required input instead of directly from the controller:
Code:
Input input = {0};
int file = open(filename, O_RDWR, S_IWRITE | S_IREAD);
//start of control loop (eg. AutonomousPeriodic)
read(file, &input, sizeof(input));
//Give the input to whatever controls it
ControlRobotOrSomethingSimilar(&input);
//end loop
close(file);
Thats how we did it, by storing the files on the roborio, and accessing them when we needed it. But wait! You may be wondering why we would
record the input of some buttons if we haven't pressed the buttons? Well the answer is simple; If your loop time is constant (which it is if you are using iterative), then the recording of inactive buttons as well as active buttons preserves the time for which those buttons are inactive or active exactly as the driver had done so the first time.
This feature has helped our team for more than just autonomous. For example: since we store the recording in a buffer (and a file), we can use it as a circular buffer, allowing us to loop over driver input while we PID tune without having to touch the controller.