[sim] Various WebSockets fixes and enhancements (#2952)

This is a breaking change to the WebSockets layer to align it with
recent specification documentation work.

To support this, HAL SimValue changed readonly to a direction enum.
This allows specifying bidirectional in addition to input and output.

The SimValue change is specifically designed to avoid API and ABI breakage.
This is completely transparent in C++; in Java a new callback class was added,
and the old readonly functions have been marked deprecated.

A new SimValue creation function for enums allows specifying double values
for each enum value, not just strings.  This allows mapping enum values to
doubles in the WebSockets layer.

A ":" in the SimDevice name now maps it to different WebSocket types (e.g.
"Accel:Name" becomes type "Accel", device "Name").  The type is hidden
in the GUI.

Other WebSockets changes:
* Implemented match_time and game_data
* Added joystick rumble data
* Added builtin accelerometer support
* SimValue enums are mapped to string and double value on WS interface
* Added WebSockets protocol specification
* Added READMEs
This commit is contained in:
Peter Johnson
2020-12-23 15:54:11 -08:00
committed by GitHub
parent 699bbe21a4
commit 10b396b4c2
38 changed files with 1264 additions and 187 deletions

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -11,6 +11,18 @@ package edu.wpi.first.hal;
* A wrapper around a simulator device handle.
*/
public class SimDevice implements AutoCloseable {
public enum Direction {
kInput(SimDeviceJNI.kInput),
kOutput(SimDeviceJNI.kOutput),
kBidir(SimDeviceJNI.kBidir);
public final int m_value;
Direction(int value) {
m_value = value;
}
}
/**
* Creates a simulated device.
*
@@ -101,9 +113,26 @@ public class SimDevice implements AutoCloseable {
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value object
*
* @deprecated Use direction function instead
*/
@Deprecated
public SimValue createValue(String name, boolean readonly, HALValue initialValue) {
int handle = SimDeviceJNI.createSimValue(m_handle, name, readonly, initialValue);
return createValue(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
}
/**
* Creates a value on the simulated device.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param initialValue initial value
* @return simulated value object
*/
public SimValue createValue(String name, Direction direction, HALValue initialValue) {
int handle = SimDeviceJNI.createSimValue(m_handle, name, direction.m_value, initialValue);
if (handle <= 0) {
return null;
}
@@ -119,9 +148,26 @@ public class SimDevice implements AutoCloseable {
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated double value object
*
* @deprecated Use direction function instead
*/
@Deprecated
public SimDouble createDouble(String name, boolean readonly, double initialValue) {
int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, readonly, initialValue);
return createDouble(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
}
/**
* Creates a double value on the simulated device.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param initialValue initial value
* @return simulated double value object
*/
public SimDouble createDouble(String name, Direction direction, double initialValue) {
int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, direction.m_value, initialValue);
if (handle <= 0) {
return null;
}
@@ -140,9 +186,54 @@ public class SimDevice implements AutoCloseable {
* @param options array of option descriptions
* @param initialValue initial value (selection)
* @return simulated enum value object
*
* @deprecated Use direction function instead
*/
@Deprecated
public SimEnum createEnum(String name, boolean readonly, String[] options, int initialValue) {
int handle = SimDeviceJNI.createSimValueEnum(m_handle, name, readonly, options, initialValue);
return createEnum(name, readonly ? Direction.kOutput : Direction.kInput, options, initialValue);
}
/**
* Creates an enumerated value on the simulated device.
*
* <p>Enumerated values are always in the range 0 to numOptions-1.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param options array of option descriptions
* @param initialValue initial value (selection)
* @return simulated enum value object
*/
public SimEnum createEnum(String name, Direction direction, String[] options, int initialValue) {
int handle = SimDeviceJNI.createSimValueEnum(m_handle, name, direction.m_value, options,
initialValue);
if (handle <= 0) {
return null;
}
return new SimEnum(handle);
}
/**
* Creates an enumerated value on the simulated device with double values.
*
* <p>Enumerated values are always in the range 0 to numOptions-1.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param options array of option descriptions
* @param optionValues array of option values (must be the same size as options)
* @param initialValue initial value (selection)
* @return simulated enum value object
*/
public SimEnum createEnumDouble(String name, Direction direction, String[] options,
double[] optionValues, int initialValue) {
int handle = SimDeviceJNI.createSimValueEnumDouble(m_handle, name, direction.m_value, options,
optionValues, initialValue);
if (handle <= 0) {
return null;
}
@@ -158,9 +249,27 @@ public class SimDevice implements AutoCloseable {
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated boolean value object
*
* @deprecated Use direction function instead
*/
@Deprecated
public SimBoolean createBoolean(String name, boolean readonly, boolean initialValue) {
int handle = SimDeviceJNI.createSimValueBoolean(m_handle, name, readonly, initialValue);
return createBoolean(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
}
/**
* Creates a boolean value on the simulated device.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param initialValue initial value
* @return simulated boolean value object
*/
public SimBoolean createBoolean(String name, Direction direction, boolean initialValue) {
int handle = SimDeviceJNI.createSimValueBoolean(m_handle, name, direction.m_value,
initialValue);
if (handle <= 0) {
return null;
}

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
@@ -8,6 +8,10 @@
package edu.wpi.first.hal;
public class SimDeviceJNI extends JNIWrapper {
public static final int kInput = 0;
public static final int kOutput = 1;
public static final int kBidir = 2;
/**
* Creates a simulated device.
*
@@ -33,7 +37,7 @@ public class SimDeviceJNI extends JNIWrapper {
*/
public static native void freeSimDevice(int handle);
private static native int createSimValueNative(int device, String name, boolean readonly,
private static native int createSimValueNative(int device, String name, int direction,
int type, long value1, double value2);
/**
@@ -47,10 +51,31 @@ public class SimDeviceJNI extends JNIWrapper {
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value handle
*
* @deprecated Use direction-taking function instead
*/
@Deprecated
public static int createSimValue(int device, String name, boolean readonly,
HALValue initialValue) {
return createSimValueNative(device, name, readonly, initialValue.getType(),
return createSimValueNative(device, name, readonly ? kOutput : kInput, initialValue.getType(),
initialValue.getNativeLong(), initialValue.getNativeDouble());
}
/**
* Creates a value on a simulated device.
*
* <p>Returns 0 if not in simulation; this can be used to avoid calls
* to Set/Get functions.
*
* @param device simulated device handle
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param initialValue initial value
* @return simulated value handle
*/
public static int createSimValue(int device, String name, int direction,
HALValue initialValue) {
return createSimValueNative(device, name, direction, initialValue.getType(),
initialValue.getNativeLong(), initialValue.getNativeDouble());
}
@@ -65,10 +90,30 @@ public class SimDeviceJNI extends JNIWrapper {
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value handle
*
* @deprecated Use direction-taking function instead
*/
@Deprecated
public static int createSimValueDouble(int device, String name, boolean readonly,
double initialValue) {
return createSimValueNative(device, name, readonly, HALValue.kDouble, 0, initialValue);
return createSimValueNative(device, name, readonly ? kOutput : kInput, HALValue.kDouble, 0, initialValue);
}
/**
* Creates a double value on a simulated device.
*
* <p>Returns 0 if not in simulation; this can be used to avoid calls
* to Set/Get functions.
*
* @param device simulated device handle
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param initialValue initial value
* @return simulated value handle
*/
public static int createSimValueDouble(int device, String name, int direction,
double initialValue) {
return createSimValueNative(device, name, direction, HALValue.kDouble, 0, initialValue);
}
/**
@@ -85,10 +130,52 @@ public class SimDeviceJNI extends JNIWrapper {
* @param options array of option descriptions
* @param initialValue initial value (selection)
* @return simulated value handle
*
* @deprecated Use direction-taking function instead
*/
public static native int createSimValueEnum(int device, String name, boolean readonly,
@Deprecated
public static int createSimValueEnum(int device, String name, boolean readonly,
String[] options, int initialValue) {
return createSimValueEnum(device, name, readonly ? kOutput : kInput, options, initialValue);
}
/**
* Creates an enumerated value on a simulated device.
*
* <p>Enumerated values are always in the range 0 to numOptions-1.
*
* <p>Returns 0 if not in simulation; this can be used to avoid calls
* to Set/Get functions.
*
* @param device simulated device handle
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param options array of option descriptions
* @param initialValue initial value (selection)
* @return simulated value handle
*/
public static native int createSimValueEnum(int device, String name, int direction,
String[] options, int initialValue);
/**
* Creates an enumerated value on a simulated device with double values.
*
* <p>Enumerated values are always in the range 0 to numOptions-1.
*
* <p>Returns 0 if not in simulation; this can be used to avoid calls
* to Set/Get functions.
*
* @param device simulated device handle
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param options array of option descriptions
* @param optionValues array of option values (must be the same size as options)
* @param initialValue initial value (selection)
* @return simulated value handle
*/
public static native int createSimValueEnumDouble(int device, String name, int direction,
String[] options, double[] optionValues, int initialValue);
/**
* Creates a boolean value on a simulated device.
*
@@ -100,10 +187,31 @@ public class SimDeviceJNI extends JNIWrapper {
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value handle
*
* @deprecated Use direction-taking function instead
*/
@Deprecated
public static int createSimValueBoolean(int device, String name, boolean readonly,
boolean initialValue) {
return createSimValueNative(device, name, readonly, HALValue.kBoolean,
return createSimValueNative(device, name, readonly ? kOutput : kInput, HALValue.kBoolean,
initialValue ? 1 : 0, 0.0);
}
/**
* Creates a boolean value on a simulated device.
*
* <p>Returns 0 if not in simulation; this can be used to avoid calls
* to Set/Get functions.
*
* @param device simulated device handle
* @param name value name
* @param direction input/output/bidir (from perspective of user code)
* @param initialValue initial value
* @return simulated value handle
*/
public static int createSimValueBoolean(int device, String name, int direction,
boolean initialValue) {
return createSimValueNative(device, name, direction, HALValue.kBoolean,
initialValue ? 1 : 0, 0.0);
}

View File

@@ -41,18 +41,21 @@ public class SimDeviceDataJNI extends JNIWrapper {
public static native SimDeviceInfo[] enumerateSimDevices(String prefix);
public static native int registerSimValueCreatedCallback(int device, SimValueCallback callback, boolean initialNotify);
public static native int registerSimValueCreatedCallback2(int device, SimValueCallback2 callback, boolean initialNotify);
public static native void cancelSimValueCreatedCallback(int uid);
public static native int registerSimValueChangedCallback(int handle, SimValueCallback callback, boolean initialNotify);
public static native int registerSimValueChangedCallback2(int handle, SimValueCallback2 callback, boolean initialNotify);
public static native void cancelSimValueChangedCallback(int uid);
public static native int getSimValueHandle(int device, String name);
public static class SimValueInfo {
public SimValueInfo(String name, int handle, boolean readonly, int type, long value1, double value2) {
public SimValueInfo(String name, int handle, int direction, int type, long value1, double value2) {
this.name = name;
this.handle = handle;
this.readonly = readonly;
this.readonly = direction == 1;
this.direction = direction;
this.value = HALValue.fromNative(type, value1, value2);
}
@@ -63,8 +66,12 @@ public class SimDeviceDataJNI extends JNIWrapper {
public int handle;
@SuppressWarnings("MemberName")
@Deprecated
public boolean readonly;
@SuppressWarnings("MemberName")
public int direction;
@SuppressWarnings("MemberName")
public HALValue value;
}
@@ -72,5 +79,7 @@ public class SimDeviceDataJNI extends JNIWrapper {
public static native String[] getSimValueEnumOptions(int handle);
public static native double[] getSimValueEnumDoubleValues(int handle);
public static native void resetSimDeviceData();
}

View File

@@ -0,0 +1,19 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.hal.simulation;
import edu.wpi.first.hal.HALValue;
@FunctionalInterface
public interface SimValueCallback2 {
void callback(String name, int handle, int direction, HALValue value);
default void callbackNative(String name, int handle, int direction, int type, long value1, double value2) {
callback(name, handle, direction, HALValue.fromNative(type, value1, value2));
}
}