Registro de telemetría en robot en registros de datos

De forma predeterminada, no se registra (guarda) ningún dato de telemetría en el robot. La clase «DataLogManager» proporciona un envoltorio conveniente alrededor de la clase «DataLog» una clase de nivel inferior para el registro en el robot de datos de telemetría. Los registros de datos de WPILib son binarios por razones de tamaño y velocidad. En general, las facilidades de registro de datos proporcionadas por WPILib tienen un sobrecosto mínimo para el código del robot, ya que toda la E/S de archivos se realiza en un hilo separado: la operación de registro consiste principalmente en la adquisición de un mutex y la copia de los datos.

Estructura de los registros de datos

Similar a NetworkTables, los registros de datos tienen el concepto de entradas con identificadores de cadena (keys) con un tipo de datos especificado. A diferencia de NetworkTables, el tipo de datos no puede cambiarse después de que se crea la entrada, y las entradas también tienen metadatos: una cadena arbitraria (típicamente JSON) que se puede utilizar para transmitir información adicional sobre la entrada, como la fuente de datos o el esquema de datos. Además, a diferencia de NetworkTables, la operación de registro de datos es unidireccional: la clase DataLog solo puede escribir registros de datos (no admite la lectura de valores escritos) y la clase DataLogReader solo puede leer registros de datos (no admite el cambio de valores en el registro de datos).

Los registros de datos consisten en una serie de registros con marca de tiempo. Los registros de control permiten iniciar, finalizar o cambiar los metadatos de las entradas, y los registros de datos registran cambios en los valores de los datos. Las marcas de tiempo se almacenan en microsegundos enteros; cuando se ejecuta en el RoboRIO, se utiliza la marca de tiempo del FPGA (la misma marca de tiempo devuelta por Timer.getFPGATimestamp()).

Nota

For more information on the details of the data log file format, see the WPILib Data Log File Format Specification.

Registro de datos estándar utilizando DataLogManager

The DataLogManager class (Java, C++, Python) provides a centralized data log that provides automatic data log file management. It automatically cleans up old files when disk space is low and renames the file based either on current date/time or (if available) competition match number. The data file will be saved to a USB flash drive in a folder called logs if one is attached, or to /home/lvuser/logs otherwise.

Nota

USB flash drives need to be formatted as FAT32 to work with the roboRIO. NTFS or exFAT formatted drives will not work.

Log files are initially named FRC_TBD_{random}.wpilog until the DS connects. After the DS connects, the log file is renamed to FRC_yyyyMMdd_HHmmss.wpilog (where the date/time is UTC). If the FMS is connected and provides a match number, the log file is renamed to FRC_yyyyMMdd_HHmmss_{event}_{match}.wpilog.

Al iniciar, se eliminarán todos los archivos de registro existentes en los que no se haya conectado un DS. Si hay menos de 50 MB de espacio libre en el almacenamiento objetivo, se eliminarán los archivos de registro FRC_ (del más antiguo al más nuevo) hasta que haya 50 MB libres O queden 10 archivos restantes.

El uso más básico de DataLogManager solo requiere una línea de código (normalmente esto se llamaría desde robotInit). Esto registrará todos los cambios de NetworkTables en el registro de datos.

import edu.wpi.first.wpilibj.DataLogManager;

// Starts recording to data log
DataLogManager.start();
#include "frc/DataLogManager.h"

// Starts recording to data log
frc::DataLogManager::Start();
from wpilib import DataLogManager

DataLogManager.start()

DataLogManager provides a convenience function (DataLogManager.log()) for logging of text messages to the messages entry in the data log. The message is also printed to standard output, so this can be a replacement for System.out.println().

DataLogManager also records the current roboRIO system time (in UTC) to the data log every ~5 seconds to the systemTime entry in the data log. This can be used to (roughly) synchronize the data log with other records such as DS logs or match video.

For custom logging, the managed DataLog can be accessed via DataLogManager.getLog().

Logging Joystick Data

DataLogManager by default does not record joystick data. The DriverStation class provides support for logging of DS control and joystick data via the startDataLog() function:

import edu.wpi.first.wpilibj.DataLogManager;
import edu.wpi.first.wpilibj.DriverStation;

