Add simulation generic device/value support

This allows high-level library classes to implement enhanced simulation
support even if no low-level corresponding simulation library exists, and
avoids the need for bit-banging complex interfaces like SPI or CAN.
This commit is contained in:
Peter Johnson
2019-09-28 11:34:46 -07:00
parent e8d6f8a2c1
commit 81c2c8a7de
29 changed files with 3037 additions and 103 deletions

View File

@@ -0,0 +1,169 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018-2019 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;
@SuppressWarnings("AbbreviationAsWordInName")
public final class HALValue {
public static final int kUnassigned = 0;
public static final int kBoolean = 0x01;
public static final int kDouble = 0x02;
public static final int kEnum = 0x04;
public static final int kInt = 0x08;
public static final int kLong = 0x10;
private int m_type;
private long m_long;
private double m_double;
private HALValue(double value, int type) {
m_type = type;
m_double = value;
}
private HALValue(long value, int type) {
m_type = type;
m_long = value;
}
private HALValue() {
}
/**
* Get the type of the value.
*
* @return Type (e.g. kBoolean).
*/
public int getType() {
return m_type;
}
/**
* Get the value as a boolean. Does not perform type checking.
*
* @return value contents
*/
public boolean getBoolean() {
return m_long != 0;
}
/**
* Get the value as a long. Does not perform type checking.
*
* @return value contents
*/
public long getLong() {
return m_long;
}
/**
* Get the value as a double. Does not perform type checking.
*
* @return value contents
*/
public double getDouble() {
return m_double;
}
/**
* Get the native long value. Does not perform type checking.
*
* @return value contents
*/
public long getNativeLong() {
return m_long;
}
/**
* Get the native double value. Does not perform type checking.
*
* @return value contents
*/
public double getNativeDouble() {
return m_double;
}
/**
* Build a HAL boolean value.
*
* @param value value
* @return HAL value
*/
public static HALValue makeBoolean(boolean value) {
return new HALValue(value ? 1 : 0, kBoolean);
}
/**
* Build a HAL enum value.
*
* @param value value
* @return HAL value
*/
public static HALValue makeEnum(int value) {
return new HALValue(value, kEnum);
}
/**
* Build a HAL integer value.
*
* @param value value
* @return HAL value
*/
public static HALValue makeInt(int value) {
return new HALValue(value, kInt);
}
/**
* Build a HAL long value.
*
* @param value value
* @return HAL value
*/
public static HALValue makeLong(long value) {
return new HALValue(value, kLong);
}
/**
* Build a HAL double value.
*
* @param value value
* @return HAL value
*/
public static HALValue makeDouble(double value) {
return new HALValue(value, kDouble);
}
public static HALValue makeUnassigned() {
return new HALValue();
}
/**
* Build a HAL value from its native components.
*
* @param type type
* @param value1 long value (all except double)
* @param value2 double value (for double only)
* @return HAL value
*/
public static HALValue fromNative(int type, long value1, double value2) {
switch (type) {
case 0x01:
return makeBoolean(value1 != 0);
case 0x02:
return makeDouble(value2);
case 0x16:
return makeEnum((int) value1);
case 0x32:
return makeInt((int) value1);
case 0x64:
return makeLong(value1);
default:
return makeUnassigned();
}
}
}

View File

@@ -0,0 +1,40 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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;
/**
* A wrapper around a simulator boolean value handle.
*/
public class SimBoolean extends SimValue {
/**
* Wraps a simulated value handle as returned by SimDeviceJNI.createSimValueBoolean().
*
* @param handle simulated value handle
*/
public SimBoolean(int handle) {
super(handle);
}
/**
* Gets the simulated value.
*
* @return The current value
*/
public boolean get() {
return SimDeviceJNI.getSimValueBoolean(m_handle);
}
/**
* Sets the simulated value.
*
* @param value the value to set
*/
public void set(boolean value) {
SimDeviceJNI.setSimValueBoolean(m_handle, value);
}
}

View File

