diff --git a/hal/src/main/java/edu/wpi/first/hal/HALValue.java b/hal/src/main/java/edu/wpi/first/hal/HALValue.java
new file mode 100644
index 0000000000..636af8f903
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/HALValue.java
@@ -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();
+ }
+ }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimBoolean.java b/hal/src/main/java/edu/wpi/first/hal/SimBoolean.java
new file mode 100644
index 0000000000..00e8552f14
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/SimBoolean.java
@@ -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);
+ }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDevice.java b/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
new file mode 100644
index 0000000000..ecddc2ef01
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/SimDevice.java
@@ -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.
+ *
+ *
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]".
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
Enumerated values are always in the range 0 to numOptions-1.
+ *
+ *
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.
+ *
+ *
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;
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java b/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
new file mode 100644
index 0000000000..d9b8931bc7
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/SimDeviceJNI.java
@@ -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.
+ *
+ *
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]".
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
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.
+ *
+ *
Enumerated values are always in the range 0 to numOptions-1.
+ *
+ *
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.
+ *
+ *
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);
+ }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimDouble.java b/hal/src/main/java/edu/wpi/first/hal/SimDouble.java
new file mode 100644
index 0000000000..51c97893ff
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/SimDouble.java
@@ -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);
+ }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimEnum.java b/hal/src/main/java/edu/wpi/first/hal/SimEnum.java
new file mode 100644
index 0000000000..d951bedd9f
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/SimEnum.java
@@ -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);
+ }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/SimValue.java b/hal/src/main/java/edu/wpi/first/hal/SimValue.java
new file mode 100644
index 0000000000..05d6b0ce3c
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/SimValue.java
@@ -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;
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/sim/NotifyCallback.java b/hal/src/main/java/edu/wpi/first/hal/sim/NotifyCallback.java
index 8781f759bf..9c655e4b48 100644
--- a/hal/src/main/java/edu/wpi/first/hal/sim/NotifyCallback.java
+++ b/hal/src/main/java/edu/wpi/first/hal/sim/NotifyCallback.java
@@ -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));
}
}
diff --git a/hal/src/main/java/edu/wpi/first/hal/sim/SimDeviceCallback.java b/hal/src/main/java/edu/wpi/first/hal/sim/SimDeviceCallback.java
new file mode 100644
index 0000000000..ff9e45b2e3
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/sim/SimDeviceCallback.java
@@ -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);
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/sim/SimValue.java b/hal/src/main/java/edu/wpi/first/hal/sim/SimValue.java
deleted file mode 100644
index 3dbb7c9477..0000000000
--- a/hal/src/main/java/edu/wpi/first/hal/sim/SimValue.java
+++ /dev/null
@@ -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();
- }
-}
diff --git a/hal/src/main/java/edu/wpi/first/hal/sim/SimValueCallback.java b/hal/src/main/java/edu/wpi/first/hal/sim/SimValueCallback.java
new file mode 100644
index 0000000000..786158acf4
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/sim/SimValueCallback.java
@@ -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));
+ }
+}
diff --git a/hal/src/main/java/edu/wpi/first/hal/sim/mockdata/SimDeviceDataJNI.java b/hal/src/main/java/edu/wpi/first/hal/sim/mockdata/SimDeviceDataJNI.java
new file mode 100644
index 0000000000..2b8a38ab93
--- /dev/null
+++ b/hal/src/main/java/edu/wpi/first/hal/sim/mockdata/SimDeviceDataJNI.java
@@ -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();
+}
diff --git a/hal/src/main/native/athena/SimDevice.cpp b/hal/src/main/native/athena/SimDevice.cpp
new file mode 100644
index 0000000000..94a65d49fe
--- /dev/null
+++ b/hal/src/main/native/athena/SimDevice.cpp
@@ -0,0 +1,41 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#include "hal/SimDevice.h"
+
+extern "C" {
+
+HAL_SimDeviceHandle HAL_CreateSimDevice(const char* name) { return 0; }
+
+void HAL_FreeSimDevice(HAL_SimDeviceHandle handle) {}
+
+HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
+ const char* name, HAL_Bool readonly,
+ const struct HAL_Value* initialValue) {
+ return 0;
+}
+
+HAL_SimValueHandle HAL_CreateSimValueEnum(HAL_SimDeviceHandle device,
+ const char* name, HAL_Bool readonly,
+ int32_t numOptions,
+ const char** options,
+ int32_t initialValue) {
+ return 0;
+}
+
+void HAL_GetSimValue(HAL_SimValueHandle handle, struct HAL_Value* value) {
+ value->type = HAL_UNASSIGNED;
+}
+
+void HAL_SetSimValue(HAL_SimValueHandle handle, const struct HAL_Value* value) {
+}
+
+hal::SimDevice::SimDevice(const char* name, int index) {}
+
+hal::SimDevice::SimDevice(const char* name, int index, int channel) {}
+
+} // extern "C"
diff --git a/hal/src/main/native/cpp/jni/HALUtil.cpp b/hal/src/main/native/cpp/jni/HALUtil.cpp
index 46c068b544..26b791914a 100644
--- a/hal/src/main/native/cpp/jni/HALUtil.cpp
+++ b/hal/src/main/native/cpp/jni/HALUtil.cpp
@@ -49,13 +49,15 @@ static JClass canStatusCls;
static JClass matchInfoDataCls;
static JClass accumulatorResultCls;
static JClass canDataCls;
+static JClass halValueCls;
static const JClassInit classes[] = {
{"edu/wpi/first/hal/PWMConfigDataResult", &pwmConfigDataResultCls},
{"edu/wpi/first/hal/can/CANStatus", &canStatusCls},
{"edu/wpi/first/hal/MatchInfoData", &matchInfoDataCls},
{"edu/wpi/first/hal/AccumulatorResult", &accumulatorResultCls},
- {"edu/wpi/first/hal/CANData", &canDataCls}};
+ {"edu/wpi/first/hal/CANData", &canDataCls},
+ {"edu/wpi/first/hal/HALValue", &halValueCls}};
static const JExceptionInit exceptions[] = {
{"java/lang/IllegalArgumentException", &illegalArgExCls},
@@ -224,8 +226,9 @@ jobject CreatePWMConfigDataResult(JNIEnv* env, int32_t maxPwm,
int32_t deadbandMinPwm, int32_t minPwm) {
static jmethodID constructor =
env->GetMethodID(pwmConfigDataResultCls, "", "(IIIII)V");
- return env->NewObject(pwmConfigDataResultCls, constructor, maxPwm,
- deadbandMaxPwm, centerPwm, deadbandMinPwm, minPwm);
+ return env->NewObject(pwmConfigDataResultCls, constructor, (jint)maxPwm,
+ (jint)deadbandMaxPwm, (jint)centerPwm,
+ (jint)deadbandMinPwm, (jint)minPwm);
}
void SetCanStatusObject(JNIEnv* env, jobject canStatus,
@@ -271,6 +274,34 @@ jbyteArray SetCANDataObject(JNIEnv* env, jobject canData, int32_t length,
return retVal;
}
+jobject CreateHALValue(JNIEnv* env, const HAL_Value& value) {
+ static jmethodID fromNative = env->GetStaticMethodID(
+ halValueCls, "fromNative", "(IJD)Ledu/wpi/first/hal/HALValue;");
+ jlong value1 = 0;
+ jdouble value2 = 0.0;
+ switch (value.type) {
+ case HAL_BOOLEAN:
+ value1 = value.data.v_boolean;
+ break;
+ case HAL_DOUBLE:
+ value2 = value.data.v_double;
+ break;
+ case HAL_ENUM:
+ value1 = value.data.v_enum;
+ break;
+ case HAL_INT:
+ value1 = value.data.v_int;
+ break;
+ case HAL_LONG:
+ value1 = value.data.v_long;
+ break;
+ default:
+ break;
+ }
+ return env->CallStaticObjectMethod(halValueCls, fromNative, (jint)value.type,
+ value1, value2);
+}
+
JavaVM* GetJVM() { return jvm; }
} // namespace frc
diff --git a/hal/src/main/native/cpp/jni/HALUtil.h b/hal/src/main/native/cpp/jni/HALUtil.h
index 8197e1a766..c035f75ab1 100644
--- a/hal/src/main/native/cpp/jni/HALUtil.h
+++ b/hal/src/main/native/cpp/jni/HALUtil.h
@@ -1,5 +1,5 @@
/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
+/* Copyright (c) 2016-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. */
@@ -14,6 +14,7 @@
#include
struct HAL_MatchInfo;
+struct HAL_Value;
namespace frc {
@@ -67,6 +68,8 @@ void SetAccumulatorResultObject(JNIEnv* env, jobject accumulatorResult,
jbyteArray SetCANDataObject(JNIEnv* env, jobject canData, int32_t length,
uint64_t timestamp);
+jobject CreateHALValue(JNIEnv* env, const HAL_Value& value);
+
JavaVM* GetJVM();
} // namespace frc
diff --git a/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp b/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp
new file mode 100644
index 0000000000..d9652ccf2b
--- /dev/null
+++ b/hal/src/main/native/cpp/jni/SimDeviceJNI.cpp
@@ -0,0 +1,168 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#include
+
+#include
+
+#include "HALUtil.h"
+#include "edu_wpi_first_hal_SimDeviceJNI.h"
+#include "hal/SimDevice.h"
+
+using namespace wpi::java;
+
+static HAL_Value ValueFromJava(jint type, jlong value1, jdouble value2) {
+ HAL_Value value;
+ value.type = static_cast(type);
+ switch (value.type) {
+ case HAL_BOOLEAN:
+ value.data.v_boolean = value1;
+ break;
+ case HAL_DOUBLE:
+ value.data.v_double = value2;
+ break;
+ case HAL_ENUM:
+ value.data.v_enum = value1;
+ break;
+ case HAL_INT:
+ value.data.v_int = value1;
+ break;
+ case HAL_LONG:
+ value.data.v_long = value1;
+ break;
+ default:
+ break;
+ }
+ return value;
+}
+
+extern "C" {
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: createSimDevice
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_createSimDevice
+ (JNIEnv* env, jclass, jstring name)
+{
+ return HAL_CreateSimDevice(JStringRef{env, name}.c_str());
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: freeSimDevice
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_freeSimDevice
+ (JNIEnv*, jclass, jint handle)
+{
+ HAL_FreeSimDevice(handle);
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: createSimValueNative
+ * Signature: (ILjava/lang/String;ZIJD)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueNative
+ (JNIEnv* env, jclass, jint device, jstring name, jboolean readonly, jint type,
+ jlong value1, jdouble value2)
+{
+ return HAL_CreateSimValue(device, JStringRef{env, name}.c_str(), readonly,
+ ValueFromJava(type, value1, value2));
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: createSimValueEnum
+ * Signature: (ILjava/lang/String;Z[Ljava/lang/Object;I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueEnum
+ (JNIEnv* env, jclass, jint device, jstring name, jboolean readonly,
+ jobjectArray options, jint initialValue)
+{
+ size_t len = env->GetArrayLength(options);
+ std::vector arr;
+ arr.reserve(len);
+ for (size_t i = 0; i < len; ++i) {
+ JLocal elem{
+ env, static_cast(env->GetObjectArrayElement(options, i))};
+ if (!elem) return 0;
+ arr.push_back(JStringRef{env, elem}.str());
+ }
+ wpi::SmallVector carr;
+ for (auto&& val : arr) carr.push_back(val.c_str());
+ return HAL_CreateSimValueEnum(device, JStringRef{env, name}.c_str(), readonly,
+ len, carr.data(), initialValue);
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: getSimValue
+ * Signature: (I)Ljava/lang/Object;
+ */
+JNIEXPORT jobject JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_getSimValue
+ (JNIEnv* env, jclass, jint handle)
+{
+ return frc::CreateHALValue(env, HAL_GetSimValue(handle));
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: getSimValueDouble
+ * Signature: (I)D
+ */
+JNIEXPORT jdouble JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_getSimValueDouble
+ (JNIEnv*, jclass, jint handle)
+{
+ return HAL_GetSimValueDouble(handle);
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: getSimValueEnum
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_getSimValueEnum
+ (JNIEnv*, jclass, jint handle)
+{
+ return HAL_GetSimValueEnum(handle);
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: getSimValueBoolean
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_getSimValueBoolean
+ (JNIEnv*, jclass, jint handle)
+{
+ return HAL_GetSimValueBoolean(handle);
+}
+
+/*
+ * Class: edu_wpi_first_hal_SimDeviceJNI
+ * Method: setSimValueNative
+ * Signature: (IIJD)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_SimDeviceJNI_setSimValueNative
+ (JNIEnv*, jclass, jint handle, jint type, jlong value1, jdouble value2)
+{
+ HAL_SetSimValue(handle, ValueFromJava(type, value1, value2));
+}
+
+} // extern "C"
diff --git a/hal/src/main/native/include/hal/HAL.h b/hal/src/main/native/include/hal/HAL.h
index 974be0a54a..2f71d796eb 100644
--- a/hal/src/main/native/include/hal/HAL.h
+++ b/hal/src/main/native/include/hal/HAL.h
@@ -36,6 +36,7 @@
#include "hal/Relay.h"
#include "hal/SPI.h"
#include "hal/SerialPort.h"
+#include "hal/SimDevice.h"
#include "hal/Solenoid.h"
#include "hal/Threads.h"
#include "hal/Types.h"
diff --git a/hal/src/main/native/include/hal/SimDevice.h b/hal/src/main/native/include/hal/SimDevice.h
new file mode 100644
index 0000000000..b05021e5ee
--- /dev/null
+++ b/hal/src/main/native/include/hal/SimDevice.h
@@ -0,0 +1,610 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include
+
+#ifdef __cplusplus
+#include
+
+#include
+#endif
+
+#include "hal/Types.h"
+#include "hal/Value.h"
+
+/**
+ * @defgroup hal_simdevice Simulator Device Framework
+ * @ingroup hal_capi
+ * HAL Simulator Device Framework. This enables creating simulation-only
+ * variables for higher level device access. For example, a device such as
+ * a SPI gyro can expose angle and rate variables to enable direct access
+ * from simulation extensions or user test code instead of requiring that
+ * the SPI bit-level protocol be implemented in simulation code.
+ *
+ * @{
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Creates a simulated device.
+ *
+ * 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]".
+ *
+ * 0 is returned if not in simulation.
+ *
+ * @param name device name
+ * @return simulated device handle
+ */
+HAL_SimDeviceHandle HAL_CreateSimDevice(const char* name);
+
+/**
+ * Frees a simulated device.
+ *
+ * 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
+ */
+void HAL_FreeSimDevice(HAL_SimDeviceHandle handle);
+
+/**
+ * Creates a value on a simulated device.
+ *
+ * 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
+ */
+HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
+ const char* name, HAL_Bool readonly,
+ const struct HAL_Value* initialValue);
+
+#ifdef __cplusplus
+extern "C++" {
+inline HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
+ const char* name,
+ HAL_Bool readonly,
+ const HAL_Value& initialValue) {
+ return HAL_CreateSimValue(device, name, readonly, &initialValue);
+}
+} // extern "C++"
+#endif
+
+/**
+ * Creates a double value on a simulated device.
+ *
+ * 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
+ */
+inline HAL_SimValueHandle HAL_CreateSimValueDouble(HAL_SimDeviceHandle device,
+ const char* name,
+ HAL_Bool readonly,
+ double initialValue) {
+ struct HAL_Value v = HAL_MakeDouble(initialValue);
+ return HAL_CreateSimValue(device, name, readonly, &v);
+}
+
+/**
+ * Creates an enumerated value on a simulated device.
+ *
+ * Enumerated values are always in the range 0 to numOptions-1.
+ *
+ * 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 numOptions number of enumerated value options (length of options)
+ * @param options array of option descriptions
+ * @param initialValue initial value (selection)
+ * @return simulated value handle
+ */
+HAL_SimValueHandle HAL_CreateSimValueEnum(HAL_SimDeviceHandle device,
+ const char* name, HAL_Bool readonly,
+ int32_t numOptions,
+ const char** options,
+ int32_t initialValue);
+
+/**
+ * Creates a boolean value on a simulated device.
+ *
+ * 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
+ */
+inline HAL_SimValueHandle HAL_CreateSimValueBoolean(HAL_SimDeviceHandle device,
+ const char* name,
+ HAL_Bool readonly,
+ HAL_Bool initialValue) {
+ struct HAL_Value v = HAL_MakeBoolean(initialValue);
+ return HAL_CreateSimValue(device, name, readonly, &v);
+}
+
+/**
+ * Gets a simulated value.
+ *
+ * @param handle simulated value handle
+ * @param value value (output parameter)
+ */
+void HAL_GetSimValue(HAL_SimValueHandle handle, struct HAL_Value* value);
+
+#ifdef __cplusplus
+extern "C++" {
+inline HAL_Value HAL_GetSimValue(HAL_SimValueHandle handle) {
+ HAL_Value v;
+ HAL_GetSimValue(handle, &v);
+ return v;
+}
+} // extern "C++"
+#endif
+
+/**
+ * Gets a simulated value (double).
+ *
+ * @param handle simulated value handle
+ * @return The current value
+ */
+inline double HAL_GetSimValueDouble(HAL_SimValueHandle handle) {
+ struct HAL_Value v;
+ HAL_GetSimValue(handle, &v);
+ return v.type == HAL_DOUBLE ? v.data.v_double : 0.0;
+}
+
+/**
+ * Gets a simulated value (enum).
+ *
+ * @param handle simulated value handle
+ * @return The current value
+ */
+inline int32_t HAL_GetSimValueEnum(HAL_SimValueHandle handle) {
+ struct HAL_Value v;
+ HAL_GetSimValue(handle, &v);
+ return v.type == HAL_ENUM ? v.data.v_enum : 0;
+}
+
+/**
+ * Gets a simulated value (boolean).
+ *
+ * @param handle simulated value handle
+ * @return The current value
+ */
+inline HAL_Bool HAL_GetSimValueBoolean(HAL_SimValueHandle handle) {
+ struct HAL_Value v;
+ HAL_GetSimValue(handle, &v);
+ return v.type == HAL_BOOLEAN ? v.data.v_boolean : 0;
+}
+
+/**
+ * Sets a simulated value.
+ *
+ * @param handle simulated value handle
+ * @param value the value to set
+ */
+void HAL_SetSimValue(HAL_SimValueHandle handle, const struct HAL_Value* value);
+
+#ifdef __cplusplus
+extern "C++" {
+inline void HAL_SetSimValue(HAL_SimValueHandle handle, const HAL_Value& value) {
+ HAL_SetSimValue(handle, &value);
+}
+} // extern "C++"
+#endif
+
+/**
+ * Sets a simulated value (double).
+ *
+ * @param handle simulated value handle
+ * @param value the value to set
+ */
+inline void HAL_SetSimValueDouble(HAL_SimValueHandle handle, double value) {
+ struct HAL_Value v = HAL_MakeDouble(value);
+ HAL_SetSimValue(handle, &v);
+}
+
+/**
+ * Sets a simulated value (enum).
+ *
+ * @param handle simulated value handle
+ * @param value the value to set
+ */
+inline void HAL_SetSimValueEnum(HAL_SimValueHandle handle, int32_t value) {
+ struct HAL_Value v = HAL_MakeEnum(value);
+ HAL_SetSimValue(handle, &v);
+}
+
+/**
+ * Sets a simulated value (boolean).
+ *
+ * @param handle simulated value handle
+ * @param value the value to set
+ */
+inline void HAL_SetSimValueBoolean(HAL_SimValueHandle handle, HAL_Bool value) {
+ struct HAL_Value v = HAL_MakeBoolean(value);
+ HAL_SetSimValue(handle, &v);
+}
+
+/** @} */
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#ifdef __cplusplus
+namespace hal {
+
+/**
+ * C++ wrapper around a HAL simulator value handle.
+ */
+class SimValue {
+ public:
+ /**
+ * Default constructor that results in an "empty" object that is false in
+ * a boolean context.
+ */
+ SimValue() = default;
+
+ /**
+ * Wraps a simulated value handle as returned by HAL_CreateSimValue().
+ *
+ * @param handle simulated value handle
+ */
+ /*implicit*/ SimValue(HAL_SimValueHandle val) // NOLINT(runtime/explicit)
+ : m_handle(val) {}
+
+ /**
+ * Determine if handle is empty. Should be used to optimize out code paths
+ * that are taken/not taken in simulation.
+ *
+ * @return False if handle is empty, true if handle is valid.
+ */
+ explicit operator bool() const { return m_handle != HAL_kInvalidHandle; }
+
+ /**
+ * Get the internal device handle.
+ *
+ * @return internal handle
+ */
+ operator HAL_SimValueHandle() const { return m_handle; }
+
+ /**
+ * Gets the simulated value.
+ *
+ * @return The current value
+ */
+ HAL_Value GetValue() const { return HAL_GetSimValue(m_handle); }
+
+ /**
+ * Sets the simulated value.
+ *
+ * @param value the value to set
+ */
+ void SetValue(const HAL_Value& value) { HAL_SetSimValue(m_handle, value); }
+
+ protected:
+ HAL_SimValueHandle m_handle = HAL_kInvalidHandle;
+};
+
+/**
+ * C++ wrapper around a HAL simulator double value handle.
+ */
+class SimDouble : public SimValue {
+ public:
+ /**
+ * Default constructor that results in an "empty" object that is false in
+ * a boolean context.
+ */
+ SimDouble() = default;
+
+ /**
+ * Wraps a simulated value handle as returned by HAL_CreateSimValueDouble().
+ *
+ * @param handle simulated value handle
+ */
+ /*implicit*/ SimDouble(HAL_SimValueHandle val) // NOLINT(runtime/explicit)
+ : SimValue(val) {}
+
+ /**
+ * Gets the simulated value.
+ *
+ * @return The current value
+ */
+ double Get() const { return HAL_GetSimValueDouble(m_handle); }
+
+ /**
+ * Sets the simulated value.
+ *
+ * @param value the value to set
+ */
+ void Set(double value) { HAL_SetSimValueDouble(m_handle, value); }
+};
+
+/**
+ * C++ wrapper around a HAL simulator enum value handle.
+ */
+class SimEnum : public SimValue {
+ public:
+ /**
+ * Default constructor that results in an "empty" object that is false in
+ * a boolean context.
+ */
+ SimEnum() = default;
+
+ /**
+ * Wraps a simulated value handle as returned by HAL_CreateSimValueEnum().
+ *
+ * @param handle simulated value handle
+ */
+ /*implicit*/ SimEnum(HAL_SimValueHandle val) // NOLINT(runtime/explicit)
+ : SimValue(val) {}
+
+ /**
+ * Gets the simulated value.
+ *
+ * @return The current value
+ */
+ int32_t Get() const { return HAL_GetSimValueEnum(m_handle); }
+
+ /**
+ * Sets the simulated value.
+ *
+ * @param value the value to set
+ */
+ void Set(int32_t value) { HAL_SetSimValueEnum(m_handle, value); }
+};
+
+/**
+ * C++ wrapper around a HAL simulator boolean value handle.
+ */
+class SimBoolean : public SimValue {
+ public:
+ /**
+ * Default constructor that results in an "empty" object that is false in
+ * a boolean context.
+ */
+ SimBoolean() = default;
+
+ /**
+ * Wraps a simulated value handle as returned by HAL_CreateSimValueBoolean().
+ *
+ * @param handle simulated value handle
+ */
+ /*implicit*/ SimBoolean(HAL_SimValueHandle val) // NOLINT(runtime/explicit)
+ : SimValue(val) {}
+
+ /**
+ * Gets the simulated value.
+ *
+ * @return The current value
+ */
+ bool Get() const { return HAL_GetSimValueBoolean(m_handle); }
+
+ /**
+ * Sets the simulated value.
+ *
+ * @param value the value to set
+ */
+ void Set(bool value) { HAL_SetSimValueBoolean(m_handle, value); }
+};
+
+/**
+ * A move-only C++ wrapper around a HAL simulator device handle.
+ */
+class SimDevice {
+ public:
+ /**
+ * Default constructor that results in an "empty" object that is false in
+ * a boolean context.
+ */
+ SimDevice() = default;
+
+ /**
+ * Creates a simulated device.
+ *
+ * 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]".
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @param name device name
+ */
+ explicit SimDevice(const char* name) : m_handle(HAL_CreateSimDevice(name)) {}
+
+ /**
+ * Creates a simulated device.
+ *
+ * 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.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @param name device name
+ * @param index device index number to append to name
+ */
+ SimDevice(const char* name, int index);
+
+ /**
+ * Creates a simulated device.
+ *
+ * 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.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @param name device name
+ * @param index device index number to append to name
+ * @param channel device channel number to append to name
+ */
+ SimDevice(const char* name, int index, int channel);
+
+ /**
+ * Wraps a simulated device handle as returned by HAL_CreateSimDevice().
+ *
+ * @param handle simulated device handle
+ */
+ /*implicit*/ SimDevice(HAL_SimDeviceHandle val) // NOLINT(runtime/explicit)
+ : m_handle(val) {}
+
+ ~SimDevice() {
+ if (m_handle != HAL_kInvalidHandle) HAL_FreeSimDevice(m_handle);
+ }
+
+ SimDevice(const SimDevice&) = delete;
+ SimDevice& operator=(const SimDevice&) = delete;
+
+ SimDevice(SimDevice&& rhs) : m_handle(rhs.m_handle) {
+ rhs.m_handle = HAL_kInvalidHandle;
+ }
+
+ SimDevice& operator=(SimDevice&& rhs) {
+ m_handle = rhs.m_handle;
+ rhs.m_handle = HAL_kInvalidHandle;
+ return *this;
+ }
+
+ /**
+ * Determine if handle is empty. Should be used to optimize out code paths
+ * that are taken/not taken in simulation.
+ *
+ * @return False if handle is empty, true if handle is valid.
+ */
+ explicit operator bool() const { return m_handle != HAL_kInvalidHandle; }
+
+ /**
+ * Get the internal device handle.
+ *
+ * @return internal handle
+ */
+ operator HAL_SimDeviceHandle() const { return m_handle; }
+
+ /**
+ * Creates a value on the simulated device.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @param name value name
+ * @param readonly if the value should not be written from simulation side
+ * @param initialValue initial value
+ * @return simulated value object
+ */
+ SimValue CreateValue(const char* name, bool readonly,
+ const HAL_Value& initialValue) {
+ return HAL_CreateSimValue(m_handle, name, readonly, &initialValue);
+ }
+
+ /**
+ * Creates a double value on the simulated device.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @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
+ */
+ SimDouble CreateDouble(const char* name, bool readonly, double initialValue) {
+ return HAL_CreateSimValueDouble(m_handle, name, readonly, initialValue);
+ }
+
+ /**
+ * Creates an enumerated value on the simulated device.
+ *
+ * Enumerated values are always in the range 0 to numOptions-1.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @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
+ */
+ SimEnum CreateEnum(const char* name, bool readonly,
+ std::initializer_list options,
+ int32_t initialValue) {
+ return HAL_CreateSimValueEnum(m_handle, name, readonly, options.size(),
+ const_cast(options.begin()),
+ initialValue);
+ }
+
+ /**
+ * Creates an enumerated value on the simulated device.
+ *
+ * Enumerated values are always in the range 0 to numOptions-1.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @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
+ */
+ SimEnum CreateEnum(const char* name, bool readonly,
+ wpi::ArrayRef options, int32_t initialValue) {
+ return HAL_CreateSimValueEnum(m_handle, name, readonly, options.size(),
+ const_cast(options.data()),
+ initialValue);
+ }
+
+ /**
+ * Creates a boolean value on the simulated device.
+ *
+ * If not in simulation, results in an "empty" object that evaluates to false
+ * in a boolean context.
+ *
+ * @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
+ */
+ SimBoolean CreateBoolean(const char* name, bool readonly, bool initialValue) {
+ return HAL_CreateSimValueBoolean(m_handle, name, readonly, initialValue);
+ }
+
+ protected:
+ HAL_SimDeviceHandle m_handle = HAL_kInvalidHandle;
+};
+
+} // namespace hal
+#endif // __cplusplus
diff --git a/hal/src/main/native/include/hal/Value.h b/hal/src/main/native/include/hal/Value.h
index 70e465ce02..578d989046 100644
--- a/hal/src/main/native/include/hal/Value.h
+++ b/hal/src/main/native/include/hal/Value.h
@@ -14,9 +14,9 @@ enum HAL_Type {
HAL_UNASSIGNED = 0,
HAL_BOOLEAN = 0x01,
HAL_DOUBLE = 0x02,
- HAL_ENUM = 0x16,
- HAL_INT = 0x32,
- HAL_LONG = 0x64,
+ HAL_ENUM = 0x04,
+ HAL_INT = 0x08,
+ HAL_LONG = 0x10,
};
/** HAL Entry Value. Note this is a typed union. */
diff --git a/hal/src/main/native/include/mockdata/SimDeviceData.h b/hal/src/main/native/include/mockdata/SimDeviceData.h
new file mode 100644
index 0000000000..44398d78e8
--- /dev/null
+++ b/hal/src/main/native/include/mockdata/SimDeviceData.h
@@ -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. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include "NotifyListener.h"
+#include "hal/Types.h"
+#include "hal/Value.h"
+
+typedef void (*HALSIM_SimDeviceCallback)(const char* name, void* param,
+ HAL_SimDeviceHandle handle);
+
+typedef void (*HALSIM_SimValueCallback)(const char* name, void* param,
+ HAL_SimValueHandle handle,
+ HAL_Bool readonly,
+ const struct HAL_Value* value);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int32_t HALSIM_RegisterSimDeviceCreatedCallback(
+ const char* prefix, void* param, HALSIM_SimDeviceCallback callback,
+ HAL_Bool initialNotify);
+
+void HALSIM_CancelSimDeviceCreatedCallback(int32_t uid);
+
+int32_t HALSIM_RegisterSimDeviceFreedCallback(
+ const char* prefix, void* param, HALSIM_SimDeviceCallback callback);
+
+void HALSIM_CancelSimDeviceFreedCallback(int32_t uid);
+
+HAL_SimDeviceHandle HALSIM_GetSimDeviceHandle(const char* name);
+
+const char* HALSIM_GetSimDeviceName(HAL_SimDeviceHandle handle);
+
+HAL_SimDeviceHandle HALSIM_GetSimValueDeviceHandle(HAL_SimValueHandle handle);
+
+void HALSIM_EnumerateSimDevices(const char* prefix, void* param,
+ HALSIM_SimDeviceCallback callback);
+
+int32_t HALSIM_RegisterSimValueCreatedCallback(HAL_SimDeviceHandle device,
+ void* param,
+ HALSIM_SimValueCallback callback,
+ HAL_Bool initialNotify);
+
+void HALSIM_CancelSimValueCreatedCallback(int32_t uid);
+
+int32_t HALSIM_RegisterSimValueChangedCallback(HAL_SimValueHandle handle,
+ void* param,
+ HALSIM_SimValueCallback callback,
+ HAL_Bool initialNotify);
+
+void HALSIM_CancelSimValueChangedCallback(int32_t uid);
+
+HAL_SimValueHandle HALSIM_GetSimValueHandle(HAL_SimDeviceHandle device,
+ const char* name);
+
+void HALSIM_EnumerateSimValues(HAL_SimDeviceHandle device, void* param,
+ HALSIM_SimValueCallback callback);
+
+const char** HALSIM_GetSimValueEnumOptions(HAL_SimValueHandle handle,
+ int32_t* numOptions);
+
+void HALSIM_ResetSimDeviceData(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/hal/src/main/native/sim/HAL.cpp b/hal/src/main/native/sim/HAL.cpp
index 191cfdcbb0..cf019cfcc4 100644
--- a/hal/src/main/native/sim/HAL.cpp
+++ b/hal/src/main/native/sim/HAL.cpp
@@ -41,6 +41,7 @@ void InitializeHAL() {
InitializePWMData();
InitializeRelayData();
InitializeRoboRioData();
+ InitializeSimDeviceData();
InitializeSPIAccelerometerData();
InitializeSPIData();
InitializeAccelerometer();
@@ -69,6 +70,7 @@ void InitializeHAL() {
InitializePWM();
InitializeRelay();
InitializeSerialPort();
+ InitializeSimDevice();
InitializeSolenoid();
InitializeSPI();
InitializeThreads();
diff --git a/hal/src/main/native/sim/HALInitializer.h b/hal/src/main/native/sim/HALInitializer.h
index c765042f3f..4c91b404fe 100644
--- a/hal/src/main/native/sim/HALInitializer.h
+++ b/hal/src/main/native/sim/HALInitializer.h
@@ -35,6 +35,7 @@ extern void InitializePDPData();
extern void InitializePWMData();
extern void InitializeRelayData();
extern void InitializeRoboRioData();
+extern void InitializeSimDeviceData();
extern void InitializeSPIAccelerometerData();
extern void InitializeSPIData();
extern void InitializeAccelerometer();
@@ -64,6 +65,7 @@ extern void InitializePower();
extern void InitializePWM();
extern void InitializeRelay();
extern void InitializeSerialPort();
+extern void InitializeSimDevice();
extern void InitializeSolenoid();
extern void InitializeSPI();
extern void InitializeThreads();
diff --git a/hal/src/main/native/sim/SimDevice.cpp b/hal/src/main/native/sim/SimDevice.cpp
new file mode 100644
index 0000000000..a8f8f8048e
--- /dev/null
+++ b/hal/src/main/native/sim/SimDevice.cpp
@@ -0,0 +1,75 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#include "hal/SimDevice.h"
+
+#include
+#include
+
+#include "HALInitializer.h"
+#include "mockdata/SimDeviceDataInternal.h"
+
+using namespace hal;
+
+namespace hal {
+namespace init {
+void InitializeSimDevice() {}
+} // namespace init
+} // namespace hal
+
+extern "C" {
+
+HAL_SimDeviceHandle HAL_CreateSimDevice(const char* name) {
+ hal::init::CheckInit();
+ return SimSimDeviceData->CreateDevice(name);
+}
+
+void HAL_FreeSimDevice(HAL_SimDeviceHandle handle) {
+ SimSimDeviceData->FreeDevice(handle);
+}
+
+HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
+ const char* name, HAL_Bool readonly,
+ const struct HAL_Value* initialValue) {
+ return SimSimDeviceData->CreateValue(device, name, readonly, 0, nullptr,
+ *initialValue);
+}
+
+HAL_SimValueHandle HAL_CreateSimValueEnum(HAL_SimDeviceHandle device,
+ const char* name, HAL_Bool readonly,
+ int32_t numOptions,
+ const char** options,
+ int32_t initialValue) {
+ return SimSimDeviceData->CreateValue(device, name, readonly, numOptions,
+ options, HAL_MakeEnum(initialValue));
+}
+
+void HAL_GetSimValue(HAL_SimValueHandle handle, struct HAL_Value* value) {
+ *value = SimSimDeviceData->GetValue(handle);
+}
+
+void HAL_SetSimValue(HAL_SimValueHandle handle, const struct HAL_Value* value) {
+ SimSimDeviceData->SetValue(handle, *value);
+}
+
+hal::SimDevice::SimDevice(const char* name, int index) {
+ wpi::SmallString<128> fullname;
+ wpi::raw_svector_ostream os(fullname);
+ os << name << '[' << index << ']';
+
+ m_handle = HAL_CreateSimDevice(fullname.c_str());
+}
+
+hal::SimDevice::SimDevice(const char* name, int index, int channel) {
+ wpi::SmallString<128> fullname;
+ wpi::raw_svector_ostream os(fullname);
+ os << name << '[' << index << ',' << channel << ']';
+
+ m_handle = HAL_CreateSimDevice(fullname.c_str());
+}
+
+} // extern "C"
diff --git a/hal/src/main/native/sim/jni/SimDeviceDataJNI.cpp b/hal/src/main/native/sim/jni/SimDeviceDataJNI.cpp
new file mode 100644
index 0000000000..f6cd05e7e9
--- /dev/null
+++ b/hal/src/main/native/sim/jni/SimDeviceDataJNI.cpp
@@ -0,0 +1,573 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#include "SimDeviceDataJNI.h"
+
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+
+#include "SimulatorJNI.h"
+#include "edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI.h"
+#include "mockdata/SimDeviceData.h"
+
+using namespace wpi::java;
+
+static JClass simDeviceInfoCls;
+static JClass simValueInfoCls;
+static JClass simDeviceCallbackCls;
+static JClass simValueCallbackCls;
+static jmethodID simDeviceCallbackCallback;
+static jmethodID simValueCallbackCallback;
+
+namespace {
+
+struct DeviceInfo {
+ DeviceInfo(const char* name_, HAL_SimDeviceHandle handle_)
+ : name{name_}, handle{handle_} {}
+ std::string name;
+ HAL_SimValueHandle handle;
+
+ jobject MakeJava(JNIEnv* env) const;
+ void CallJava(JNIEnv* env, jobject callobj) const;
+};
+
+struct ValueInfo {
+ ValueInfo(const char* name_, HAL_SimValueHandle handle_, bool readonly_,
+ const HAL_Value& value_)
+ : name{name_}, handle{handle_}, readonly{readonly_}, value{value_} {}
+ std::string name;
+ HAL_SimValueHandle handle;
+ bool readonly;
+ HAL_Value value;
+
+ jobject MakeJava(JNIEnv* env) const;
+ void CallJava(JNIEnv* env, jobject callobj) const;
+
+ private:
+ std::pair ToValue12() const;
+};
+
+} // namespace
+
+jobject DeviceInfo::MakeJava(JNIEnv* env) const {
+ static jmethodID func =
+ env->GetMethodID(simDeviceInfoCls, "", "(Ljava/lang/String;I)V");
+ return env->NewObject(simDeviceInfoCls, func, MakeJString(env, name),
+ (jint)handle);
+}
+
+void DeviceInfo::CallJava(JNIEnv* env, jobject callobj) const {
+ env->CallVoidMethod(callobj, simDeviceCallbackCallback,
+ MakeJString(env, name), (jint)handle);
+}
+
+std::pair ValueInfo::ToValue12() const {
+ jlong value1 = 0;
+ jdouble value2 = 0.0;
+ switch (value.type) {
+ case HAL_BOOLEAN:
+ value1 = value.data.v_boolean;
+ break;
+ case HAL_DOUBLE:
+ value2 = value.data.v_double;
+ break;
+ case HAL_ENUM:
+ value1 = value.data.v_enum;
+ break;
+ case HAL_INT:
+ value1 = value.data.v_int;
+ break;
+ case HAL_LONG:
+ value1 = value.data.v_long;
+ break;
+ default:
+ break;
+ }
+ return std::pair(value1, value2);
+}
+
+jobject ValueInfo::MakeJava(JNIEnv* env) const {
+ static jmethodID func =
+ env->GetMethodID(simValueInfoCls, "", "(Ljava/lang/String;IZIJD)V");
+ auto [value1, value2] = ToValue12();
+ return env->NewObject(simValueInfoCls, func, MakeJString(env, name),
+ (jint)handle, (jboolean)readonly, (jint)value.type,
+ value1, value2);
+}
+
+void ValueInfo::CallJava(JNIEnv* env, jobject callobj) const {
+ auto [value1, value2] = ToValue12();
+ env->CallVoidMethod(callobj, simValueCallbackCallback, MakeJString(env, name),
+ (jint)handle, (jboolean)readonly, (jint)value.type,
+ value1, value2);
+}
+
+namespace {
+
+class CallbackStore {
+ public:
+ explicit CallbackStore(JNIEnv* env, jobject obj) : m_call{env, obj} {}
+ ~CallbackStore() {
+ if (m_cancelCallback) m_cancelCallback();
+ }
+
+ void SetCancel(std::function cancelCallback) {
+ m_cancelCallback = std::move(cancelCallback);
+ }
+ void Free(JNIEnv* env) { m_call.free(env); }
+ jobject Get() const { return m_call; }
+
+ private:
+ wpi::java::JGlobal m_call;
+ std::function m_cancelCallback;
+};
+
+class CallbackThreadJNI : public wpi::SafeThread {
+ public:
+ void Main();
+
+ using DeviceCalls =
+ std::vector, DeviceInfo>>;
+ DeviceCalls m_deviceCalls;
+ using ValueCalls =
+ std::vector, ValueInfo>>;
+ ValueCalls m_valueCalls;
+
+ wpi::UidVector, 4> m_callbacks;
+};
+
+class CallbackJNI {
+ public:
+ static CallbackJNI& GetInstance() {
+ static CallbackJNI inst;
+ return inst;
+ }
+ void SendDevice(int32_t callback, DeviceInfo info);
+ void SendValue(int32_t callback, ValueInfo info);
+
+ std::pair> AllocateCallback(
+ JNIEnv* env, jobject obj);
+
+ void FreeCallback(JNIEnv* env, int32_t uid);
+
+ private:
+ CallbackJNI() { m_owner.Start(); }
+
+ wpi::SafeThreadOwner m_owner;
+};
+
+} // namespace
+
+void CallbackThreadJNI::Main() {
+ JNIEnv* env;
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_2;
+ args.name = const_cast("SimDeviceCallback");
+ args.group = nullptr;
+ jint rs = sim::GetJVM()->AttachCurrentThreadAsDaemon(
+ reinterpret_cast(&env), &args);
+ if (rs != JNI_OK) return;
+
+ DeviceCalls deviceCalls;
+ ValueCalls valueCalls;
+
+ std::unique_lock lock(m_mutex);
+ while (m_active) {
+ m_cond.wait(lock, [&] { return !m_active; });
+ if (!m_active) break;
+
+ deviceCalls.swap(m_deviceCalls);
+ valueCalls.swap(m_valueCalls);
+
+ lock.unlock(); // don't hold mutex during callback execution
+
+ for (auto&& call : deviceCalls) {
+ if (auto store = call.first.lock()) {
+ if (jobject callobj = store->Get()) {
+ call.second.CallJava(env, callobj);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ }
+ }
+ }
+
+ for (auto&& call : valueCalls) {
+ if (auto store = call.first.lock()) {
+ if (jobject callobj = store->Get()) {
+ call.second.CallJava(env, callobj);
+ if (env->ExceptionCheck()) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ }
+ }
+ }
+
+ deviceCalls.clear();
+ valueCalls.clear();
+
+ lock.lock();
+ }
+
+ // free global references
+ for (auto&& callback : m_callbacks) callback->Free(env);
+
+ sim::GetJVM()->DetachCurrentThread();
+}
+
+void CallbackJNI::SendDevice(int32_t callback, DeviceInfo info) {
+ auto thr = m_owner.GetThread();
+ if (!thr) return;
+ thr->m_deviceCalls.emplace_back(thr->m_callbacks[callback], std::move(info));
+ thr->m_cond.notify_one();
+}
+
+void CallbackJNI::SendValue(int32_t callback, ValueInfo info) {
+ auto thr = m_owner.GetThread();
+ if (!thr) return;
+ thr->m_valueCalls.emplace_back(thr->m_callbacks[callback], std::move(info));
+ thr->m_cond.notify_one();
+}
+
+std::pair>
+CallbackJNI::AllocateCallback(JNIEnv* env, jobject obj) {
+ auto thr = m_owner.GetThread();
+ if (!thr) return std::pair(0, nullptr);
+ auto store = std::make_shared(env, obj);
+ return std::pair(thr->m_callbacks.emplace_back(store) + 1, store);
+}
+
+void CallbackJNI::FreeCallback(JNIEnv* env, int32_t uid) {
+ auto thr = m_owner.GetThread();
+ if (!thr) return;
+ if (uid <= 0 || static_cast(uid) >= thr->m_callbacks.size()) return;
+ --uid;
+ auto store = std::move(thr->m_callbacks[uid]);
+ thr->m_callbacks.erase(uid);
+ store->Free(env);
+}
+
+namespace sim {
+
+bool InitializeSimDeviceDataJNI(JNIEnv* env) {
+ simDeviceInfoCls = JClass(
+ env, "edu/wpi/first/hal/sim/mockdata/SimDeviceDataJNI$SimDeviceInfo");
+ if (!simDeviceInfoCls) return false;
+
+ simValueInfoCls = JClass(
+ env, "edu/wpi/first/hal/sim/mockdata/SimDeviceDataJNI$SimValueInfo");
+ if (!simValueInfoCls) return false;
+
+ simDeviceCallbackCls = JClass(env, "edu/wpi/first/hal/sim/SimDeviceCallback");
+ if (!simDeviceCallbackCls) return false;
+
+ simDeviceCallbackCallback = env->GetMethodID(simDeviceCallbackCls, "callback",
+ "(Ljava/lang/String;I)V");
+ if (!simDeviceCallbackCallback) return false;
+
+ simValueCallbackCls = JClass(env, "edu/wpi/first/hal/sim/SimValueCallback");
+ if (!simValueCallbackCls) return false;
+
+ simValueCallbackCallback = env->GetMethodID(
+ simValueCallbackCls, "callbackNative", "(Ljava/lang/String;IZIJD)V");
+ if (!simValueCallbackCallback) return false;
+
+ return true;
+}
+
+void FreeSimDeviceDataJNI(JNIEnv* env) {
+ simDeviceInfoCls.free(env);
+ simValueInfoCls.free(env);
+ simDeviceCallbackCls.free(env);
+ simValueCallbackCls.free(env);
+}
+
+} // namespace sim
+
+extern "C" {
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: registerSimDeviceCreatedCallback
+ * Signature: (Ljava/lang/String;Ljava/lang/Object;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_registerSimDeviceCreatedCallback
+ (JNIEnv* env, jclass, jstring prefix, jobject callback,
+ jboolean initialNotify)
+{
+ auto [uid, store] =
+ CallbackJNI::GetInstance().AllocateCallback(env, callback);
+ int32_t cuid = HALSIM_RegisterSimDeviceCreatedCallback(
+ JStringRef{env, prefix}.c_str(),
+ reinterpret_cast(static_cast(uid)),
+ [](const char* name, void* param, HAL_SimDeviceHandle handle) {
+ int32_t uid = reinterpret_cast(param);
+ CallbackJNI::GetInstance().SendDevice(uid, DeviceInfo{name, handle});
+ },
+ initialNotify);
+ store->SetCancel([cuid] { HALSIM_CancelSimDeviceCreatedCallback(cuid); });
+ return uid;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: cancelSimDeviceCreatedCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_cancelSimDeviceCreatedCallback
+ (JNIEnv* env, jclass, jint uid)
+{
+ CallbackJNI::GetInstance().FreeCallback(env, uid);
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: registerSimDeviceFreedCallback
+ * Signature: (Ljava/lang/String;Ljava/lang/Object;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_registerSimDeviceFreedCallback
+ (JNIEnv* env, jclass, jstring prefix, jobject callback)
+{
+ auto [uid, store] =
+ CallbackJNI::GetInstance().AllocateCallback(env, callback);
+ int32_t cuid = HALSIM_RegisterSimDeviceFreedCallback(
+ JStringRef{env, prefix}.c_str(),
+ reinterpret_cast(static_cast(uid)),
+ [](const char* name, void* param, HAL_SimDeviceHandle handle) {
+ int32_t uid = reinterpret_cast(param);
+ CallbackJNI::GetInstance().SendDevice(uid, DeviceInfo{name, handle});
+ });
+ store->SetCancel([cuid] { HALSIM_CancelSimDeviceFreedCallback(cuid); });
+ return uid;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: cancelSimDeviceFreedCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_cancelSimDeviceFreedCallback
+ (JNIEnv* env, jclass, jint uid)
+{
+ CallbackJNI::GetInstance().FreeCallback(env, uid);
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: getSimDeviceHandle
+ * Signature: (Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_getSimDeviceHandle
+ (JNIEnv* env, jclass, jstring name)
+{
+ return HALSIM_GetSimDeviceHandle(JStringRef{env, name}.c_str());
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: getSimValueDeviceHandle
+ * Signature: (I)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_getSimValueDeviceHandle
+ (JNIEnv*, jclass, jint handle)
+{
+ return HALSIM_GetSimValueDeviceHandle(handle);
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: enumerateSimDevices
+ * Signature: (Ljava/lang/String;)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_enumerateSimDevices
+ (JNIEnv* env, jclass, jstring prefix)
+{
+ // get values
+ std::vector arr;
+ HALSIM_EnumerateSimDevices(
+ JStringRef{env, prefix}.c_str(), &arr,
+ [](const char* name, void* param, HAL_SimDeviceHandle handle) {
+ auto arr = static_cast*>(param);
+ arr->emplace_back(name, handle);
+ });
+
+ // convert to java
+ size_t numElems = arr.size();
+ jobjectArray jarr =
+ env->NewObjectArray(arr.size(), simDeviceInfoCls, nullptr);
+ if (!jarr) return nullptr;
+ for (size_t i = 0; i < numElems; ++i) {
+ JLocal elem{env, arr[i].MakeJava(env)};
+ env->SetObjectArrayElement(jarr, i, elem.obj());
+ }
+ return jarr;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: registerSimValueCreatedCallback
+ * Signature: (ILjava/lang/Object;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_registerSimValueCreatedCallback
+ (JNIEnv* env, jclass, jint device, jobject callback, jboolean initialNotify)
+{
+ auto [uid, store] =
+ CallbackJNI::GetInstance().AllocateCallback(env, callback);
+ int32_t cuid = HALSIM_RegisterSimValueCreatedCallback(
+ device, reinterpret_cast(static_cast(uid)),
+ [](const char* name, void* param, HAL_SimValueHandle handle,
+ HAL_Bool readonly, const HAL_Value* value) {
+ int32_t uid = reinterpret_cast(param);
+ CallbackJNI::GetInstance().SendValue(
+ uid, ValueInfo{name, handle, static_cast(readonly), *value});
+ },
+ initialNotify);
+ store->SetCancel([cuid] { HALSIM_CancelSimValueCreatedCallback(cuid); });
+ return uid;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: cancelSimValueCreatedCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_cancelSimValueCreatedCallback
+ (JNIEnv* env, jclass, jint uid)
+{
+ CallbackJNI::GetInstance().FreeCallback(env, uid);
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: registerSimValueChangedCallback
+ * Signature: (ILjava/lang/Object;Z)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_registerSimValueChangedCallback
+ (JNIEnv* env, jclass, jint handle, jobject callback, jboolean initialNotify)
+{
+ auto [uid, store] =
+ CallbackJNI::GetInstance().AllocateCallback(env, callback);
+ int32_t cuid = HALSIM_RegisterSimValueChangedCallback(
+ handle, reinterpret_cast(static_cast(uid)),
+ [](const char* name, void* param, HAL_SimValueHandle handle,
+ HAL_Bool readonly, const HAL_Value* value) {
+ int32_t uid = reinterpret_cast(param);
+ CallbackJNI::GetInstance().SendValue(
+ uid, ValueInfo{name, handle, static_cast(readonly), *value});
+ },
+ initialNotify);
+ store->SetCancel([cuid] { HALSIM_CancelSimValueChangedCallback(cuid); });
+ return uid;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: cancelSimValueChangedCallback
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_cancelSimValueChangedCallback
+ (JNIEnv* env, jclass, jint uid)
+{
+ CallbackJNI::GetInstance().FreeCallback(env, uid);
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: getSimValueHandle
+ * Signature: (ILjava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_getSimValueHandle
+ (JNIEnv* env, jclass, jint device, jstring name)
+{
+ return HALSIM_GetSimValueHandle(device, JStringRef{env, name}.c_str());
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: enumerateSimValues
+ * Signature: (I)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_enumerateSimValues
+ (JNIEnv* env, jclass, jint device)
+{
+ // get values
+ std::vector arr;
+ HALSIM_EnumerateSimValues(
+ device, &arr,
+ [](const char* name, void* param, HAL_SimValueHandle handle,
+ HAL_Bool readonly, const HAL_Value* value) {
+ auto arr = static_cast*>(param);
+ arr->emplace_back(name, handle, readonly, *value);
+ });
+
+ // convert to java
+ size_t numElems = arr.size();
+ jobjectArray jarr = env->NewObjectArray(arr.size(), simValueInfoCls, nullptr);
+ if (!jarr) return nullptr;
+ for (size_t i = 0; i < numElems; ++i) {
+ JLocal elem{env, arr[i].MakeJava(env)};
+ env->SetObjectArrayElement(jarr, i, elem.obj());
+ }
+ return jarr;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: getSimValueEnumOptions
+ * Signature: (I)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_getSimValueEnumOptions
+ (JNIEnv* env, jclass, jint handle)
+{
+ static JClass stringCls{env, "java/lang/String"};
+ if (!stringCls) return nullptr;
+ int32_t numElems = 0;
+ const char** elems = HALSIM_GetSimValueEnumOptions(handle, &numElems);
+ jobjectArray jarr = env->NewObjectArray(numElems, stringCls, nullptr);
+ if (!jarr) return nullptr;
+ for (int32_t i = 0; i < numElems; ++i) {
+ JLocal elem{env, MakeJString(env, elems[i])};
+ env->SetObjectArrayElement(jarr, i, elem.obj());
+ }
+ return jarr;
+}
+
+/*
+ * Class: edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI
+ * Method: resetSimDeviceData
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL
+Java_edu_wpi_first_hal_sim_mockdata_SimDeviceDataJNI_resetSimDeviceData
+ (JNIEnv*, jclass)
+{
+ HALSIM_ResetSimDeviceData();
+}
+
+} // extern "C"
diff --git a/hal/src/main/native/sim/jni/SimDeviceDataJNI.h b/hal/src/main/native/sim/jni/SimDeviceDataJNI.h
new file mode 100644
index 0000000000..56f6d9b322
--- /dev/null
+++ b/hal/src/main/native/sim/jni/SimDeviceDataJNI.h
@@ -0,0 +1,15 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include
+
+namespace sim {
+bool InitializeSimDeviceDataJNI(JNIEnv* env);
+void FreeSimDeviceDataJNI(JNIEnv* env);
+} // namespace sim
diff --git a/hal/src/main/native/sim/jni/SimulatorJNI.cpp b/hal/src/main/native/sim/jni/SimulatorJNI.cpp
index 32b6110725..bba8f01521 100644
--- a/hal/src/main/native/sim/jni/SimulatorJNI.cpp
+++ b/hal/src/main/native/sim/jni/SimulatorJNI.cpp
@@ -7,9 +7,12 @@
#include "SimulatorJNI.h"
+#include
+
#include "BufferCallbackStore.h"
#include "CallbackStore.h"
#include "ConstBufferCallbackStore.h"
+#include "SimDeviceDataJNI.h"
#include "SpiReadAutoReceiveBufferCallbackStore.h"
#include "edu_wpi_first_hal_sim_mockdata_SimulatorJNI.h"
#include "hal/HAL.h"
@@ -19,7 +22,6 @@
using namespace wpi::java;
static JavaVM* jvm = nullptr;
-static JClass simValueCls;
static JClass notifyCallbackCls;
static JClass bufferCallbackCls;
static JClass constBufferCallbackCls;
@@ -37,9 +39,6 @@ jint SimOnLoad(JavaVM* vm, void* reserved) {
if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK)
return JNI_ERR;
- simValueCls = JClass(env, "edu/wpi/first/hal/sim/SimValue");
- if (!simValueCls) return JNI_ERR;
-
notifyCallbackCls = JClass(env, "edu/wpi/first/hal/sim/NotifyCallback");
if (!notifyCallbackCls) return JNI_ERR;
@@ -75,6 +74,7 @@ jint SimOnLoad(JavaVM* vm, void* reserved) {
InitializeBufferStore();
InitializeConstBufferStore();
InitializeSpiBufferStore();
+ if (!InitializeSimDeviceDataJNI(env)) return JNI_ERR;
return JNI_VERSION_1_6;
}
@@ -84,11 +84,11 @@ void SimOnUnload(JavaVM* vm, void* reserved) {
if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK)
return;
- simValueCls.free(env);
notifyCallbackCls.free(env);
bufferCallbackCls.free(env);
constBufferCallbackCls.free(env);
spiReadAutoReceiveBufferCallbackCls.free(env);
+ FreeSimDeviceDataJNI(env);
jvm = nullptr;
}
diff --git a/hal/src/main/native/sim/jni/SimulatorJNI.h b/hal/src/main/native/sim/jni/SimulatorJNI.h
index de74a1cd32..8680396955 100644
--- a/hal/src/main/native/sim/jni/SimulatorJNI.h
+++ b/hal/src/main/native/sim/jni/SimulatorJNI.h
@@ -7,10 +7,7 @@
#pragma once
-#include
-
#include "hal/Types.h"
-#include "hal/Value.h"
#include "jni.h"
typedef HAL_Handle SIM_JniHandle;
diff --git a/hal/src/main/native/sim/mockdata/SimDeviceData.cpp b/hal/src/main/native/sim/mockdata/SimDeviceData.cpp
new file mode 100644
index 0000000000..61407ee37b
--- /dev/null
+++ b/hal/src/main/native/sim/mockdata/SimDeviceData.cpp
@@ -0,0 +1,413 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#include "mockdata/SimDeviceData.h" // NOLINT(build/include_order)
+
+#include
+
+#include "SimDeviceDataInternal.h"
+
+using namespace hal;
+
+namespace hal {
+namespace init {
+void InitializeSimDeviceData() {
+ static SimDeviceData sdd;
+ ::hal::SimSimDeviceData = &sdd;
+}
+} // namespace init
+} // namespace hal
+
+SimDeviceData* hal::SimSimDeviceData;
+
+SimDeviceData::Device* SimDeviceData::LookupDevice(HAL_SimDeviceHandle handle) {
+ if (handle <= 0) return nullptr;
+ --handle;
+ if (static_cast(handle) >= m_devices.size() || !m_devices[handle])
+ return nullptr;
+ return m_devices[handle].get();
+}
+
+SimDeviceData::Value* SimDeviceData::LookupValue(HAL_SimValueHandle handle) {
+ if (handle <= 0) return nullptr;
+
+ // look up device
+ Device* deviceImpl = LookupDevice(handle >> 16);
+
+ // look up value
+ handle &= 0xffff;
+ --handle;
+ if (static_cast(handle) >= deviceImpl->values.size() ||
+ !deviceImpl->values[handle])
+ return nullptr;
+
+ return deviceImpl->values[handle].get();
+}
+
+HAL_SimDeviceHandle SimDeviceData::CreateDevice(const char* name) {
+ std::scoped_lock lock(m_mutex);
+
+ // check for duplicates and don't overwrite them
+ auto it = m_deviceMap.find(name);
+ if (it != m_deviceMap.end()) return 0;
+
+ // don't allow more than 4096 devices (limit driven by 12-bit allocation in
+ // value changed callback uid)
+ if (m_devices.size() >= 4095) return 0;
+
+ // create and save
+ auto deviceImpl = std::make_shared(name);
+ HAL_SimDeviceHandle deviceHandle = m_devices.emplace_back(deviceImpl) + 1;
+ deviceImpl->handle = deviceHandle;
+ m_deviceMap[name] = deviceImpl;
+
+ // notify callbacks
+ m_deviceCreated(name, deviceHandle);
+
+ return deviceHandle;
+}
+
+void SimDeviceData::FreeDevice(HAL_SimDeviceHandle handle) {
+ std::scoped_lock lock(m_mutex);
+ --handle;
+
+ // see if it exists
+ if (handle < 0 || static_cast(handle) >= m_devices.size()) return;
+ auto deviceImpl = std::move(m_devices[handle]);
+ if (!deviceImpl) return;
+
+ // remove from map
+ m_deviceMap.erase(deviceImpl->name);
+
+ // remove from vector
+ m_devices.erase(handle);
+
+ // notify callbacks
+ m_deviceFreed(deviceImpl->name.c_str(), handle + 1);
+}
+
+HAL_SimValueHandle SimDeviceData::CreateValue(HAL_SimDeviceHandle device,
+ const char* name, bool readonly,
+ int32_t numOptions,
+ const char** options,
+ const HAL_Value& initialValue) {
+ std::scoped_lock lock(m_mutex);
+
+ // look up device
+ Device* deviceImpl = LookupDevice(device);
+ if (!deviceImpl) return 0;
+
+ // check for duplicates and don't overwrite them
+ auto it = deviceImpl->valueMap.find(name);
+ if (it != deviceImpl->valueMap.end()) return 0;
+
+ // don't allow more than 4096 values per device (limit driven by 12-bit
+ // allocation in value changed callback uid)
+ if (deviceImpl->values.size() >= 4095) return 0;
+
+ // create and save; encode device into handle
+ auto valueImplPtr = std::make_unique(name, readonly, initialValue);
+ Value* valueImpl = valueImplPtr.get();
+ HAL_SimValueHandle valueHandle =
+ (device << 16) |
+ (deviceImpl->values.emplace_back(std::move(valueImplPtr)) + 1);
+ valueImpl->handle = valueHandle;
+ // copy options (if any provided)
+ if (numOptions > 0 && options) {
+ valueImpl->enumOptions.reserve(numOptions);
+ valueImpl->cstrEnumOptions.reserve(numOptions);
+ for (int32_t i = 0; i < numOptions; ++i) {
+ valueImpl->enumOptions.emplace_back(options[i]);
+ // point to our copy of the string, not the passed-in one
+ valueImpl->cstrEnumOptions.emplace_back(
+ valueImpl->enumOptions.back().c_str());
+ }
+ }
+ deviceImpl->valueMap[name] = valueImpl;
+
+ // notify callbacks
+ deviceImpl->valueCreated(name, valueHandle, readonly, &initialValue);
+
+ return valueHandle;
+}
+
+HAL_Value SimDeviceData::GetValue(HAL_SimValueHandle handle) {
+ std::scoped_lock lock(m_mutex);
+ Value* valueImpl = LookupValue(handle);
+
+ if (!valueImpl) {
+ HAL_Value value;
+ value.data.v_int = 0;
+ value.type = HAL_UNASSIGNED;
+ return value;
+ }
+
+ return valueImpl->value;
+}
+
+void SimDeviceData::SetValue(HAL_SimValueHandle handle,
+ const HAL_Value& value) {
+ std::scoped_lock lock(m_mutex);
+ Value* valueImpl = LookupValue(handle);
+ if (!valueImpl) return;
+
+ valueImpl->value = value;
+
+ // notify callbacks
+ valueImpl->changed(valueImpl->name.c_str(), valueImpl->handle,
+ valueImpl->readonly, &value);
+}
+
+int32_t SimDeviceData::RegisterDeviceCreatedCallback(
+ const char* prefix, void* param, HALSIM_SimDeviceCallback callback,
+ bool initialNotify) {
+ std::scoped_lock lock(m_mutex);
+
+ // register callback
+ int32_t index = m_deviceCreated.Register(prefix, param, callback);
+
+ // initial notifications
+ if (initialNotify) {
+ for (auto&& device : m_devices)
+ callback(device->name.c_str(), param, device->handle);
+ }
+
+ return index;
+}
+
+void SimDeviceData::CancelDeviceCreatedCallback(int32_t uid) {
+ if (uid <= 0) return;
+ std::scoped_lock lock(m_mutex);
+ m_deviceCreated.Cancel(uid);
+}
+
+int32_t SimDeviceData::RegisterDeviceFreedCallback(
+ const char* prefix, void* param, HALSIM_SimDeviceCallback callback) {
+ std::scoped_lock lock(m_mutex);
+ return m_deviceFreed.Register(prefix, param, callback);
+}
+
+void SimDeviceData::CancelDeviceFreedCallback(int32_t uid) {
+ if (uid <= 0) return;
+ std::scoped_lock lock(m_mutex);
+ m_deviceFreed.Cancel(uid);
+}
+
+HAL_SimDeviceHandle SimDeviceData::GetDeviceHandle(const char* name) {
+ std::scoped_lock lock(m_mutex);
+ auto it = m_deviceMap.find(name);
+ if (it == m_deviceMap.end()) return 0;
+ if (auto deviceImpl = it->getValue().lock())
+ return deviceImpl->handle;
+ else
+ return 0;
+}
+
+const char* SimDeviceData::GetDeviceName(HAL_SimDeviceHandle handle) {
+ std::scoped_lock lock(m_mutex);
+
+ // look up device
+ Device* deviceImpl = LookupDevice(handle);
+ if (!deviceImpl) return nullptr;
+
+ return deviceImpl->name.c_str();
+}
+
+void SimDeviceData::EnumerateDevices(const char* prefix, void* param,
+ HALSIM_SimDeviceCallback callback) {
+ std::scoped_lock lock(m_mutex);
+ for (auto&& device : m_devices) {
+ if (wpi::StringRef{device->name}.startswith(prefix))
+ callback(device->name.c_str(), param, device->handle);
+ }
+}
+
+int32_t SimDeviceData::RegisterValueCreatedCallback(
+ HAL_SimDeviceHandle device, void* param, HALSIM_SimValueCallback callback,
+ bool initialNotify) {
+ std::scoped_lock lock(m_mutex);
+ Device* deviceImpl = LookupDevice(device);
+ if (!deviceImpl) return -1;
+
+ // register callback
+ int32_t index = deviceImpl->valueCreated.Register(callback, param);
+
+ // initial notifications
+ if (initialNotify) {
+ for (auto&& value : deviceImpl->values)
+ callback(value->name.c_str(), param, value->handle, value->readonly,
+ &value->value);
+ }
+
+ // encode device into uid
+ return (device << 16) | (index & 0xffff);
+}
+
+void SimDeviceData::CancelValueCreatedCallback(int32_t uid) {
+ if (uid <= 0) return;
+ std::scoped_lock lock(m_mutex);
+ Device* deviceImpl = LookupDevice(uid >> 16);
+ if (!deviceImpl) return;
+ deviceImpl->valueCreated.Cancel(uid & 0xffff);
+}
+
+int32_t SimDeviceData::RegisterValueChangedCallback(
+ HAL_SimValueHandle handle, void* param, HALSIM_SimValueCallback callback,
+ bool initialNotify) {
+ std::scoped_lock lock(m_mutex);
+ Value* valueImpl = LookupValue(handle);
+ if (!valueImpl) return -1;
+
+ // register callback
+ int32_t index = valueImpl->changed.Register(callback, param);
+
+ // initial notification
+ if (initialNotify)
+ callback(valueImpl->name.c_str(), param, valueImpl->handle,
+ valueImpl->readonly, &valueImpl->value);
+
+ // encode device and value into uid
+ return (((handle >> 16) & 0xfff) << 19) | ((handle & 0xfff) << 7) |
+ (index & 0x7f);
+}
+
+void SimDeviceData::CancelValueChangedCallback(int32_t uid) {
+ if (uid <= 0) return;
+ std::scoped_lock lock(m_mutex);
+ Value* valueImpl = LookupValue(((uid >> 19) << 16) | ((uid >> 7) & 0xfff));
+ if (!valueImpl) return;
+ valueImpl->changed.Cancel(uid & 0x7f);
+}
+
+HAL_SimValueHandle SimDeviceData::GetValueHandle(HAL_SimDeviceHandle device,
+ const char* name) {
+ std::scoped_lock lock(m_mutex);
+ Device* deviceImpl = LookupDevice(device);
+ if (!deviceImpl) return 0;
+
+ // lookup value
+ auto it = deviceImpl->valueMap.find(name);
+ if (it == deviceImpl->valueMap.end()) return 0;
+ if (!it->getValue()) return 0;
+ return it->getValue()->handle;
+}
+
+void SimDeviceData::EnumerateValues(HAL_SimDeviceHandle device, void* param,
+ HALSIM_SimValueCallback callback) {
+ std::scoped_lock lock(m_mutex);
+ Device* deviceImpl = LookupDevice(device);
+ if (!deviceImpl) return;
+
+ for (auto&& value : deviceImpl->values)
+ callback(value->name.c_str(), param, value->handle, value->readonly,
+ &value->value);
+}
+
+const char** SimDeviceData::GetValueEnumOptions(HAL_SimValueHandle handle,
+ int32_t* numOptions) {
+ *numOptions = 0;
+
+ std::scoped_lock lock(m_mutex);
+ Value* valueImpl = LookupValue(handle);
+ if (!valueImpl) return nullptr;
+
+ // get list of options (safe to return as they never change)
+ auto& options = valueImpl->cstrEnumOptions;
+ *numOptions = options.size();
+ return options.data();
+}
+
+void SimDeviceData::ResetData() {
+ std::scoped_lock lock(m_mutex);
+ m_devices.clear();
+ m_deviceMap.clear();
+ m_deviceCreated.Reset();
+ m_deviceFreed.Reset();
+}
+
+extern "C" {
+
+int32_t HALSIM_RegisterSimDeviceCreatedCallback(
+ const char* prefix, void* param, HALSIM_SimDeviceCallback callback,
+ HAL_Bool initialNotify) {
+ return SimSimDeviceData->RegisterDeviceCreatedCallback(
+ prefix, param, callback, initialNotify);
+}
+
+void HALSIM_CancelSimDeviceCreatedCallback(int32_t uid) {
+ SimSimDeviceData->CancelDeviceCreatedCallback(uid);
+}
+
+int32_t HALSIM_RegisterSimDeviceFreedCallback(
+ const char* prefix, void* param, HALSIM_SimDeviceCallback callback) {
+ return SimSimDeviceData->RegisterDeviceFreedCallback(prefix, param, callback);
+}
+
+void HALSIM_CancelSimDeviceFreedCallback(int32_t uid) {
+ SimSimDeviceData->CancelDeviceFreedCallback(uid);
+}
+
+HAL_SimDeviceHandle HALSIM_GetSimDeviceHandle(const char* name) {
+ return SimSimDeviceData->GetDeviceHandle(name);
+}
+
+const char* HALSIM_GetSimDeviceName(HAL_SimDeviceHandle handle) {
+ return SimSimDeviceData->GetDeviceName(handle);
+}
+
+HAL_SimDeviceHandle HALSIM_GetSimValueDeviceHandle(HAL_SimValueHandle handle) {
+ if (handle <= 0) return 0;
+ return handle >> 16;
+}
+
+void HALSIM_EnumerateSimDevices(const char* prefix, void* param,
+ HALSIM_SimDeviceCallback callback) {
+ SimSimDeviceData->EnumerateDevices(prefix, param, callback);
+}
+
+int32_t HALSIM_RegisterSimValueCreatedCallback(HAL_SimDeviceHandle device,
+ void* param,
+ HALSIM_SimValueCallback callback,
+ HAL_Bool initialNotify) {
+ return SimSimDeviceData->RegisterValueCreatedCallback(device, param, callback,
+ initialNotify);
+}
+
+void HALSIM_CancelSimValueCreatedCallback(int32_t uid) {
+ SimSimDeviceData->CancelValueCreatedCallback(uid);
+}
+
+int32_t HALSIM_RegisterSimValueChangedCallback(HAL_SimValueHandle handle,
+ void* param,
+ HALSIM_SimValueCallback callback,
+ HAL_Bool initialNotify) {
+ return SimSimDeviceData->RegisterValueChangedCallback(handle, param, callback,
+ initialNotify);
+}
+
+void HALSIM_CancelSimValueChangedCallback(int32_t uid) {
+ SimSimDeviceData->CancelValueChangedCallback(uid);
+}
+
+HAL_SimValueHandle HALSIM_GetSimValueHandle(HAL_SimDeviceHandle device,
+ const char* name) {
+ return SimSimDeviceData->GetValueHandle(device, name);
+}
+
+void HALSIM_EnumerateSimValues(HAL_SimDeviceHandle device, void* param,
+ HALSIM_SimValueCallback callback) {
+ SimSimDeviceData->EnumerateValues(device, param, callback);
+}
+
+const char** HALSIM_GetSimValueEnumOptions(HAL_SimValueHandle handle,
+ int32_t* numOptions) {
+ return SimSimDeviceData->GetValueEnumOptions(handle, numOptions);
+}
+
+void HALSIM_ResetSimDeviceData(void) { SimSimDeviceData->ResetData(); }
+
+} // extern "C"
diff --git a/hal/src/main/native/sim/mockdata/SimDeviceDataInternal.h b/hal/src/main/native/sim/mockdata/SimDeviceDataInternal.h
new file mode 100644
index 0000000000..2582eca3ac
--- /dev/null
+++ b/hal/src/main/native/sim/mockdata/SimDeviceDataInternal.h
@@ -0,0 +1,214 @@
+/*----------------------------------------------------------------------------*/
+/* 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. */
+/*----------------------------------------------------------------------------*/
+
+#pragma once
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "hal/Value.h"
+#include "mockdata/SimCallbackRegistry.h"
+#include "mockdata/SimDeviceData.h"
+
+namespace hal {
+
+namespace impl {
+
+template
+class SimUnnamedCallbackRegistry {
+ public:
+ using RawFunctor = void (*)();
+
+ protected:
+ using CallbackVector = wpi::UidVector, 4>;
+
+ public:
+ void Cancel(int32_t uid) {
+ if (m_callbacks) m_callbacks->erase(uid - 1);
+ }
+
+ void Reset() {
+ if (m_callbacks) m_callbacks->clear();
+ }
+
+ int32_t Register(CallbackFunction callback, void* param) {
+ // Must return -1 on a null callback for error handling
+ if (callback == nullptr) return -1;
+ if (!m_callbacks) m_callbacks = std::make_unique();
+ return m_callbacks->emplace_back(param,
+ reinterpret_cast(callback)) +
+ 1;
+ }
+
+ template
+ void Invoke(const char* name, U&&... u) const {
+ if (m_callbacks) {
+ for (auto&& cb : *m_callbacks) {
+ reinterpret_cast(cb.callback)(name, cb.param,
+ std::forward(u)...);
+ }
+ }
+ }
+
+ template
+ LLVM_ATTRIBUTE_ALWAYS_INLINE void operator()(U&&... u) const {
+ return Invoke(std::forward(u)...);
+ }
+
+ private:
+ std::unique_ptr m_callbacks;
+};
+
+template
+class SimPrefixCallbackRegistry {
+ struct CallbackData {
+ CallbackData() = default;
+ CallbackData(const char* prefix_, void* param_, CallbackFunction callback_)
+ : prefix{prefix_}, callback{callback_}, param{param_} {}
+ std::string prefix;
+ CallbackFunction callback;
+ void* param;
+
+ explicit operator bool() const { return callback != nullptr; }
+ };
+ using CallbackVector = wpi::UidVector;
+
+ public:
+ void Cancel(int32_t uid) {
+ if (m_callbacks) m_callbacks->erase(uid - 1);
+ }
+
+ void Reset() {
+ if (m_callbacks) m_callbacks->clear();
+ }
+
+ int32_t Register(const char* prefix, void* param, CallbackFunction callback) {
+ // Must return -1 on a null callback for error handling
+ if (callback == nullptr) return -1;
+ if (!m_callbacks) m_callbacks = std::make_unique();
+ return m_callbacks->emplace_back(prefix, param, callback) + 1;
+ }
+
+ template
+ void Invoke(const char* name, U&&... u) const {
+ if (m_callbacks) {
+ for (auto&& cb : *m_callbacks) {
+ if (wpi::StringRef{name}.startswith(cb.prefix)) {
+ cb.callback(name, cb.param, std::forward(u)...);
+ }
+ }
+ }
+ }
+
+ template
+ LLVM_ATTRIBUTE_ALWAYS_INLINE void operator()(U&&... u) const {
+ return Invoke(std::forward(u)...);
+ }
+
+ private:
+ std::unique_ptr m_callbacks;
+};
+
+} // namespace impl
+
+class SimDeviceData {
+ private:
+ struct Value {
+ Value(const char* name_, bool readonly_, const HAL_Value& value_)
+ : name{name_}, readonly{readonly_}, value{value_} {}
+
+ HAL_SimValueHandle handle{0};
+ std::string name;
+ bool readonly;
+ HAL_Value value;
+ std::vector enumOptions;
+ std::vector cstrEnumOptions;
+ impl::SimUnnamedCallbackRegistry changed;
+ };
+
+ struct Device {
+ explicit Device(const char* name_) : name{name_} {}
+
+ HAL_SimDeviceHandle handle{0};
+ std::string name;
+ wpi::UidVector, 16> values;
+ wpi::StringMap valueMap;
+ impl::SimUnnamedCallbackRegistry valueCreated;
+ };
+
+ wpi::UidVector, 4> m_devices;
+ wpi::StringMap> m_deviceMap;
+
+ wpi::recursive_spinlock m_mutex;
+
+ impl::SimPrefixCallbackRegistry m_deviceCreated;
+ impl::SimPrefixCallbackRegistry m_deviceFreed;
+
+ // call with lock held, returns null if does not exist
+ Device* LookupDevice(HAL_SimDeviceHandle handle);
+ Value* LookupValue(HAL_SimValueHandle handle);
+
+ public:
+ HAL_SimDeviceHandle CreateDevice(const char* name);
+ void FreeDevice(HAL_SimDeviceHandle handle);
+ HAL_SimValueHandle CreateValue(HAL_SimDeviceHandle device, const char* name,
+ bool readonly, int32_t numOptions,
+ const char** options,
+ const HAL_Value& initialValue);
+ HAL_Value GetValue(HAL_SimValueHandle handle);
+ void SetValue(HAL_SimValueHandle handle, const HAL_Value& value);
+
+ int32_t RegisterDeviceCreatedCallback(const char* prefix, void* param,
+ HALSIM_SimDeviceCallback callback,
+ bool initialNotify);
+
+ void CancelDeviceCreatedCallback(int32_t uid);
+
+ int32_t RegisterDeviceFreedCallback(const char* prefix, void* param,
+ HALSIM_SimDeviceCallback callback);
+
+ void CancelDeviceFreedCallback(int32_t uid);
+
+ HAL_SimDeviceHandle GetDeviceHandle(const char* name);
+ const char* GetDeviceName(HAL_SimDeviceHandle handle);
+
+ void EnumerateDevices(const char* prefix, void* param,
+ HALSIM_SimDeviceCallback callback);
+
+ int32_t RegisterValueCreatedCallback(HAL_SimDeviceHandle device, void* param,
+ HALSIM_SimValueCallback callback,
+ bool initialNotify);
+
+ void CancelValueCreatedCallback(int32_t uid);
+
+ int32_t RegisterValueChangedCallback(HAL_SimValueHandle handle, void* param,
+ HALSIM_SimValueCallback callback,
+ bool initialNotify);
+
+ void CancelValueChangedCallback(int32_t uid);
+
+ HAL_SimValueHandle GetValueHandle(HAL_SimDeviceHandle device,
+ const char* name);
+
+ void EnumerateValues(HAL_SimDeviceHandle device, void* param,
+ HALSIM_SimValueCallback callback);
+
+ const char** GetValueEnumOptions(HAL_SimValueHandle handle,
+ int32_t* numOptions);
+
+ void ResetData();
+};
+extern SimDeviceData* SimSimDeviceData;
+} // namespace hal