Crear tipos de datos personalizados

Los widgets le permiten controlar y visualizar diferentes tipos de datos. Estos datos podrían ser integers o doubles o incluso Objetos de Java. Para visualizar estos tipos de datos utilizando widgets es útil crear una clase contenedora para estos. No es necesario crear su propia clase de Datos si el widget manejará tipos de datos con un solo campo, tales como doubles, arrays o strings.

Crear Clase de Datos

En este ejemplo, crearemos un tipo de datos personalizado para un punto 2D y sus coordenadas x e y. Para crear una clase de tipo de datos personalizada, debe extender la clase abstracta ComplexData. Su clase de datos personalizados también debe implementar el método asMap() que devuelve los datos representados como un mapa simple como se indica a continuación con la anotación @Override:

import edu.wpi.first.shuffleboard.api.data.ComplexData;

import java.util.Map;

public class MyPoint2D extends ComplexData<MyPoint2D> {

   private final double x;
   private final double y;

   //Constructor should take all the different fields needed and assign them their corresponding instance variables.
   public MyPoint2D(double x, double y) {
      this.x = x;
      this.y = y;
   }

   @Override
   public Map<String, Object> asMap() {
      return Map.of("x", x, "y", y);
   }
}

It is also good practice to override the default equals and hashcode methods to ensure that different objects are considered equivalent when their fields are the same. The asMap() method should return the data represented in a simple Map object as it will be mapped to the NetworkTables entry it corresponds to. In this case, we can represent the point as its X and Y coordinates and return a Map containing them.

import edu.wpi.first.shuffleboard.api.data.ComplexData;

import java.util.Map;

public final class MyPoint2D extends ComplexData<MyPoint2D> {

   private final double x;
   private final double y;

   // Constructor should take all the different fields needed and assign them to their corresponding instance variables.
   public Point(double x, double y) {
      this.x = x;
      this.y = y;
   }

   @Override
   public Map<String, Object> asMap() {
      return Map.of("x", this.x, "y", this.y);
   }
 }

Otros métodos pueden añadirse para recuperar o editar campos y variables de la instancia, sin embargo, es buena práctica hacer estas clases inmutables para prevenir cambios en la fuente de datos. En cambio, puede realizar una copia del objeto en lugar de manipular el objeto existente. Por ejemplo, si quisiera cambiar la coordenada y de su punto, podría definir el siguiente método:

public MyPoint2D withY(double newY) {
   return new MyPoint2D(this.x, newY);
}

Esto crea un nuevo objeto MyPoint2D y lo regresa con una nueva coordenada Y. Puede hacerse lo mismo para la coordenada X.

Crear un Tipo de Dato

Se pueden crear dos tipos de datos: Tipos de datos simples que solo contienen un campo (por ejemplo, un solo número o un string) y tipos de datos complejos que contienen varios campos (por ejemplo, múltiples strings o números).

Para definir un tipo de dato simple, la clase debe extender la clase SimpleDataType<DataType> con el tipo de dato necesario e implementar el método getDefaultValue(). En este ejemplo, usaremos un double como su tipo de dato simple.

public final class MyDoubleDataType extends SimpleDataType<Double> {

   private static final String NAME = "Double";

   private MyDataType() {
      super(NAME, Double.class);
   }

   @Override
   public Double getDefaultValue() {
      return 0.0;
   }

}

El constructor de la clase es privado para asegurar que una sola instancia del tipo de dato exista

Para definir un tipo de dato complejo, la clase debe extender la clase ComplexDataType y sobrescribir los métodos fromMap() y getDefaultValue(). Se continuará usando como ejemplo la clase MyPoint2D para demostrar cómo se ve una clase de tipos de datos complejos.

public final class PointDataType extends ComplexDataType<MyPoint2D> {

   private static final String NAME = "MyPoint2D";
   public static final PointDataType Instance = new PointDataType();

   private PointDataType() {
      super(NAME, MyPoint2D.class);
   }

   @Override
   public Function<Map<String, Object>, MyPoint2D> fromMap() {
      return map -> {
         return new MyPoint2D((double) map.getOrDefault("x", 0.0), (double) map.getOrDefault("y", 0.0));
      };
   }

   @Override
   public MyPoint2D getDefaultValue() {
      // use default values of 0 for X and Y coordinates
      return new MyPoint2D(0, 0);
   }

}

El código de arriba funciona de la siguiente forma:

The fromMap() method creates a new MyPoint2D using the values in the NetworkTables entry it is bound to. The getOrDefault method will return 0.0 if it cannot get the entry values. The getDefaultValue will return a new MyPoint2D object if no source is present.

Exportar un tipo de dato al plugin

Para que Shuffleboard reconozca el tipo de dato, el plugin debe exportarlos al sobrescribir el método getDataTypes. Por ejemplo,

public class MyPlugin extends Plugin {

   @Override
   public List<DataType> getDataTypes() {
      List.of(PointDataType.Instance);
   }

}