@@ -0,0 +1,171 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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;
/**
* A wrapper around a simulator device handle.
*/
public class SimDevice implements AutoCloseable {
/**
* Creates a simulated device.
*
* <p>The device name must be unique. Returns null if the device name
* already exists. If multiple instances of the same device are desired,
* recommend appending the instance/unique identifer in brackets to the base
* name, e.g. "device[1]".
*
* <p>null is returned if not in simulation.
*
* @param name device name
* @return simulated device object
*/
public static SimDevice create(String name) {
int handle = SimDeviceJNI.createSimDevice(name);
if (handle <= 0) {
return null;
}
return new SimDevice(handle);
}
/**
* Creates a simulated device.
*
* <p>The device name must be unique. Returns null if the device name
* already exists. This is a convenience method that appends index in
* brackets to the device name, e.g. passing index=1 results in "device[1]"
* for the device name.
*
* <p>null is returned if not in simulation.
*
* @param name device name
* @param index device index number to append to name
* @return simulated device object
*/
public static SimDevice create(String name, int index) {
return create(name + "[" + index + "]");
}
/**
* Creates a simulated device.
*
* <p>The device name must be unique. Returns null if the device name
* already exists. This is a convenience method that appends index and
* channel in brackets to the device name, e.g. passing index=1 and channel=2
* results in "device[1,2]" for the device name.
*
* <p>null is returned if not in simulation.
*
* @param name device name
* @param index device index number to append to name
* @param channel device channel number to append to name
* @return simulated device object
*/
public static SimDevice create(String name, int index, int channel) {
return create(name + "[" + index + "," + channel + "]");
}
/**
* Wraps a simulated device handle as returned by SimDeviceJNI.createSimDevice().
*
* @param handle simulated device handle
*/
public SimDevice(int handle) {
m_handle = handle;
}
@Override
public void close() {
SimDeviceJNI.freeSimDevice(m_handle);
}
/**
* Get the internal device handle.
*
* @return internal handle
*/
public int getNativeHandle() {
return m_handle;
}
/**
* Creates a value on the simulated device.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value object
*/
public SimValue createValue(String name, boolean readonly, HALValue initialValue) {
int handle = SimDeviceJNI.createSimValue(m_handle, name, readonly, initialValue);
if (handle <= 0) {
return null;
}
return new SimValue(handle);
}
/**
* Creates a double value on the simulated device.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated double value object
*/
public SimDouble createDouble(String name, boolean readonly, double initialValue) {
int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, readonly, initialValue);
if (handle <= 0) {
return null;
}
return new SimDouble(handle);
}
/**
* 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 readonly if the value should not be written from simulation side
* @param options array of option descriptions
* @param initialValue initial value (selection)
* @return simulated enum value object
*/
public SimEnum createEnum(String name, boolean readonly, String[] options, int initialValue) {
int handle = SimDeviceJNI.createSimValueEnum(m_handle, name, readonly, options, initialValue);
if (handle <= 0) {
return null;
}
return new SimEnum(handle);
}
/**
* Creates a boolean value on the simulated device.
*
* <p>Returns null if not in simulation.
*
* @param name value name
* @param readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated boolean value object
*/
public SimBoolean createBoolean(String name, boolean readonly, boolean initialValue) {
int handle = SimDeviceJNI.createSimValueBoolean(m_handle, name, readonly, initialValue);
if (handle <= 0) {
return null;
}
return new SimBoolean(handle);
}
private final int m_handle;
}

View File