// Starts recording to data log
DataLogManager.start();

// Record both DS control and joystick data
DriverStation.startDataLog(DataLogManager.getLog());

// (alternatively) Record only DS control data
DriverStation.startDataLog(DataLogManager.getLog(), false);
#include "frc/DataLogManager.h"
#include "frc/DriverStation.h"

// Starts recording to data log
frc::DataLogManager::Start();

// Record both DS control and joystick data
DriverStation::StartDataLog(DataLogManager::GetLog());

// (alternatively) Record only DS control data
DriverStation::StartDataLog(DataLogManager::GetLog(), false);
from wpilib import DataLogManager, DriverStation

# Starts recording to data log
DataLogManager.start()

# Record both DS control and joystick data
DriverStation.startDataLog(DataLogManager.getLog())

# (alternatively) Record only DS control data
DriverStation.startDataLog(DataLogManager.getLog(), False)

Custom Data Logging using DataLog

The DataLog class (Java, C++, Python) and its associated LogEntry classes (e.g. BooleanLogEntry, DoubleLogEntry, etc) provides low-level access for writing data logs.

Nota

Unlike NetworkTables, there is no change checking performed. Every call to a LogEntry.append() function will result in a record being written to the data log. Checking for changes and only appending to the log when necessary is the responsibility of the caller.

The LogEntry classes can be used in conjunction with DataLogManager to record values only to a data log and not to NetworkTables:

import edu.wpi.first.util.datalog.BooleanLogEntry;
import edu.wpi.first.util.datalog.DataLog;
import edu.wpi.first.util.datalog.DoubleLogEntry;
import edu.wpi.first.util.datalog.StringLogEntry;
import edu.wpi.first.wpilibj.DataLogManager;

BooleanLogEntry myBooleanLog;
DoubleLogEntry myDoubleLog;
StringLogEntry myStringLog;

public void robotInit() {
  // Starts recording to data log
  DataLogManager.start();

  // Set up custom log entries
  DataLog log = DataLogManager.getLog();
  myBooleanLog = new BooleanLogEntry(log, "/my/boolean");
  myDoubleLog = new DoubleLogEntry(log, "/my/double");
  myStringLog = new StringLogEntry(log, "/my/string");
}

public void teleopPeriodic() {
  if (...) {
    // Only log when necessary
    myBooleanLog.append(true);
    myDoubleLog.append(3.5);
    myStringLog.append("wow!");
  }
}
#include "frc/DataLogManager.h"
#include "wpi/DataLog.h"

wpi::log::BooleanLogEntry myBooleanLog;
wpi::log::DoubleLogEntry myDoubleLog;
wpi::log::StringLogEntry myStringLog;

void RobotInit() {
  // Starts recording to data log
  frc::DataLogManager::Start();

  // Set up custom log entries
  wpi::log::DataLog& log = frc::DataLogManager::GetLog();
  myBooleanLog = wpi::Log::BooleanLogEntry(log, "/my/boolean");
  myDoubleLog = wpi::log::DoubleLogEntry(log, "/my/double");
  myStringLog = wpi::log::StringLogEntry(log, "/my/string");
}

void TeleopPeriodic() {
  if (...) {
    // Only log when necessary
    myBooleanLog.Append(true);
    myDoubleLog.Append(3.5);
    myStringLog.Append("wow!");
  }
}
from wpilib import DataLogManager, TimedRobot
from wpiutil.log import (
    DataLog,
    BooleanLogEntry,
    DoubleLogEntry,
    StringLogEntry,
)

class MyRobot(TimedRobot):
    def robotInit(self):
        # Starts recording to data log
        DataLogManager.start()

        # Set up custom log entries
        log = DataLogManager.getLog()
        self.myBooleanLog = BooleanLogEntry(log, "/my/boolean")
        self.myDoubleLog = DoubleLogEntry(log, "/my/double")
        self.myStringLog = StringLogEntry(log, "/my/string")

    def teleopPeriodic(self):
        if ...:
            # Only log when necessary
            self.myBooleanLog.append(True)
            self.myDoubleLog.append(3.5)
            self.myStringLog.append("wow!")