2017 FRC Control System

Creating a custom control using Java

sfx comes with a palette of built-in controls that feature a wide range of use cases. But sometimes you would like to further customize your robot dashboard with controls that you create yourself. There are two strategies for creating custom controls, either:

FXML - a XML-based markup language for describing your own controls using a declarative language without needing programming

Java-based controls can have more complex requirements and behaviors

In this lesson we'll look at creating Java-based controls. For FXML controls see the FXML tutorial.

Creating a X-Y location indicator using Java

Suppose you need to display some object in 2D space, like a vision target from the camera, robot position on the field, or Joystick position so that field operators can easily see the location. As this has multiple variables, it is much easier to do this with Java-based controls.

In this simple example, we will be adding an Ellipse to a data-enabled AnchorPane and moving it based on an object with x and y properties.

Create a Netbeans project

In Netbeans, create a new Java Class Library. Once you create the project, right click it and go to properties. Inside the properties, select "Libraries" and add sfxlib.jar and sfxmeta.jar (they will appear in the same directory as sfx.jar after one run). Also add jfxrt.jar, which is system dependent, but is normally found in $JAVA_HOME/jre/lib/jfxrt.jar

Adding the Control class

Now add a Java source file for your new class (this example will call it xyLocation.java in package com.example). For our example we will extend DataAnchorPane as it is both data-enabled and supports positioning children via x and y coordinates. Since Java classes can have annotations, we can place what FXML files require in manifests in annotations only. Add the following annotation to the class:

@Category("Tutorial")

This marks the class as being in the toolbox category "Tutorial"

@Designable(value = "X-Y Location", description = "A control to show x/y position in a range")

This describes this class as being designable in SFX, showing it with the given name and description

@GroupType("xyLocation")

This says that the control designs all groups of type xyLocation. This is implemented in NetworkTables by giving a table a sub-key of ~TYPE~ with value xyLocation

@DashFXProperties("Sealed: true, Save Children: false")

This adds any arbitrary manifest attributes to the class. These say to treat this as an atomic object, even though we are extending a pane that supports designable children.

As we are extending from a data-enabled class (DataAnchorPane), we can simply call getObservable() on ourselves and not worry too much about it. As we are displaying an object, we do need to enable the default name-prepending via setDataMode(DataPaneMode.Nested). This makes all calls to getObservable("x") to retrieve the values under this.getName() + "/x" instead of just "x".

All controls are given a data source to follow when they are registered with the DataCore, which then calls registered(). We need to override this so we can get our keys from the provider at this time.

@Override
public void registered(DataCoreProvider provider)
{
super.registered(provider);
unwatch();
// if we are being registered, then we can finally get the x and y variable
// otherwise just unwatch as we are being unregistered
if (provider != null)
{
xValue = getObservable("x");
yValue = getObservable("y");
rewatch();
}
}

In order to enable more complex actions later, we will add listeners to the SmartValues

This simply directly sets the position from the values with a constant offset of the radius. Note that this does not scale nor have any limits, so the ellipse can move off the canvas. The units are JavaFX DPI-independent pixels (roughly 1 px at normal dpi with no transforms)

The ellipse is very simple and defined as such:

// we are displaying results by moving the ellipse. initialize it here
ellipse = new Ellipse(10, 10, 10, 10);
ellipse.setFill(Color.LIGHTBLUE);
this.getChildren().add(ellipse); // we inherited from DAP so just add it to ourselves

Note that currently there is a small bug in DataPane in that nested mode does not update the correct keys when the name changes. As such, it is currently required to re-bind on each name change, however will not be once this bug is fixed

Creating the manifest to register the control with SFX

To add your control to the dashboard, you need to package it in a plugin. For FXML controls you can simply put it in a folder or pack it in a jar. Either way, we need a manifest file that describes what the plugin contains. Manifests are written in YAML and can contain multiple controls and other options. For our needs, we will start with this file (please generate your own UUID. uuidgenerator.net is where the provided UUID was generated)

0
Report Errors

Use this form to report any errors with the documentation. For help with WPILib, please use the FIRST Forums at http://forums.usfirst.org For reporting WPILib bugs, please submit an issue on GitHub at https://github.com/wpilibsuite/allwpilib/issues/new