@@ -0,0 +1,183 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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;
public class SimDeviceJNI extends JNIWrapper {
/**
* Creates a simulated device.
*
* <p>The device name must be unique. 0 is returned if the device name
* already exists. If multiple instances of the same device are desired,
* recommend appending the instance/unique identifer in brackets to the base
* name, e.g. "device[1]".
*
* <p>0 is returned if not in simulation.
*
* @param name device name
* @return simulated device handle
*/
public static native int createSimDevice(String name);
/**
* Frees a simulated device.
*
* <p>This also allows the same device name to be used again.
* This also frees all the simulated values created on the device.
*
* @param handle simulated device handle
*/
public static native void freeSimDevice(int handle);
private static native int createSimValueNative(int device, String name, boolean readonly,
int type, long value1, double value2);
/**
* 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 readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value handle
*/
public static int createSimValue(int device, String name, boolean readonly,
HALValue initialValue) {
return createSimValueNative(device, name, readonly, initialValue.getType(),
initialValue.getNativeLong(), initialValue.getNativeDouble());
}
/**
* 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 readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value handle
*/
public static int createSimValueDouble(int device, String name, boolean readonly,
double initialValue) {
return createSimValueNative(device, name, readonly, HALValue.kDouble, 0, 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 readonly if the value should not be written from simulation side
* @param options array of option descriptions
* @param initialValue initial value (selection)
* @return simulated value handle
*/
public static native int createSimValueEnum(int device, String name, boolean readonly,
String[] options, int initialValue);
/**
* 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 readonly if the value should not be written from simulation side
* @param initialValue initial value
* @return simulated value handle
*/
public static int createSimValueBoolean(int device, String name, boolean readonly,
boolean initialValue) {
return createSimValueNative(device, name, readonly, HALValue.kBoolean,
initialValue ? 1 : 0, 0.0);
}
/**
* Gets a simulated value.
*
* @param handle simulated value handle
* @return The current value
*/
public static native HALValue getSimValue(int handle);
/**
* Gets a simulated value (double).
*
* @param handle simulated value handle
* @return The current value
*/
public static native double getSimValueDouble(int handle);
/**
* Gets a simulated value (enum).
*
* @param handle simulated value handle
* @return The current value
*/
public static native int getSimValueEnum(int handle);
/**
* Gets a simulated value (boolean).
*
* @param handle simulated value handle
* @return The current value
*/
public static native boolean getSimValueBoolean(int handle);
private static native void setSimValueNative(int handle, int type, long value1, double value2);
/**
* Sets a simulated value.
*
* @param handle simulated value handle
* @param value the value to set
*/
public static void setSimValue(int handle, HALValue value) {
setSimValueNative(handle, value.getType(), value.getNativeLong(), value.getNativeDouble());
}
/**
* Sets a simulated value (double).
*
* @param handle simulated value handle
* @param value the value to set
*/
public static void setSimValueDouble(int handle, double value) {
setSimValueNative(handle, HALValue.kDouble, 0, value);
}
/**
* Sets a simulated value (enum).
*
* @param handle simulated value handle
* @param value the value to set
*/
public static void setSimValueEnum(int handle, int value) {
setSimValueNative(handle, HALValue.kEnum, value, 0.0);
}
/**
* Sets a simulated value (boolean).
*
* @param handle simulated value handle
* @param value the value to set
*/
public static void setSimValueBoolean(int handle, boolean value) {
setSimValueNative(handle, HALValue.kBoolean, value ? 1 : 0, 0.0);
}
}

View File

@@ -0,0 +1,40 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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;
/**
* A wrapper around a simulator double value handle.
*/
public class SimDouble extends SimValue {
/**
* Wraps a simulated value handle as returned by SimDeviceJNI.createSimValueDouble().
*
* @param handle simulated value handle
*/
public SimDouble(int handle) {
super(handle);
}
/**
* Gets the simulated value.
*
* @return The current value
*/
public double get() {
return SimDeviceJNI.getSimValueDouble(m_handle);
}
/**
* Sets the simulated value.
*
* @param value the value to set
*/
public void set(double value) {
SimDeviceJNI.setSimValueDouble(m_handle, value);
}
}

View File

@@ -0,0 +1,40 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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;
/**
* A wrapper around a simulator enum value handle.
*/
public class SimEnum extends SimValue {
/**
* Wraps a simulated value handle as returned by SimDeviceJNI.createSimValueEnum().
*
* @param handle simulated value handle
*/
public SimEnum(int handle) {
super(handle);
}
/**
* Gets the simulated value.
*
* @return The current value
*/
public int get() {
return SimDeviceJNI.getSimValueEnum(m_handle);
}
/**
* Sets the simulated value.
*
* @param value the value to set
*/
public void set(int value) {
SimDeviceJNI.setSimValueEnum(m_handle, value);
}
}

View File

@@ -0,0 +1,51 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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;
/**
* A wrapper around a simulator value handle.
*/
public class SimValue {
/**
* Wraps a simulated value handle as returned by SimDeviceJNI.createSimValue().
*
* @param handle simulated value handle
*/
public SimValue(int handle) {
m_handle = handle;
}
/**
* Get the internal device handle.
*
* @return internal handle
*/
public int getNativeHandle() {
return m_handle;
}
/**
* Gets the simulated value.
*
* @return The current value
*/
public HALValue getValue() {
return SimDeviceJNI.getSimValue(m_handle);
}
/**
* Sets the simulated value.
*
* @param value the value to set
*/
public void setValue(HALValue value) {
SimDeviceJNI.setSimValue(m_handle, value);
}
protected final int m_handle;
}

View File

@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 FIRST. All Rights Reserved. */
/* Copyright (c) 2018-2019 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. */
@@ -7,29 +7,12 @@
package edu.wpi.first.hal.sim;
import edu.wpi.first.hal.HALValue;
public interface NotifyCallback {
void callback(String name, SimValue value);
void callback(String name, HALValue value);
default void callbackNative(String name, int type, long value1, double value2) {
switch (type) {
case 0x01:
callback(name, SimValue.makeBoolean(value1 != 0));
break;
case 0x02:
callback(name, SimValue.makeDouble(value2));
break;
case 0x16:
callback(name, SimValue.makeEnum((int) value1));
break;
case 0x32:
callback(name, SimValue.makeInt((int) value1));
break;
case 0x64:
callback(name, SimValue.makeLong(value1));
break;
default:
callback(name, SimValue.makeUnassigned());
break;
}
callback(name, HALValue.fromNative(type, value1, value2));
}
}

View File

@@ -0,0 +1,13 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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.sim;
@FunctionalInterface
public interface SimDeviceCallback {
void callback(String name, int handle);
}

View File

@@ -1,66 +0,0 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2018 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.sim;
public final class SimValue {
private boolean m_boolean;
private long m_long;
private double m_double;
private SimValue(boolean b) {
m_boolean = b;
}
private SimValue(double v) {
m_double = v;
}
private SimValue(long v) {
m_long = v;
}
private SimValue() {
}
public boolean getBoolean() {
return m_boolean;
}
public long getLong() {
return m_long;
}
public double getDouble() {
return m_double;
}
public static SimValue makeBoolean(boolean value) {
return new SimValue(value);
}
public static SimValue makeEnum(int value) {
return new SimValue(value);
}
public static SimValue makeInt(int value) {
return new SimValue(value);
}
public static SimValue makeLong(long value) {
return new SimValue(value);
}
public static SimValue makeDouble(double value) {
return new SimValue(value);
}
public static SimValue makeUnassigned() {
return new SimValue();
}
}

View File

@@ -0,0 +1,19 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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.sim;
import edu.wpi.first.hal.HALValue;
@FunctionalInterface
public interface SimValueCallback {
void callback(String name, int handle, boolean readonly, HALValue value);
default void callbackNative(String name, int handle, boolean readonly, int type, long value1, double value2) {
callback(name, handle, readonly, HALValue.fromNative(type, value1, value2));
}
}

View File

@@ -0,0 +1,73 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2019 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.sim.mockdata;
import edu.wpi.first.hal.sim.SimDeviceCallback;
import edu.wpi.first.hal.sim.SimValueCallback;
import edu.wpi.first.hal.HALValue;
import edu.wpi.first.hal.JNIWrapper;
public class SimDeviceDataJNI extends JNIWrapper {
public static native int registerSimDeviceCreatedCallback(String prefix, SimDeviceCallback callback, boolean initialNotify);
public static native void cancelSimDeviceCreatedCallback(int uid);
public static native int registerSimDeviceFreedCallback(String prefix, SimDeviceCallback callback);
public static native void cancelSimDeviceFreedCallback(int uid);
public static native int getSimDeviceHandle(String name);
public static native int getSimValueDeviceHandle(int handle);
public static class SimDeviceInfo {
public SimDeviceInfo(String name, int handle) {
this.name = name;
this.handle = handle;
}
@SuppressWarnings("MemberName")
String name;
@SuppressWarnings("MemberName")
int handle;
}
public static native SimDeviceInfo[] enumerateSimDevices(String prefix);
public static native int registerSimValueCreatedCallback(int device, SimValueCallback callback, boolean initialNotify);
public static native void cancelSimValueCreatedCallback(int uid);
public static native int registerSimValueChangedCallback(int handle, SimValueCallback 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) {
this.name = name;
this.handle = handle;
this.readonly = readonly;
this.value = HALValue.fromNative(type, value1, value2);
}
@SuppressWarnings("MemberName")
String name;
@SuppressWarnings("MemberName")
int handle;
@SuppressWarnings("MemberName")
boolean readonly;
@SuppressWarnings("MemberName")
HALValue value;
}
public static native SimValueInfo[] enumerateSimValues(int device);
public static native String[] getSimValueEnumOptions(int handle);
public static native void resetSimDeviceData();
}