mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[sim] Various WebSockets fixes and enhancements (#2952)
This is a breaking change to the WebSockets layer to align it with recent specification documentation work. To support this, HAL SimValue changed readonly to a direction enum. This allows specifying bidirectional in addition to input and output. The SimValue change is specifically designed to avoid API and ABI breakage. This is completely transparent in C++; in Java a new callback class was added, and the old readonly functions have been marked deprecated. A new SimValue creation function for enums allows specifying double values for each enum value, not just strings. This allows mapping enum values to doubles in the WebSockets layer. A ":" in the SimDevice name now maps it to different WebSocket types (e.g. "Accel:Name" becomes type "Accel", device "Name"). The type is hidden in the GUI. Other WebSockets changes: * Implemented match_time and game_data * Added joystick rumble data * Added builtin accelerometer support * SimValue enums are mapped to string and double value on WS interface * Added WebSockets protocol specification * Added READMEs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -11,6 +11,18 @@ package edu.wpi.first.hal;
|
||||
* A wrapper around a simulator device handle.
|
||||
*/
|
||||
public class SimDevice implements AutoCloseable {
|
||||
public enum Direction {
|
||||
kInput(SimDeviceJNI.kInput),
|
||||
kOutput(SimDeviceJNI.kOutput),
|
||||
kBidir(SimDeviceJNI.kBidir);
|
||||
|
||||
public final int m_value;
|
||||
|
||||
Direction(int value) {
|
||||
m_value = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simulated device.
|
||||
*
|
||||
@@ -101,9 +113,26 @@ public class SimDevice implements AutoCloseable {
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param initialValue initial value
|
||||
* @return simulated value object
|
||||
*
|
||||
* @deprecated Use direction function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public SimValue createValue(String name, boolean readonly, HALValue initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValue(m_handle, name, readonly, initialValue);
|
||||
return createValue(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value on the simulated device.
|
||||
*
|
||||
* <p>Returns null if not in simulation.
|
||||
*
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value object
|
||||
*/
|
||||
public SimValue createValue(String name, Direction direction, HALValue initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValue(m_handle, name, direction.m_value, initialValue);
|
||||
if (handle <= 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -119,9 +148,26 @@ public class SimDevice implements AutoCloseable {
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param initialValue initial value
|
||||
* @return simulated double value object
|
||||
*
|
||||
* @deprecated Use direction function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public SimDouble createDouble(String name, boolean readonly, double initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, readonly, initialValue);
|
||||
return createDouble(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double value on the simulated device.
|
||||
*
|
||||
* <p>Returns null if not in simulation.
|
||||
*
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated double value object
|
||||
*/
|
||||
public SimDouble createDouble(String name, Direction direction, double initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueDouble(m_handle, name, direction.m_value, initialValue);
|
||||
if (handle <= 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -140,9 +186,54 @@ public class SimDevice implements AutoCloseable {
|
||||
* @param options array of option descriptions
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*
|
||||
* @deprecated Use direction function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public SimEnum createEnum(String name, boolean readonly, String[] options, int initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueEnum(m_handle, name, readonly, options, initialValue);
|
||||
return createEnum(name, readonly ? Direction.kOutput : Direction.kInput, options, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on the simulated device.
|
||||
*
|
||||
* <p>Enumerated values are always in the range 0 to numOptions-1.
|
||||
*
|
||||
* <p>Returns null if not in simulation.
|
||||
*
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*/
|
||||
public SimEnum createEnum(String name, Direction direction, String[] options, int initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueEnum(m_handle, name, direction.m_value, options,
|
||||
initialValue);
|
||||
if (handle <= 0) {
|
||||
return null;
|
||||
}
|
||||
return new SimEnum(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on the simulated device with double values.
|
||||
*
|
||||
* <p>Enumerated values are always in the range 0 to numOptions-1.
|
||||
*
|
||||
* <p>Returns null if not in simulation.
|
||||
*
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param optionValues array of option values (must be the same size as options)
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*/
|
||||
public SimEnum createEnumDouble(String name, Direction direction, String[] options,
|
||||
double[] optionValues, int initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueEnumDouble(m_handle, name, direction.m_value, options,
|
||||
optionValues, initialValue);
|
||||
if (handle <= 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -158,9 +249,27 @@ public class SimDevice implements AutoCloseable {
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param initialValue initial value
|
||||
* @return simulated boolean value object
|
||||
*
|
||||
* @deprecated Use direction function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public SimBoolean createBoolean(String name, boolean readonly, boolean initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueBoolean(m_handle, name, readonly, initialValue);
|
||||
return createBoolean(name, readonly ? Direction.kOutput : Direction.kInput, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean value on the simulated device.
|
||||
*
|
||||
* <p>Returns null if not in simulation.
|
||||
*
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated boolean value object
|
||||
*/
|
||||
public SimBoolean createBoolean(String name, Direction direction, boolean initialValue) {
|
||||
int handle = SimDeviceJNI.createSimValueBoolean(m_handle, name, direction.m_value,
|
||||
initialValue);
|
||||
if (handle <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -8,6 +8,10 @@
|
||||
package edu.wpi.first.hal;
|
||||
|
||||
public class SimDeviceJNI extends JNIWrapper {
|
||||
public static final int kInput = 0;
|
||||
public static final int kOutput = 1;
|
||||
public static final int kBidir = 2;
|
||||
|
||||
/**
|
||||
* Creates a simulated device.
|
||||
*
|
||||
@@ -33,7 +37,7 @@ public class SimDeviceJNI extends JNIWrapper {
|
||||
*/
|
||||
public static native void freeSimDevice(int handle);
|
||||
|
||||
private static native int createSimValueNative(int device, String name, boolean readonly,
|
||||
private static native int createSimValueNative(int device, String name, int direction,
|
||||
int type, long value1, double value2);
|
||||
|
||||
/**
|
||||
@@ -47,10 +51,31 @@ public class SimDeviceJNI extends JNIWrapper {
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*
|
||||
* @deprecated Use direction-taking function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static int createSimValue(int device, String name, boolean readonly,
|
||||
HALValue initialValue) {
|
||||
return createSimValueNative(device, name, readonly, initialValue.getType(),
|
||||
return createSimValueNative(device, name, readonly ? kOutput : kInput, initialValue.getType(),
|
||||
initialValue.getNativeLong(), initialValue.getNativeDouble());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a value on a simulated device.
|
||||
*
|
||||
* <p>Returns 0 if not in simulation; this can be used to avoid calls
|
||||
* to Set/Get functions.
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*/
|
||||
public static int createSimValue(int device, String name, int direction,
|
||||
HALValue initialValue) {
|
||||
return createSimValueNative(device, name, direction, initialValue.getType(),
|
||||
initialValue.getNativeLong(), initialValue.getNativeDouble());
|
||||
}
|
||||
|
||||
@@ -65,10 +90,30 @@ public class SimDeviceJNI extends JNIWrapper {
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*
|
||||
* @deprecated Use direction-taking function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static int createSimValueDouble(int device, String name, boolean readonly,
|
||||
double initialValue) {
|
||||
return createSimValueNative(device, name, readonly, HALValue.kDouble, 0, initialValue);
|
||||
return createSimValueNative(device, name, readonly ? kOutput : kInput, HALValue.kDouble, 0, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a double value on a simulated device.
|
||||
*
|
||||
* <p>Returns 0 if not in simulation; this can be used to avoid calls
|
||||
* to Set/Get functions.
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*/
|
||||
public static int createSimValueDouble(int device, String name, int direction,
|
||||
double initialValue) {
|
||||
return createSimValueNative(device, name, direction, HALValue.kDouble, 0, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,10 +130,52 @@ public class SimDeviceJNI extends JNIWrapper {
|
||||
* @param options array of option descriptions
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated value handle
|
||||
*
|
||||
* @deprecated Use direction-taking function instead
|
||||
*/
|
||||
public static native int createSimValueEnum(int device, String name, boolean readonly,
|
||||
@Deprecated
|
||||
public static int createSimValueEnum(int device, String name, boolean readonly,
|
||||
String[] options, int initialValue) {
|
||||
return createSimValueEnum(device, name, readonly ? kOutput : kInput, options, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on a simulated device.
|
||||
*
|
||||
* <p>Enumerated values are always in the range 0 to numOptions-1.
|
||||
*
|
||||
* <p>Returns 0 if not in simulation; this can be used to avoid calls
|
||||
* to Set/Get functions.
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated value handle
|
||||
*/
|
||||
public static native int createSimValueEnum(int device, String name, int direction,
|
||||
String[] options, int initialValue);
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on a simulated device with double values.
|
||||
*
|
||||
* <p>Enumerated values are always in the range 0 to numOptions-1.
|
||||
*
|
||||
* <p>Returns 0 if not in simulation; this can be used to avoid calls
|
||||
* to Set/Get functions.
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param optionValues array of option values (must be the same size as options)
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated value handle
|
||||
*/
|
||||
public static native int createSimValueEnumDouble(int device, String name, int direction,
|
||||
String[] options, double[] optionValues, int initialValue);
|
||||
|
||||
/**
|
||||
* Creates a boolean value on a simulated device.
|
||||
*
|
||||
@@ -100,10 +187,31 @@ public class SimDeviceJNI extends JNIWrapper {
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*
|
||||
* @deprecated Use direction-taking function instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static int createSimValueBoolean(int device, String name, boolean readonly,
|
||||
boolean initialValue) {
|
||||
return createSimValueNative(device, name, readonly, HALValue.kBoolean,
|
||||
return createSimValueNative(device, name, readonly ? kOutput : kInput, HALValue.kBoolean,
|
||||
initialValue ? 1 : 0, 0.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean value on a simulated device.
|
||||
*
|
||||
* <p>Returns 0 if not in simulation; this can be used to avoid calls
|
||||
* to Set/Get functions.
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*/
|
||||
public static int createSimValueBoolean(int device, String name, int direction,
|
||||
boolean initialValue) {
|
||||
return createSimValueNative(device, name, direction, HALValue.kBoolean,
|
||||
initialValue ? 1 : 0, 0.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,18 +41,21 @@ public class SimDeviceDataJNI extends JNIWrapper {
|
||||
public static native SimDeviceInfo[] enumerateSimDevices(String prefix);
|
||||
|
||||
public static native int registerSimValueCreatedCallback(int device, SimValueCallback callback, boolean initialNotify);
|
||||
public static native int registerSimValueCreatedCallback2(int device, SimValueCallback2 callback, boolean initialNotify);
|
||||
public static native void cancelSimValueCreatedCallback(int uid);
|
||||
|
||||
public static native int registerSimValueChangedCallback(int handle, SimValueCallback callback, boolean initialNotify);
|
||||
public static native int registerSimValueChangedCallback2(int handle, SimValueCallback2 callback, boolean initialNotify);
|
||||
public static native void cancelSimValueChangedCallback(int uid);
|
||||
|
||||
public static native int getSimValueHandle(int device, String name);
|
||||
|
||||
public static class SimValueInfo {
|
||||
public SimValueInfo(String name, int handle, boolean readonly, int type, long value1, double value2) {
|
||||
public SimValueInfo(String name, int handle, int direction, int type, long value1, double value2) {
|
||||
this.name = name;
|
||||
this.handle = handle;
|
||||
this.readonly = readonly;
|
||||
this.readonly = direction == 1;
|
||||
this.direction = direction;
|
||||
this.value = HALValue.fromNative(type, value1, value2);
|
||||
}
|
||||
|
||||
@@ -63,8 +66,12 @@ public class SimDeviceDataJNI extends JNIWrapper {
|
||||
public int handle;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
@Deprecated
|
||||
public boolean readonly;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public int direction;
|
||||
|
||||
@SuppressWarnings("MemberName")
|
||||
public HALValue value;
|
||||
}
|
||||
@@ -72,5 +79,7 @@ public class SimDeviceDataJNI extends JNIWrapper {
|
||||
|
||||
public static native String[] getSimValueEnumOptions(int handle);
|
||||
|
||||
public static native double[] getSimValueEnumDoubleValues(int handle);
|
||||
|
||||
public static native void resetSimDeviceData();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
package edu.wpi.first.hal.simulation;
|
||||
|
||||
import edu.wpi.first.hal.HALValue;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SimValueCallback2 {
|
||||
void callback(String name, int handle, int direction, HALValue value);
|
||||
|
||||
default void callbackNative(String name, int handle, int direction, int type, long value1, double value2) {
|
||||
callback(name, handle, direction, HALValue.fromNative(type, value1, value2));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -14,19 +14,26 @@ 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 char* name, int32_t direction,
|
||||
const struct HAL_Value* initialValue) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_SimValueHandle HAL_CreateSimValueEnum(HAL_SimDeviceHandle device,
|
||||
const char* name, HAL_Bool readonly,
|
||||
const char* name, int32_t direction,
|
||||
int32_t numOptions,
|
||||
const char** options,
|
||||
int32_t initialValue) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HAL_SimValueHandle HAL_CreateSimValueEnumDouble(
|
||||
HAL_SimDeviceHandle device, const char* name, int32_t direction,
|
||||
int32_t numOptions, const char** options, const double* optionValues,
|
||||
int32_t initialValue) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HAL_GetSimValue(HAL_SimValueHandle handle, struct HAL_Value* value) {
|
||||
value->type = HAL_UNASSIGNED;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,12 @@ const char** HALSIM_GetSimValueEnumOptions(HAL_SimValueHandle handle,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const double* HALSIM_GetSimValueEnumDoubleValues(HAL_SimValueHandle handle,
|
||||
int32_t* numOptions) {
|
||||
*numOptions = 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HALSIM_ResetSimDeviceData(void) {}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -69,25 +69,25 @@ Java_edu_wpi_first_hal_SimDeviceJNI_freeSimDevice
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_SimDeviceJNI
|
||||
* Method: createSimValueNative
|
||||
* Signature: (ILjava/lang/String;ZIJD)I
|
||||
* Signature: (ILjava/lang/String;IIJD)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueNative
|
||||
(JNIEnv* env, jclass, jint device, jstring name, jboolean readonly, jint type,
|
||||
(JNIEnv* env, jclass, jint device, jstring name, jint direction, jint type,
|
||||
jlong value1, jdouble value2)
|
||||
{
|
||||
return HAL_CreateSimValue(device, JStringRef{env, name}.c_str(), readonly,
|
||||
return HAL_CreateSimValue(device, JStringRef{env, name}.c_str(), direction,
|
||||
ValueFromJava(type, value1, value2));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_SimDeviceJNI
|
||||
* Method: createSimValueEnum
|
||||
* Signature: (ILjava/lang/String;Z[Ljava/lang/Object;I)I
|
||||
* Signature: (ILjava/lang/String;I[Ljava/lang/Object;I)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueEnum
|
||||
(JNIEnv* env, jclass, jint device, jstring name, jboolean readonly,
|
||||
(JNIEnv* env, jclass, jint device, jstring name, jint direction,
|
||||
jobjectArray options, jint initialValue)
|
||||
{
|
||||
size_t len = env->GetArrayLength(options);
|
||||
@@ -101,8 +101,37 @@ Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueEnum
|
||||
}
|
||||
wpi::SmallVector<const char*, 8> 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);
|
||||
return HAL_CreateSimValueEnum(device, JStringRef{env, name}.c_str(),
|
||||
direction, len, carr.data(), initialValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_SimDeviceJNI
|
||||
* Method: createSimValueEnumDouble
|
||||
* Signature: (ILjava/lang/String;I[Ljava/lang/Object;[DI)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_SimDeviceJNI_createSimValueEnumDouble
|
||||
(JNIEnv* env, jclass, jint device, jstring name, jint direction,
|
||||
jobjectArray options, jdoubleArray optionValues, jint initialValue)
|
||||
{
|
||||
size_t len = env->GetArrayLength(options);
|
||||
size_t len2 = env->GetArrayLength(optionValues);
|
||||
if (len != len2) return 0;
|
||||
std::vector<std::string> arr;
|
||||
arr.reserve(len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
JLocal<jstring> elem{
|
||||
env, static_cast<jstring>(env->GetObjectArrayElement(options, i))};
|
||||
if (!elem) return 0;
|
||||
arr.push_back(JStringRef{env, elem}.str());
|
||||
}
|
||||
|
||||
wpi::SmallVector<const char*, 8> carr;
|
||||
for (auto&& val : arr) carr.push_back(val.c_str());
|
||||
return HAL_CreateSimValueEnumDouble(
|
||||
device, JStringRef{env, name}.c_str(), direction, len, carr.data(),
|
||||
JDoubleArrayRef{env, optionValues}.array().data(), initialValue);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "SimulatorJNI.h"
|
||||
#include "edu_wpi_first_hal_simulation_SimDeviceDataJNI.h"
|
||||
#include "hal/SimDevice.h"
|
||||
#include "hal/handles/UnlimitedHandleResource.h"
|
||||
#include "hal/simulation/SimDeviceData.h"
|
||||
|
||||
@@ -40,12 +41,12 @@ struct DeviceInfo {
|
||||
};
|
||||
|
||||
struct ValueInfo {
|
||||
ValueInfo(const char* name_, HAL_SimValueHandle handle_, bool readonly_,
|
||||
ValueInfo(const char* name_, HAL_SimValueHandle handle_, int32_t direction_,
|
||||
const HAL_Value& value_)
|
||||
: name{name_}, handle{handle_}, readonly{readonly_}, value{value_} {}
|
||||
: name{name_}, handle{handle_}, direction{direction_}, value{value_} {}
|
||||
std::string name;
|
||||
HAL_SimValueHandle handle;
|
||||
bool readonly;
|
||||
int32_t direction;
|
||||
HAL_Value value;
|
||||
|
||||
jobject MakeJava(JNIEnv* env) const;
|
||||
@@ -87,11 +88,11 @@ static std::pair<jlong, jdouble> ToValue12(const HAL_Value& value) {
|
||||
|
||||
jobject ValueInfo::MakeJava(JNIEnv* env) const {
|
||||
static jmethodID func =
|
||||
env->GetMethodID(simValueInfoCls, "<init>", "(Ljava/lang/String;IZIJD)V");
|
||||
env->GetMethodID(simValueInfoCls, "<init>", "(Ljava/lang/String;IIIJD)V");
|
||||
auto [value1, value2] = ToValue12(value);
|
||||
return env->NewObject(simValueInfoCls, func, MakeJString(env, name),
|
||||
(jint)handle, (jboolean)readonly, (jint)value.type,
|
||||
value1, value2);
|
||||
(jint)handle, (jint)direction, (jint)value.type, value1,
|
||||
value2);
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -111,16 +112,19 @@ class DeviceCallbackStore {
|
||||
|
||||
class ValueCallbackStore {
|
||||
public:
|
||||
explicit ValueCallbackStore(bool dirCallback) : m_dirCallback{dirCallback} {}
|
||||
|
||||
void create(JNIEnv* env, jobject obj) { m_call = JGlobal<jobject>(env, obj); }
|
||||
void performCallback(const char* name, HAL_SimValueHandle handle,
|
||||
bool readonly, const HAL_Value& value);
|
||||
int32_t direction, const HAL_Value& value);
|
||||
void free(JNIEnv* env) { m_call.free(env); }
|
||||
void setCallbackId(int32_t id) { callbackId = id; }
|
||||
int32_t getCallbackId() { return callbackId; }
|
||||
void setCallbackId(int32_t id) { m_callbackId = id; }
|
||||
int32_t getCallbackId() { return m_callbackId; }
|
||||
|
||||
private:
|
||||
wpi::java::JGlobal<jobject> m_call;
|
||||
int32_t callbackId;
|
||||
int32_t m_callbackId;
|
||||
bool m_dirCallback;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -159,7 +163,7 @@ void DeviceCallbackStore::performCallback(const char* name,
|
||||
|
||||
void ValueCallbackStore::performCallback(const char* name,
|
||||
HAL_SimValueHandle handle,
|
||||
bool readonly,
|
||||
int32_t direction,
|
||||
const HAL_Value& value) {
|
||||
JNIEnv* env;
|
||||
JavaVM* vm = sim::GetJVM();
|
||||
@@ -180,9 +184,16 @@ void ValueCallbackStore::performCallback(const char* name,
|
||||
}
|
||||
|
||||
auto [value1, value2] = ToValue12(value);
|
||||
env->CallVoidMethod(m_call, simValueCallbackCallback, MakeJString(env, name),
|
||||
(jint)handle, (jboolean)readonly, (jint)value.type,
|
||||
value1, value2);
|
||||
if (m_dirCallback) {
|
||||
env->CallVoidMethod(m_call, simValueCallbackCallback,
|
||||
MakeJString(env, name), (jint)handle, (jint)direction,
|
||||
(jint)value.type, value1, value2);
|
||||
} else {
|
||||
env->CallVoidMethod(m_call, simValueCallbackCallback,
|
||||
MakeJString(env, name), (jint)handle,
|
||||
(jboolean)(direction == HAL_SimValueOutput),
|
||||
(jint)value.type, value1, value2);
|
||||
}
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
@@ -255,11 +266,12 @@ typedef void (*FreeValueCallbackFunc)(int32_t uid);
|
||||
|
||||
template <typename THandle>
|
||||
static SIM_JniHandle AllocateValueCallback(
|
||||
JNIEnv* env, THandle h, jobject callback, jboolean initialNotify,
|
||||
JNIEnv* env, THandle h, jobject callback, bool dirCallback,
|
||||
jboolean initialNotify,
|
||||
int32_t (*createCallback)(THandle handle, void* param,
|
||||
HALSIM_SimValueCallback callback,
|
||||
HAL_Bool initialNotify)) {
|
||||
auto callbackStore = std::make_shared<ValueCallbackStore>();
|
||||
auto callbackStore = std::make_shared<ValueCallbackStore>(dirCallback);
|
||||
|
||||
auto handle = valueCallbackHandles->Allocate(callbackStore);
|
||||
|
||||
@@ -273,14 +285,14 @@ static SIM_JniHandle AllocateValueCallback(
|
||||
callbackStore->create(env, callback);
|
||||
|
||||
auto callbackFunc = [](const char* name, void* param,
|
||||
HAL_SimValueHandle handle, HAL_Bool readonly,
|
||||
HAL_SimValueHandle handle, int32_t direction,
|
||||
const HAL_Value* value) {
|
||||
uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
|
||||
SIM_JniHandle jnihandle = static_cast<SIM_JniHandle>(handleTmp);
|
||||
auto data = valueCallbackHandles->Get(jnihandle);
|
||||
if (!data) return;
|
||||
|
||||
data->performCallback(name, handle, readonly, *value);
|
||||
data->performCallback(name, handle, direction, *value);
|
||||
};
|
||||
|
||||
auto id = createCallback(h, handleAsVoidPtr, callbackFunc, initialNotify);
|
||||
@@ -504,7 +516,21 @@ Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueCreatedCallba
|
||||
(JNIEnv* env, jclass, jint device, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return AllocateValueCallback(env, static_cast<HAL_SimDeviceHandle>(device),
|
||||
callback, initialNotify,
|
||||
callback, false, initialNotify,
|
||||
&HALSIM_RegisterSimValueCreatedCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_SimDeviceDataJNI
|
||||
* Method: registerSimValueCreatedCallback2
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueCreatedCallback2
|
||||
(JNIEnv* env, jclass, jint device, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return AllocateValueCallback(env, static_cast<HAL_SimDeviceHandle>(device),
|
||||
callback, true, initialNotify,
|
||||
&HALSIM_RegisterSimValueCreatedCallback);
|
||||
}
|
||||
|
||||
@@ -530,7 +556,21 @@ Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueChangedCallba
|
||||
(JNIEnv* env, jclass, jint handle, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return AllocateValueCallback(env, static_cast<HAL_SimValueHandle>(handle),
|
||||
callback, initialNotify,
|
||||
callback, false, initialNotify,
|
||||
&HALSIM_RegisterSimValueChangedCallback);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_SimDeviceDataJNI
|
||||
* Method: registerSimValueChangedCallback2
|
||||
* Signature: (ILjava/lang/Object;Z)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_registerSimValueChangedCallback2
|
||||
(JNIEnv* env, jclass, jint handle, jobject callback, jboolean initialNotify)
|
||||
{
|
||||
return AllocateValueCallback(env, static_cast<HAL_SimValueHandle>(handle),
|
||||
callback, true, initialNotify,
|
||||
&HALSIM_RegisterSimValueChangedCallback);
|
||||
}
|
||||
|
||||
@@ -610,6 +650,20 @@ Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_getSimValueEnumOptions
|
||||
return jarr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_SimDeviceDataJNI
|
||||
* Method: getSimValueEnumDoubleValues
|
||||
* Signature: (I)[D
|
||||
*/
|
||||
JNIEXPORT jdoubleArray JNICALL
|
||||
Java_edu_wpi_first_hal_simulation_SimDeviceDataJNI_getSimValueEnumDoubleValues
|
||||
(JNIEnv* env, jclass, jint handle)
|
||||
{
|
||||
int32_t numElems = 0;
|
||||
const double* elems = HALSIM_GetSimValueEnumDoubleValues(handle, &numElems);
|
||||
return MakeJDoubleArray(env, wpi::makeArrayRef(elems, numElems));
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: edu_wpi_first_hal_simulation_SimDeviceDataJNI
|
||||
* Method: resetSimDeviceData
|
||||
|
||||
@@ -30,6 +30,17 @@
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Direction of a simulated value (from the perspective of user code).
|
||||
*/
|
||||
// clang-format off
|
||||
HAL_ENUM(HAL_SimValueDirection) {
|
||||
HAL_SimValueInput = 0, /**< input to user code from the simulator */
|
||||
HAL_SimValueOutput, /**< output from user code to the simulator */
|
||||
HAL_SimValueBidir /**< bidirectional between user code and simulator */
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -67,21 +78,21 @@ void HAL_FreeSimDevice(HAL_SimDeviceHandle handle);
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*/
|
||||
HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
|
||||
const char* name, HAL_Bool readonly,
|
||||
const char* name, int32_t direction,
|
||||
const struct HAL_Value* initialValue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C++" {
|
||||
inline HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
|
||||
const char* name,
|
||||
HAL_Bool readonly,
|
||||
int32_t direction,
|
||||
const HAL_Value& initialValue) {
|
||||
return HAL_CreateSimValue(device, name, readonly, &initialValue);
|
||||
return HAL_CreateSimValue(device, name, direction, &initialValue);
|
||||
}
|
||||
} // extern "C++"
|
||||
#endif
|
||||
@@ -94,16 +105,16 @@ inline HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*/
|
||||
inline HAL_SimValueHandle HAL_CreateSimValueDouble(HAL_SimDeviceHandle device,
|
||||
const char* name,
|
||||
HAL_Bool readonly,
|
||||
int32_t direction,
|
||||
double initialValue) {
|
||||
struct HAL_Value v = HAL_MakeDouble(initialValue);
|
||||
return HAL_CreateSimValue(device, name, readonly, &v);
|
||||
return HAL_CreateSimValue(device, name, direction, &v);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,18 +127,40 @@ inline HAL_SimValueHandle HAL_CreateSimValueDouble(HAL_SimDeviceHandle device,
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @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,
|
||||
const char* name, int32_t direction,
|
||||
int32_t numOptions,
|
||||
const char** options,
|
||||
int32_t initialValue);
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on a simulated device with double values.
|
||||
*
|
||||
* 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 direction input/output/bidir (from perspective of user code)
|
||||
* @param numOptions number of enumerated value options (length of options)
|
||||
* @param options array of option descriptions
|
||||
* @param optionValues array of option double values
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated value handle
|
||||
*/
|
||||
HAL_SimValueHandle HAL_CreateSimValueEnumDouble(
|
||||
HAL_SimDeviceHandle device, const char* name, int32_t direction,
|
||||
int32_t numOptions, const char** options, const double* optionValues,
|
||||
int32_t initialValue);
|
||||
|
||||
/**
|
||||
* Creates a boolean value on a simulated device.
|
||||
*
|
||||
@@ -136,16 +169,16 @@ HAL_SimValueHandle HAL_CreateSimValueEnum(HAL_SimDeviceHandle device,
|
||||
*
|
||||
* @param device simulated device handle
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value handle
|
||||
*/
|
||||
inline HAL_SimValueHandle HAL_CreateSimValueBoolean(HAL_SimDeviceHandle device,
|
||||
const char* name,
|
||||
HAL_Bool readonly,
|
||||
int32_t direction,
|
||||
HAL_Bool initialValue) {
|
||||
struct HAL_Value v = HAL_MakeBoolean(initialValue);
|
||||
return HAL_CreateSimValue(device, name, readonly, &v);
|
||||
return HAL_CreateSimValue(device, name, direction, &v);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -419,6 +452,15 @@ class SimBoolean : public SimValue {
|
||||
*/
|
||||
class SimDevice {
|
||||
public:
|
||||
/**
|
||||
* Direction of a simulated value (from the perspective of user code).
|
||||
*/
|
||||
enum Direction {
|
||||
kInput = HAL_SimValueInput,
|
||||
kOutput = HAL_SimValueOutput,
|
||||
kBidir = HAL_SimValueBidir
|
||||
};
|
||||
|
||||
/**
|
||||
* Default constructor that results in an "empty" object that is false in
|
||||
* a boolean context.
|
||||
@@ -512,13 +554,13 @@ class SimDevice {
|
||||
* in a boolean context.
|
||||
*
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param initialValue initial value
|
||||
* @return simulated value object
|
||||
*/
|
||||
SimValue CreateValue(const char* name, bool readonly,
|
||||
SimValue CreateValue(const char* name, int32_t direction,
|
||||
const HAL_Value& initialValue) {
|
||||
return HAL_CreateSimValue(m_handle, name, readonly, &initialValue);
|
||||
return HAL_CreateSimValue(m_handle, name, direction, &initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -528,12 +570,13 @@ class SimDevice {
|
||||
* in a boolean context.
|
||||
*
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @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);
|
||||
SimDouble CreateDouble(const char* name, int32_t direction,
|
||||
double initialValue) {
|
||||
return HAL_CreateSimValueDouble(m_handle, name, direction, initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -545,15 +588,15 @@ class SimDevice {
|
||||
* in a boolean context.
|
||||
*
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*/
|
||||
SimEnum CreateEnum(const char* name, bool readonly,
|
||||
SimEnum CreateEnum(const char* name, int32_t direction,
|
||||
std::initializer_list<const char*> options,
|
||||
int32_t initialValue) {
|
||||
return HAL_CreateSimValueEnum(m_handle, name, readonly, options.size(),
|
||||
return HAL_CreateSimValueEnum(m_handle, name, direction, options.size(),
|
||||
const_cast<const char**>(options.begin()),
|
||||
initialValue);
|
||||
}
|
||||
@@ -567,18 +610,72 @@ class SimDevice {
|
||||
* in a boolean context.
|
||||
*
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*/
|
||||
SimEnum CreateEnum(const char* name, bool readonly,
|
||||
SimEnum CreateEnum(const char* name, int32_t direction,
|
||||
wpi::ArrayRef<const char*> options, int32_t initialValue) {
|
||||
return HAL_CreateSimValueEnum(m_handle, name, readonly, options.size(),
|
||||
return HAL_CreateSimValueEnum(m_handle, name, direction, options.size(),
|
||||
const_cast<const char**>(options.data()),
|
||||
initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on the simulated device with double values.
|
||||
*
|
||||
* 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 direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param optionValues array of option values (must be the same size as
|
||||
* options)
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*/
|
||||
SimEnum CreateEnumDouble(const char* name, int32_t direction,
|
||||
std::initializer_list<const char*> options,
|
||||
std::initializer_list<double> optionValues,
|
||||
int32_t initialValue) {
|
||||
if (options.size() != optionValues.size()) return {};
|
||||
return HAL_CreateSimValueEnumDouble(
|
||||
m_handle, name, direction, options.size(),
|
||||
const_cast<const char**>(options.begin()), optionValues.begin(),
|
||||
initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enumerated value on the simulated device with double values.
|
||||
*
|
||||
* 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 direction input/output/bidir (from perspective of user code)
|
||||
* @param options array of option descriptions
|
||||
* @param optionValues array of option values (must be the same size as
|
||||
* options)
|
||||
* @param initialValue initial value (selection)
|
||||
* @return simulated enum value object
|
||||
*/
|
||||
SimEnum CreateEnumDouble(const char* name, int32_t direction,
|
||||
wpi::ArrayRef<const char*> options,
|
||||
wpi::ArrayRef<double> optionValues,
|
||||
int32_t initialValue) {
|
||||
if (options.size() != optionValues.size()) return {};
|
||||
return HAL_CreateSimValueEnumDouble(
|
||||
m_handle, name, direction, options.size(),
|
||||
const_cast<const char**>(options.data()), optionValues.data(),
|
||||
initialValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a boolean value on the simulated device.
|
||||
*
|
||||
@@ -586,12 +683,13 @@ class SimDevice {
|
||||
* in a boolean context.
|
||||
*
|
||||
* @param name value name
|
||||
* @param readonly if the value should not be written from simulation side
|
||||
* @param direction input/output/bidir (from perspective of user code)
|
||||
* @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);
|
||||
SimBoolean CreateBoolean(const char* name, int32_t direction,
|
||||
bool initialValue) {
|
||||
return HAL_CreateSimValueBoolean(m_handle, name, direction, initialValue);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -15,7 +15,7 @@ typedef void (*HALSIM_SimDeviceCallback)(const char* name, void* param,
|
||||
|
||||
typedef void (*HALSIM_SimValueCallback)(const char* name, void* param,
|
||||
HAL_SimValueHandle handle,
|
||||
HAL_Bool readonly,
|
||||
int32_t direction,
|
||||
const struct HAL_Value* value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
@@ -69,6 +69,9 @@ void HALSIM_EnumerateSimValues(HAL_SimDeviceHandle device, void* param,
|
||||
const char** HALSIM_GetSimValueEnumOptions(HAL_SimValueHandle handle,
|
||||
int32_t* numOptions);
|
||||
|
||||
const double* HALSIM_GetSimValueEnumDoubleValues(HAL_SimValueHandle handle,
|
||||
int32_t* numOptions);
|
||||
|
||||
void HALSIM_ResetSimDeviceData(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2019 FIRST. All Rights Reserved. */
|
||||
/* Copyright (c) 2019-2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
@@ -33,19 +33,29 @@ void HAL_FreeSimDevice(HAL_SimDeviceHandle handle) {
|
||||
}
|
||||
|
||||
HAL_SimValueHandle HAL_CreateSimValue(HAL_SimDeviceHandle device,
|
||||
const char* name, HAL_Bool readonly,
|
||||
const char* name, int32_t direction,
|
||||
const struct HAL_Value* initialValue) {
|
||||
return SimSimDeviceData->CreateValue(device, name, readonly, 0, nullptr,
|
||||
*initialValue);
|
||||
return SimSimDeviceData->CreateValue(device, name, direction, 0, nullptr,
|
||||
nullptr, *initialValue);
|
||||
}
|
||||
|
||||
HAL_SimValueHandle HAL_CreateSimValueEnum(HAL_SimDeviceHandle device,
|
||||
const char* name, HAL_Bool readonly,
|
||||
const char* name, int32_t direction,
|
||||
int32_t numOptions,
|
||||
const char** options,
|
||||
int32_t initialValue) {
|
||||
return SimSimDeviceData->CreateValue(device, name, readonly, numOptions,
|
||||
options, HAL_MakeEnum(initialValue));
|
||||
return SimSimDeviceData->CreateValue(device, name, direction, numOptions,
|
||||
options, nullptr,
|
||||
HAL_MakeEnum(initialValue));
|
||||
}
|
||||
|
||||
HAL_SimValueHandle HAL_CreateSimValueEnumDouble(
|
||||
HAL_SimDeviceHandle device, const char* name, int32_t direction,
|
||||
int32_t numOptions, const char** options, const double* optionValues,
|
||||
int32_t initialValue) {
|
||||
return SimSimDeviceData->CreateValue(device, name, direction, numOptions,
|
||||
options, optionValues,
|
||||
HAL_MakeEnum(initialValue));
|
||||
}
|
||||
|
||||
void HAL_GetSimValue(HAL_SimValueHandle handle, struct HAL_Value* value) {
|
||||
|
||||
@@ -122,11 +122,10 @@ void SimDeviceData::FreeDevice(HAL_SimDeviceHandle handle) {
|
||||
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) {
|
||||
HAL_SimValueHandle SimDeviceData::CreateValue(
|
||||
HAL_SimDeviceHandle device, const char* name, int32_t direction,
|
||||
int32_t numOptions, const char** options, const double* optionValues,
|
||||
const HAL_Value& initialValue) {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
|
||||
// look up device
|
||||
@@ -142,7 +141,7 @@ HAL_SimValueHandle SimDeviceData::CreateValue(HAL_SimDeviceHandle device,
|
||||
if (deviceImpl->values.size() >= 4095) return 0;
|
||||
|
||||
// create and save; encode device into handle
|
||||
auto valueImplPtr = std::make_unique<Value>(name, readonly, initialValue);
|
||||
auto valueImplPtr = std::make_unique<Value>(name, direction, initialValue);
|
||||
Value* valueImpl = valueImplPtr.get();
|
||||
HAL_SimValueHandle valueHandle =
|
||||
(device << 16) |
|
||||
@@ -159,10 +158,14 @@ HAL_SimValueHandle SimDeviceData::CreateValue(HAL_SimDeviceHandle device,
|
||||
valueImpl->enumOptions.back().c_str());
|
||||
}
|
||||
}
|
||||
// copy option values (if any provided)
|
||||
if (numOptions > 0 && optionValues) {
|
||||
valueImpl->enumOptionValues.assign(optionValues, optionValues + numOptions);
|
||||
}
|
||||
deviceImpl->valueMap[name] = valueImpl;
|
||||
|
||||
// notify callbacks
|
||||
deviceImpl->valueCreated(name, valueHandle, readonly, &initialValue);
|
||||
deviceImpl->valueCreated(name, valueHandle, direction, &initialValue);
|
||||
|
||||
return valueHandle;
|
||||
}
|
||||
@@ -191,7 +194,7 @@ void SimDeviceData::SetValue(HAL_SimValueHandle handle,
|
||||
|
||||
// notify callbacks
|
||||
valueImpl->changed(valueImpl->name.c_str(), valueImpl->handle,
|
||||
valueImpl->readonly, &value);
|
||||
valueImpl->direction, &value);
|
||||
}
|
||||
|
||||
int32_t SimDeviceData::RegisterDeviceCreatedCallback(
|
||||
@@ -273,7 +276,7 @@ int32_t SimDeviceData::RegisterValueCreatedCallback(
|
||||
// initial notifications
|
||||
if (initialNotify) {
|
||||
for (auto&& value : deviceImpl->values)
|
||||
callback(value->name.c_str(), param, value->handle, value->readonly,
|
||||
callback(value->name.c_str(), param, value->handle, value->direction,
|
||||
&value->value);
|
||||
}
|
||||
|
||||
@@ -302,7 +305,7 @@ int32_t SimDeviceData::RegisterValueChangedCallback(
|
||||
// initial notification
|
||||
if (initialNotify)
|
||||
callback(valueImpl->name.c_str(), param, valueImpl->handle,
|
||||
valueImpl->readonly, &valueImpl->value);
|
||||
valueImpl->direction, &valueImpl->value);
|
||||
|
||||
// encode device and value into uid
|
||||
return (((handle >> 16) & 0xfff) << 19) | ((handle & 0xfff) << 7) |
|
||||
@@ -337,7 +340,7 @@ void SimDeviceData::EnumerateValues(HAL_SimDeviceHandle device, void* param,
|
||||
if (!deviceImpl) return;
|
||||
|
||||
for (auto&& value : deviceImpl->values)
|
||||
callback(value->name.c_str(), param, value->handle, value->readonly,
|
||||
callback(value->name.c_str(), param, value->handle, value->direction,
|
||||
&value->value);
|
||||
}
|
||||
|
||||
@@ -355,6 +358,20 @@ const char** SimDeviceData::GetValueEnumOptions(HAL_SimValueHandle handle,
|
||||
return options.data();
|
||||
}
|
||||
|
||||
const double* SimDeviceData::GetValueEnumDoubleValues(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 option values (safe to return as they never change)
|
||||
auto& optionValues = valueImpl->enumOptionValues;
|
||||
*numOptions = optionValues.size();
|
||||
return optionValues.data();
|
||||
}
|
||||
|
||||
void SimDeviceData::ResetData() {
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_devices.clear();
|
||||
@@ -452,6 +469,11 @@ const char** HALSIM_GetSimValueEnumOptions(HAL_SimValueHandle handle,
|
||||
return SimSimDeviceData->GetValueEnumOptions(handle, numOptions);
|
||||
}
|
||||
|
||||
const double* HALSIM_GetSimValueEnumDoubleValues(HAL_SimValueHandle handle,
|
||||
int32_t* numOptions) {
|
||||
return SimSimDeviceData->GetValueEnumDoubleValues(handle, numOptions);
|
||||
}
|
||||
|
||||
void HALSIM_ResetSimDeviceData(void) { SimSimDeviceData->ResetData(); }
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@@ -126,15 +126,16 @@ class SimPrefixCallbackRegistry {
|
||||
class SimDeviceData {
|
||||
private:
|
||||
struct Value {
|
||||
Value(const char* name_, bool readonly_, const HAL_Value& value_)
|
||||
: name{name_}, readonly{readonly_}, value{value_} {}
|
||||
Value(const char* name_, int32_t direction_, const HAL_Value& value_)
|
||||
: name{name_}, direction{direction_}, value{value_} {}
|
||||
|
||||
HAL_SimValueHandle handle{0};
|
||||
std::string name;
|
||||
bool readonly;
|
||||
int32_t direction;
|
||||
HAL_Value value;
|
||||
std::vector<std::string> enumOptions;
|
||||
std::vector<const char*> cstrEnumOptions;
|
||||
std::vector<double> enumOptionValues;
|
||||
impl::SimUnnamedCallbackRegistry<HALSIM_SimValueCallback> changed;
|
||||
};
|
||||
|
||||
@@ -168,8 +169,9 @@ class SimDeviceData {
|
||||
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,
|
||||
int32_t direction, int32_t numOptions,
|
||||
const char** options,
|
||||
const double* optionValues,
|
||||
const HAL_Value& initialValue);
|
||||
HAL_Value GetValue(HAL_SimValueHandle handle);
|
||||
void SetValue(HAL_SimValueHandle handle, const HAL_Value& value);
|
||||
@@ -212,6 +214,9 @@ class SimDeviceData {
|
||||
const char** GetValueEnumOptions(HAL_SimValueHandle handle,
|
||||
int32_t* numOptions);
|
||||
|
||||
const double* GetValueEnumDoubleValues(HAL_SimValueHandle handle,
|
||||
int32_t* numOptions);
|
||||
|
||||
void ResetData();
|
||||
};
|
||||
extern SimDeviceData* SimSimDeviceData;
|
||||
|
||||
@@ -33,7 +33,7 @@ class SimValueSource : public glass::DataSource {
|
||||
|
||||
private:
|
||||
static void CallbackFunc(const char*, void* param, HAL_SimValueHandle,
|
||||
HAL_Bool, const HAL_Value* value) {
|
||||
int32_t, const HAL_Value* value) {
|
||||
auto source = static_cast<SimValueSource*>(param);
|
||||
if (value->type == HAL_BOOLEAN) {
|
||||
source->SetValue(value->data.v_boolean);
|
||||
@@ -73,7 +73,7 @@ void SimDevicesModel::Update() {
|
||||
HALSIM_EnumerateSimValues(
|
||||
handle, &data,
|
||||
[](const char* name, void* dataV, HAL_SimValueHandle handle,
|
||||
HAL_Bool readonly, const HAL_Value* value) {
|
||||
int32_t direction, const HAL_Value* value) {
|
||||
auto data = static_cast<Data*>(dataV);
|
||||
auto& source = data->self->m_sources[handle];
|
||||
if (!source) {
|
||||
@@ -85,7 +85,7 @@ void SimDevicesModel::Update() {
|
||||
}
|
||||
|
||||
static void DisplaySimValue(const char* name, void* data,
|
||||
HAL_SimValueHandle handle, HAL_Bool readonly,
|
||||
HAL_SimValueHandle handle, int32_t direction,
|
||||
const HAL_Value* value) {
|
||||
auto model = static_cast<SimDevicesModel*>(data);
|
||||
|
||||
@@ -94,14 +94,16 @@ static void DisplaySimValue(const char* name, void* data,
|
||||
switch (value->type) {
|
||||
case HAL_BOOLEAN: {
|
||||
bool v = value->data.v_boolean;
|
||||
if (glass::DeviceBoolean(name, readonly, &v, model->GetSource(handle))) {
|
||||
if (glass::DeviceBoolean(name, direction == HAL_SimValueOutput, &v,
|
||||
model->GetSource(handle))) {
|
||||
valueCopy.data.v_boolean = v ? 1 : 0;
|
||||
HAL_SetSimValue(handle, valueCopy);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HAL_DOUBLE:
|
||||
if (glass::DeviceDouble(name, readonly, &valueCopy.data.v_double,
|
||||
if (glass::DeviceDouble(name, direction == HAL_SimValueOutput,
|
||||
&valueCopy.data.v_double,
|
||||
model->GetSource(handle))) {
|
||||
HAL_SetSimValue(handle, valueCopy);
|
||||
}
|
||||
@@ -109,21 +111,22 @@ static void DisplaySimValue(const char* name, void* data,
|
||||
case HAL_ENUM: {
|
||||
int32_t numOptions = 0;
|
||||
const char** options = HALSIM_GetSimValueEnumOptions(handle, &numOptions);
|
||||
if (glass::DeviceEnum(name, readonly, &valueCopy.data.v_enum, options,
|
||||
numOptions, model->GetSource(handle))) {
|
||||
if (glass::DeviceEnum(name, direction == HAL_SimValueOutput,
|
||||
&valueCopy.data.v_enum, options, numOptions,
|
||||
model->GetSource(handle))) {
|
||||
HAL_SetSimValue(handle, valueCopy);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case HAL_INT:
|
||||
if (glass::DeviceInt(name, readonly, &valueCopy.data.v_int,
|
||||
model->GetSource(handle))) {
|
||||
if (glass::DeviceInt(name, direction == HAL_SimValueOutput,
|
||||
&valueCopy.data.v_int, model->GetSource(handle))) {
|
||||
HAL_SetSimValue(handle, valueCopy);
|
||||
}
|
||||
break;
|
||||
case HAL_LONG:
|
||||
if (glass::DeviceLong(name, readonly, &valueCopy.data.v_long,
|
||||
model->GetSource(handle))) {
|
||||
if (glass::DeviceLong(name, direction == HAL_SimValueOutput,
|
||||
&valueCopy.data.v_long, model->GetSource(handle))) {
|
||||
HAL_SetSimValue(handle, valueCopy);
|
||||
}
|
||||
break;
|
||||
@@ -134,7 +137,10 @@ static void DisplaySimValue(const char* name, void* data,
|
||||
|
||||
static void DisplaySimDevice(const char* name, void* data,
|
||||
HAL_SimDeviceHandle handle) {
|
||||
if (glass::BeginDevice(name)) {
|
||||
// only show "Foo" portion of "Accel:Foo"
|
||||
auto [type, id] = wpi::StringRef{name}.split(':');
|
||||
if (id.empty()) id = type;
|
||||
if (glass::BeginDevice(id.data())) {
|
||||
HALSIM_EnumerateSimValues(handle, data, DisplaySimValue);
|
||||
glass::EndDevice();
|
||||
}
|
||||
|
||||
13
simulation/halsim_ws_client/README.md
Normal file
13
simulation/halsim_ws_client/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# HAL WebSockets Client
|
||||
|
||||
This is an extension that provides a client version of a WebSockets API for transmitting robot hardware interface state over a network. See the [Robot Hardware Interface WebSockets API specification](../halsim_ws_core/doc/hardware_ws_api.md) for more details on the protocol.
|
||||
|
||||
## Configuration
|
||||
|
||||
The WebSockets client has a number of configuration options available through environment variables.
|
||||
|
||||
``HALSIMWS_HOST``: The host to connect to. Defaults to localhost.
|
||||
|
||||
``HALSIMWS_PORT``: The port number to connect to. Defaults to 3300.
|
||||
|
||||
``HALSIMWS_URI``: The URI path to connect to. Defaults to ``"/wpilibws"``.
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <WSProviderContainer.h>
|
||||
#include <WSProvider_Analog.h>
|
||||
#include <WSProvider_BuiltInAccelerometer.h>
|
||||
#include <WSProvider_DIO.h>
|
||||
#include <WSProvider_DriverStation.h>
|
||||
#include <WSProvider_Encoder.h>
|
||||
@@ -38,6 +39,7 @@ bool HALSimWSClient::Initialize() {
|
||||
|
||||
HALSimWSProviderAnalogIn::Initialize(registerFunc);
|
||||
HALSimWSProviderAnalogOut::Initialize(registerFunc);
|
||||
HALSimWSProviderBuiltInAccelerometer::Initialize(registerFunc);
|
||||
HALSimWSProviderDIO::Initialize(registerFunc);
|
||||
HALSimWSProviderDigitalPWM::Initialize(registerFunc);
|
||||
HALSimWSProviderDriverStation::Initialize(registerFunc);
|
||||
|
||||
5
simulation/halsim_ws_core/README.md
Normal file
5
simulation/halsim_ws_core/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# HAL WebSockets Core
|
||||
|
||||
This is the common WebSockets implementation shared by the [HAL WebSockets Client](../halsim_ws_client/) and [HAL WebSockets Server](../halsim_ws_server/) extensions. It is not a standalone extension.
|
||||
|
||||
These extensions provide a WebSockets API for transmitting robot hardware interface state over a network and implement the [Robot Hardware Interface WebSockets API specification](doc/hardware_ws_api.md). See the specification for more details on the protocol.
|
||||
330
simulation/halsim_ws_core/doc/hardware_ws_api.md
Normal file
330
simulation/halsim_ws_core/doc/hardware_ws_api.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# Robot Hardware Interface WebSockets API Specification
|
||||
|
||||
- [Summary](#summary)
|
||||
- [Motivation](#motivation)
|
||||
- [References](#references)
|
||||
- [Design](#design)
|
||||
- [WebSockets Protocol Configuration](#websockets-protocol-configuration)
|
||||
- [Text Data Frames](#text-data-frames)
|
||||
- [Robot Program Behavior](#robot-program-behavior)
|
||||
- [Hardware Behavior](#hardware-behavior)
|
||||
- [Hardware Messages](#hardware-messages)
|
||||
- [CAN Messages](#can-messages)
|
||||
- [RoboRIO Messages](#roborio-messages)
|
||||
- [Other Device Messages ("SimDevice")](#other-device-messages-simdevice)
|
||||
|
||||
## Summary
|
||||
|
||||
A WebSockets API for transmitting robot hardware interface state over a network. In typical use, one end of the connection is a robot program running in a desktop environment, and the other end of the connection is either a simulation engine, physical robot, or simulation dashboard GUI.
|
||||
|
||||
## Motivation
|
||||
|
||||
Provide a standard interface for 3rd party software to easily interface with a robot program running on a desktop computer. Currently this is possible in WPILib only by writing a custom C++ simulation plugin. Providing a standard text-based network interface to the "hardware" interface layer of a robot program lowers the barrier to entry for this type of development and enables unique capabilities such as direct control of a simple physical robot over a wireless link.
|
||||
|
||||
## References
|
||||
|
||||
- [RFC 6455](https://tools.ietf.org/html/rfc6455) The WebSocket Protocol
|
||||
- [RFC 7159](https://tools.ietf.org/html/rfc7159) The JavaScript Object Notation (JSON) Data Interchange Format
|
||||
|
||||
## Design
|
||||
|
||||
The messages in the protocol are based around typical representations of electronic physical devices, rather than higher level abstractions. As such, the messages e.g. represent an analog input as a voltage, rather than a “potentiometer” device. However, the “SimDevice” message can be used to communicate a higher level of abstraction device such as a gyro. What this means is that simulation engines are responsible for implementing the mapping from their system knowledge (e.g. a joint angle) into an electronic value (e.g. an analog voltage). See the Trades section for more discussion.
|
||||
|
||||
### WebSockets Protocol Configuration
|
||||
|
||||
Binary WebSocket frames are not used. Text WebSocket frames are JSON messages for human readability and ease of debugging.
|
||||
|
||||
Both clients and servers shall support unsecure connections (``ws:``) and may support secure connections (``wss:``). In a trusted network environment (e.g. a robot network), clients that support secure connections should fall back to an unsecure connection if a secure connection is not available.
|
||||
|
||||
The resource name for the websockets connection is ``/wpilibws``. Servers shall reject a second connection to the same resource location as a currently active connection, but may support multiple connections via additional resource names; the resource name is used to prevent duplicate client connections (such as when a web browser is used). Servers may support multiplexed HTTP file serving on the same port.
|
||||
|
||||
The unsecure standard server port number shall be 3300.
|
||||
|
||||
### Text Data Frames
|
||||
|
||||
Each WebSockets text data frame shall consist of a single JSON object (“message“).
|
||||
|
||||
Each message shall be a JSON object with three keys: a ``"type"`` key and lowercase string value describing the type of message, a ``"device"`` key and string value identifying the device, and a ``"data"`` key containing the message data as a JSON object.
|
||||
|
||||
The contents of the data object depends on the message type; see the sections for each message for details for the standard message types. The contents of the data object shall be transmitted as deltas (e.g. the message should only contain the values actually being changed). Clients and servers are free to ignore data values they don’t find useful, and/or transmit additional data values not specified here. Clients and servers shall ignore data values they don’t understand.
|
||||
|
||||
Data keys have a prefix of either ``"<"``, ``">"``, or ``"<>"``. This indicates whether the data value is an output from robot code (``"<"``), an input to the robot code (``">"``), or both (``"<>"``).
|
||||
|
||||
Clients and servers shall ignore JSON messages that:
|
||||
|
||||
* are not objects
|
||||
* have no ``"type"`` key, ``"device"`` key, or ``"data"`` key
|
||||
* have a ``"type"`` or ``"device"`` value that is not a string
|
||||
* have a ``"data"`` value that is not an object
|
||||
* have a ``"type"`` value that the client or server does not recognize
|
||||
|
||||
### Robot Program Behavior
|
||||
|
||||
The robot program may operate as either a client or a server. Generally, the robot program only pays attention to data values with ``">"`` or ``"<>"`` prefixes in received messages.
|
||||
|
||||
Upon initial connection, the robot program shall send a message for every initialized device in the program with the current state of that device (both input and output values). When a robot program removes a device, it shall send a message for that device with ``"<init"`` = false.
|
||||
|
||||
For example, if a robot program has an analog input configured for port 1, it will send a message upon initial connection with type ``"AI"``, device ``"1"``, ``"<init"`` true, and ``">voltage"`` set to an indeterminate value. The remote "hardware" would send messages with type ``"AI"``, device ``"1"``, and ``">voltage"`` set to the (simulated or real) voltage. When the robot program reads the voltage, it will read the last received ``">voltage"`` value.
|
||||
|
||||
The initial state includes joystick and driver station state.
|
||||
|
||||
### “Hardware“ Behavior
|
||||
|
||||
The “hardware“ (which might be a full-fledged 3D simulation engine, a physical robot, or an interactive GUI) is responsible for mapping the robot program’s inputs and outputs into the real (or virtual) world. For example, a robot program’s Analog Input 1 might show up as simply ``Analog Input #1`` on a GUI, connect to analog input port #1 on a physical robot, or map to a virtual potentiometer in a 3D simulation engine.
|
||||
|
||||
### Hardware Messages
|
||||
|
||||
| Type value | Description | Device value |
|
||||
| ----------------------- | -------------------------- | ------------------------- |
|
||||
| [``"Accel"``][] | Accelerometer | Arbitrary device name |
|
||||
| [``"AI"``][] | Analog input | Port index, e.g. "1", "2" |
|
||||
| [``"DIO"``][] | Digital input/output | Port index, e.g. "1", "2" |
|
||||
| [``"dPWM"``][] | Duty cycle output | Arbitrary device number |
|
||||
| [``"DriverStation"``][] | Driver station / FMS state | Blank |
|
||||
| [``"DutyCycle"``][] | Duty cycle input | Arbitrary device name |
|
||||
| [``"Encoder"``][] | Quadrature encoder | Arbitrary device number |
|
||||
| [``"Gyro"``][] | Gyro | Arbitrary device name |
|
||||
| [``"Joystick"``][] | Joystick data | Joystick number |
|
||||
| [``"PWM"``][] | PWM output | Port index, e.g. "1", "2" |
|
||||
| [``"Relay"``][] | Relay output | Port index, e.g. "1", "2" |
|
||||
|
||||
#### Accelerometer ("Accel")
|
||||
|
||||
[``"Accel"``]:#accelerometer-accel
|
||||
|
||||
A 3-axis accelerometer.
|
||||
|
||||
C++/Java implementation note: these are created as either BuiltInAccelerometer or SimDevice nodes where the device name is prefixed by ``"Accel:"``, for example ``"Accel:ADXL362[1]"``. The BuiltInAccelerometer uses a device name of ``"BuiltInAccel"``.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ------------ | ------- | ---------------------------------------------------- |
|
||||
| ``"<init"`` | Boolean | If accelerometer is initialized in the robot program |
|
||||
| ``"<range"`` | Float | Desired range in G’s |
|
||||
| ``">x"`` | Float | Acceleration in G’s |
|
||||
| ``">y"`` | Float | Acceleration in G’s |
|
||||
| ``">z"`` | Float | Acceleration in G’s |
|
||||
|
||||
#### Analog Input ("AI")
|
||||
|
||||
[``"AI"``]:#analog-input-ai
|
||||
|
||||
The basic analog input just reads a voltage.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| -------------- | ------- | --------------------------------------------------- |
|
||||
| ``"<init"`` | Boolean | If analog input is initialized in the robot program |
|
||||
| ``">voltage"`` | Float | Input voltage, in volts |
|
||||
|
||||
#### Digital Input/Output ("DIO")
|
||||
|
||||
[``"DIO"``]:#digital-inputoutput-dio
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ------------- | ------- | ------------------------------------------ |
|
||||
| ``"<init"`` | Boolean | If DIO is initialized in the robot program |
|
||||
| ``"<input"`` | Boolean | True if input, false if output |
|
||||
| ``"<>value"`` | Boolean | Input or output state |
|
||||
|
||||
#### Duty Cycle Output ("dPWM")
|
||||
|
||||
[``"dPWM"``]:#duty-cycle-output-dpwm
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ----------------- | ------- | --------------------------------------------- |
|
||||
| ``"<init"`` | Boolean | If output is initialized in the robot program |
|
||||
| ``"<duty_cycle"`` | Float | Duty cycle % (0.0 to 1.0) |
|
||||
| ``"<dio_pin"`` | Integer | DIO pin number |
|
||||
|
||||
#### Driver Station ("DriverStation")
|
||||
|
||||
[``"DriverStation"``]:#driver-station-driverstation
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ----------------- | ------- | ------------------------------------------------ |
|
||||
| ``">new_data"`` | Boolean | One shot. If set to true in a message, notifies the robot program that new DS and Joystick data is available. |
|
||||
| ``">enabled"`` | Boolean | True to enable the robot program |
|
||||
| ``">autonomous"`` | Boolean | True for autonomous mode; false for teleoperated mode |
|
||||
| ``">test"`` | Boolean | True for test mode; false for other modes |
|
||||
| ``">estop"`` | Boolean | True to emergency stop (no motor outputs) |
|
||||
| ``">fms"`` | Boolean | True if the DS is connected to a Field Management System (FMS) |
|
||||
| ``">ds"`` | Boolean | True if a DS application is connected |
|
||||
| ``">station"`` | String | Station color and number; supported values are ``"red1"``, ``"red2"``, ``"red3"``, ``"blue1"``, ``"blue2"``, ``"blue3"``. |
|
||||
| ``">match_time"`` | Float | Match time countdown, in seconds, for each match period (e.g. for 15 second period, starts at 15 and counts down to 0). If not in a match, -1. |
|
||||
| ``">game_data"`` | String | Game-specific data; arbitrary string contents |
|
||||
|
||||
#### Duty Cycle Input ("DutyCycle")
|
||||
|
||||
[``"DutyCycle"``]:#duty-cycle-input-dutycycle
|
||||
|
||||
Duty Cycle inputs are commonly used for absolute encoders. The position is accumulated over multiple rotations.
|
||||
|
||||
C++/Java implementation note: these are created as SimDevice nodes where the device name is prefixed by ``"DutyCycle:"``, for example ``"DutyCycle:DutyCycleEncoder[1]"``.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ---------------- | ------- | -------------------------------- |
|
||||
| ``">connected"`` | Boolean | True if the encoder is connected |
|
||||
| ``">position"`` | Float | The position in rotations |
|
||||
|
||||
#### Quadrature Encoder ("Encoder")
|
||||
|
||||
[``"Encoder"``]:#quadrature-encoder-encoder
|
||||
|
||||
A relative encoder. For absolute encoders, use ``"DutyCycle"``.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| --------------------- | ------- | --------------------------------------------------- |
|
||||
| ``"<init"`` | Boolean | If encoder is initialized in the robot program |
|
||||
| ``"<channel_a"`` | Integer | Digital channel number for “A” phase |
|
||||
| ``"<channel_b"`` | Integer | Digital channel number for “B” phase |
|
||||
| ``"<samples_to_avg"`` | Integer | Number of samples to average for period measurement |
|
||||
| ``">count"`` | Integer | Accumulated count (pulses) |
|
||||
| ``">period"`` | Float | Period between pulses in seconds |
|
||||
|
||||
#### Gyro ("Gyro")
|
||||
|
||||
[``"Gyro"``]:#gyro-gyro
|
||||
|
||||
A single axis or 3-axis gyro. Single axis gyros only use the X angle parameter.
|
||||
|
||||
C++/Java implementation note: these are created as SimDevice nodes where the device name is prefixed by ``"Gyro:"``, for example ``"Gyro:ADXRS450[1]"``.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ----------------- | ------- | --------------------------------------------------------- |
|
||||
| ``"<init"`` | Boolean | If gyro is initialized in the robot program |
|
||||
| ``"<range"`` | Float | Gyro range in degrees/second (optional) |
|
||||
| ``">connected"`` | Boolean | True if the gyro is connected |
|
||||
| ``">angle_x"`` | Float | The gyro angle in degrees |
|
||||
| ``">angle_y"`` | Float | The gyro angle in degrees |
|
||||
| ``">angle_z"`` | Float | The gyro angle in degrees |
|
||||
| ``">rate_x"`` | Float | The current gyro angular rate of change in degrees/second |
|
||||
| ``">rate_y"`` | Float | The current gyro angular rate of change in degrees/second |
|
||||
| ``">rate_z"`` | Float | The current gyro angular rate of change in degrees/second |
|
||||
|
||||
#### Joystick Data ("Joystick")
|
||||
|
||||
[``"Joystick"``]:#joystick-data-joystick
|
||||
|
||||
Joystick data is an input to the robot program and should be updated for each input joystick on a periodic basis. To enable synchronous updates of joystick and driver station state, joystick data is not made visible to the robot program until a DriverStation message with ``">new_data"`` set to true is received.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ------------------- | ---------------- | --- |
|
||||
| ``">axes"`` | Array of float | One array element per axis; value is -1 to 1 range |
|
||||
| ``">povs"`` | Array of integer | One array element per POV; value is angle in degrees of the POV (e.g. 0, 90, 315) if pressed, or -1 if the POV is not pressed |
|
||||
| ``">buttons"`` | Array of boolean | One array element per button; true if button is pressed, false if button is released |
|
||||
| ``"<rumble_left"`` | Float | Left rumble, value is 0-1 range |
|
||||
| ``"<rumble_right"`` | Float | Right rumble, value is 0-1 range |
|
||||
|
||||
#### PWM Output ("PWM")
|
||||
|
||||
[``"PWM"``]:#pwm-output-pwm
|
||||
|
||||
PWMs may be used to control either speed controllers or servos. Typically only one of either ``"<speed"`` (for a speed controller) or ``"<position"`` (for a servo) is used for a given PWM.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| --------------- | ------- | ------------------------------------------ |
|
||||
| ``"<init"`` | Boolean | If PWM is initialized in the robot program |
|
||||
| ``"<speed"`` | Float | Speed, -1.0 to 1.0 range |
|
||||
| ``"<position"`` | Float | Servo position, 0.0 to 1.0 range |
|
||||
|
||||
#### Relay Output ("Relay")
|
||||
|
||||
[``"Relay"``]:#relay-output-relay
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| --------------- | ------- | -------------------------------------------------------------- |
|
||||
| ``"<init_fwd"`` | Boolean | If relay forward direction is initialized in the robot program |
|
||||
| ``"<init_rev"`` | Boolean | If relay reverse direction is initialized in the robot program |
|
||||
| ``"<fwd"`` | Boolean | True if forward direction is enabled |
|
||||
| ``"<rev"`` | Boolean | True if reverse direction is enabled |
|
||||
|
||||
### CAN Messages
|
||||
|
||||
CAN messages all use a device value of ``"DeviceType[Number]"``, where the DeviceType is the vendor-specific CAN device type (motor controller class) name and Number is the CAN device number (the user-visible number passed to the device constructor).
|
||||
|
||||
Many of the CAN messages use the same data key/values as other standard messages. They are separately namespaced to make it easier for implementations to separate them from main robot controller messages.
|
||||
|
||||
C++/Java implementation note: these are created as SimDevice nodes where the device name is prefixed by the message name and ``":"``, for example ``"CANMotor:Controller[1]"``.
|
||||
|
||||
#### CANMotor
|
||||
|
||||
Only one of ``"supplyCurrent"`` or ``"motorCurrent"`` should be sent by the hardware; the other value should be set to zero. If ``"busVoltage"`` is not simulated it should also be set to zero.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| -------------------- | ---------------- | ------------------------------------------------ |
|
||||
| ``"<percentOutput"`` | Integer or Float | Percent output (-1 to 1 range) |
|
||||
| ``">supplyCurrent"`` | Float | The supply current in amps as simulated/measured |
|
||||
| ``">motorCurrent"`` | Float | The motor current in amps as simulated/measured |
|
||||
| ``">busVoltage"`` | Float | The bus voltage as simulated/measured |
|
||||
|
||||
#### CANEncoder
|
||||
|
||||
A relative encoder (typically quadrature). For absolute encoders, use ``"CANDutyCycle"``.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| --------------- | ----- | -------------------------------- |
|
||||
| ``">position"`` | Float | Relative position, in rotations |
|
||||
| ``">velocity"`` | Float | Velocity in rotations per second |
|
||||
|
||||
#### CANGyro
|
||||
|
||||
Uses the same keys as [``"Gyro"``][].
|
||||
|
||||
#### CANAccel
|
||||
|
||||
Uses the same keys as [``"Accel"``][].
|
||||
|
||||
#### CANAIn
|
||||
|
||||
Uses the same keys as [``"AI"``][].
|
||||
|
||||
The device value may have a suffix for multiple inputs on a single CAN device; ``"-"`` followed by the input name or number is recommended but not required.
|
||||
|
||||
#### CANDIO
|
||||
|
||||
Uses the same keys as [``"DIO"``][]. This is commonly used for limit switches.
|
||||
|
||||
The device value may have a suffix for multiple inputs on a single CAN device; ``"-"`` followed by the input name or number is recommended but not required.
|
||||
|
||||
#### CANDutyCycle
|
||||
|
||||
Uses the same keys as [``"DutyCycle"``][].
|
||||
|
||||
The device value may have a suffix for multiple inputs on a single CAN device; ``"-"`` followed by the input name or number is recommended but not required.
|
||||
|
||||
### RoboRIO Messages
|
||||
|
||||
These messages are specific to the RoboRIO and will not likely be found in other simulators or hardware devices except in a very limited capacity (e.g. Vin voltage).
|
||||
|
||||
| Type value | Description | Device value |
|
||||
| ----------------- | ------------------- | ------------ |
|
||||
| [``"RoboRIO"``][] | RoboRIO information | Blank |
|
||||
|
||||
#### RoboRIO
|
||||
|
||||
[``"RoboRIO"``]:#roborio
|
||||
|
||||
The RoboRIO.
|
||||
|
||||
| Data Key | Type | Description |
|
||||
| ------------------ | ------- | ------------------------------------------- |
|
||||
| ``">fpga_button"`` | Boolean | FPGA button state |
|
||||
| ``">vin_voltage"`` | Float | Vin rail voltage |
|
||||
| ``">vin_current"`` | Float | Vin rail current |
|
||||
| ``">6v_voltage"`` | Float | 6V rail voltage |
|
||||
| ``">6v_current"`` | Float | 6V rail current |
|
||||
| ``">6v_active"`` | Boolean | True if 6V rail active, false if inactive |
|
||||
| ``">6v_faults"`` | Integer | Number of faults on 6V rail |
|
||||
| ``">5v_voltage"`` | Float | 5V rail voltage |
|
||||
| ``">5v_current"`` | Float | 5V rail current |
|
||||
| ``">5v_active"`` | Boolean | True if 5V rail active, false if inactive |
|
||||
| ``">5v_faults"`` | Integer | Number of faults on 5V rail |
|
||||
| ``">3v3_voltage"`` | Float | 3.3V rail voltage |
|
||||
| ``">3v3_current"`` | Float | 3.3V rail current |
|
||||
| ``">3v3_active"`` | Boolean | True if 3.3V rail active, false if inactive |
|
||||
| ``">3v3_faults"`` | Integer | Number of faults on 3.3V rail |
|
||||
|
||||
### Other Device Messages ("SimDevice")
|
||||
|
||||
[``"SimDevice"``]:#other-device-messages-simdevice
|
||||
|
||||
A device type of ``"SimDevice"`` may be used for extending the protocol for arbitrary complex devices. The device value is an arbitrary string, generally ``"DeviceName[Port/Index]"``, and the data keys are arbitrary and device-dependent.
|
||||
@@ -0,0 +1,97 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WSProvider_BuiltInAccelerometer.h"
|
||||
|
||||
#include <hal/Ports.h>
|
||||
#include <hal/simulation/AccelerometerData.h>
|
||||
|
||||
#define REGISTER(halsim, jsonid, ctype, haltype) \
|
||||
HALSIM_RegisterAccelerometer##halsim##Callback( \
|
||||
0, \
|
||||
[](const char* name, void* param, const struct HAL_Value* value) { \
|
||||
static_cast<HALSimWSProviderBuiltInAccelerometer*>(param) \
|
||||
->ProcessHalCallback( \
|
||||
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
|
||||
}, \
|
||||
this, true)
|
||||
namespace wpilibws {
|
||||
HALSimWSProviderBuiltInAccelerometer::HALSimWSProviderBuiltInAccelerometer()
|
||||
: HALSimWSHalProvider("Accel/BuiltInAccel", "Accel") {
|
||||
m_deviceId = "BuiltInAccel";
|
||||
}
|
||||
|
||||
void HALSimWSProviderBuiltInAccelerometer::Initialize(
|
||||
WSRegisterFunc webRegisterFunc) {
|
||||
webRegisterFunc("Accel/BuiltInAccel",
|
||||
std::make_unique<HALSimWSProviderBuiltInAccelerometer>());
|
||||
}
|
||||
|
||||
HALSimWSProviderBuiltInAccelerometer::~HALSimWSProviderBuiltInAccelerometer() {
|
||||
DoCancelCallbacks();
|
||||
}
|
||||
|
||||
void HALSimWSProviderBuiltInAccelerometer::RegisterCallbacks() {
|
||||
m_activeCbKey = REGISTER(Active, "<init", bool, boolean);
|
||||
m_rangeCbKey = HALSIM_RegisterAccelerometerRangeCallback(
|
||||
0,
|
||||
[](const char* name, void* param, const struct HAL_Value* value) {
|
||||
double range;
|
||||
switch (value->data.v_int) {
|
||||
case 0:
|
||||
range = 2;
|
||||
break;
|
||||
case 1:
|
||||
range = 4;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
range = 8;
|
||||
break;
|
||||
}
|
||||
static_cast<HALSimWSProviderBuiltInAccelerometer*>(param)
|
||||
->ProcessHalCallback({{"<range", range}});
|
||||
},
|
||||
this, true);
|
||||
m_xCbKey = REGISTER(X, ">x", double, double);
|
||||
m_yCbKey = REGISTER(Y, ">y", double, double);
|
||||
m_zCbKey = REGISTER(Z, ">z", double, double);
|
||||
}
|
||||
|
||||
void HALSimWSProviderBuiltInAccelerometer::CancelCallbacks() {
|
||||
DoCancelCallbacks();
|
||||
}
|
||||
|
||||
void HALSimWSProviderBuiltInAccelerometer::DoCancelCallbacks() {
|
||||
HALSIM_CancelAccelerometerActiveCallback(0, m_activeCbKey);
|
||||
HALSIM_CancelAccelerometerRangeCallback(0, m_rangeCbKey);
|
||||
HALSIM_CancelAccelerometerXCallback(0, m_xCbKey);
|
||||
HALSIM_CancelAccelerometerYCallback(0, m_yCbKey);
|
||||
HALSIM_CancelAccelerometerZCallback(0, m_zCbKey);
|
||||
|
||||
m_activeCbKey = 0;
|
||||
m_rangeCbKey = 0;
|
||||
m_xCbKey = 0;
|
||||
m_yCbKey = 0;
|
||||
m_zCbKey = 0;
|
||||
}
|
||||
|
||||
void HALSimWSProviderBuiltInAccelerometer::OnNetValueChanged(
|
||||
const wpi::json& json) {
|
||||
wpi::json::const_iterator it;
|
||||
if ((it = json.find(">x")) != json.end()) {
|
||||
HALSIM_SetAccelerometerX(0, it.value());
|
||||
}
|
||||
if ((it = json.find(">y")) != json.end()) {
|
||||
HALSIM_SetAccelerometerY(0, it.value());
|
||||
}
|
||||
if ((it = json.find(">z")) != json.end()) {
|
||||
HALSIM_SetAccelerometerZ(0, it.value());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace wpilibws
|
||||
@@ -92,7 +92,7 @@ void HALSimWSProviderDriverStation::RegisterCallbacks() {
|
||||
},
|
||||
this, true);
|
||||
|
||||
m_matchTimeCbKey = REGISTER(MatchTime, "<match_time", double, double);
|
||||
m_matchTimeCbKey = REGISTER(MatchTime, ">match_time", double, double);
|
||||
}
|
||||
|
||||
void HALSimWSProviderDriverStation::CancelCallbacks() { DoCancelCallbacks(); }
|
||||
@@ -160,6 +160,14 @@ void HALSimWSProviderDriverStation::OnNetValueChanged(const wpi::json& json) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((it = json.find(">match_time")) != json.end()) {
|
||||
HALSIM_SetDriverStationMatchTime(it.value());
|
||||
}
|
||||
if ((it = json.find(">game_data")) != json.end()) {
|
||||
HALSIM_SetGameSpecificMessage(
|
||||
it.value().get_ref<const std::string&>().c_str());
|
||||
}
|
||||
|
||||
// Only notify usercode if we get the new data message
|
||||
if ((it = json.find(">new_data")) != json.end()) {
|
||||
HALSIM_NotifyDriverStationNewData();
|
||||
|
||||
@@ -57,9 +57,19 @@ void HALSimWSProviderJoystick::RegisterCallbacks() {
|
||||
buttonsValues.push_back(((buttons.buttons >> i) & 0x1) == 1);
|
||||
}
|
||||
|
||||
// Rumble data
|
||||
int64_t outputs = 0;
|
||||
int32_t leftRumble = 0;
|
||||
int32_t rightRumble = 0;
|
||||
HALSIM_GetJoystickOutputs(provider->GetChannel(), &outputs, &leftRumble,
|
||||
&rightRumble);
|
||||
|
||||
payload[">axes"] = axesValues;
|
||||
payload[">povs"] = povsValues;
|
||||
payload[">buttons"] = buttonsValues;
|
||||
payload["<outputs"] = outputs;
|
||||
payload["<rumble_left"] = leftRumble;
|
||||
payload["<rumble_right"] = rightRumble;
|
||||
|
||||
provider->ProcessHalCallback(payload);
|
||||
},
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include "WSProvider_SimDevice.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <hal/Ports.h>
|
||||
|
||||
namespace wpilibws {
|
||||
@@ -70,9 +73,27 @@ void HALSimWSProviderSimDevice::OnNetValueChanged(const wpi::json& json) {
|
||||
case HAL_DOUBLE:
|
||||
value.data.v_double = it.value();
|
||||
break;
|
||||
case HAL_ENUM:
|
||||
case HAL_ENUM: {
|
||||
if (it->is_string()) {
|
||||
auto& options = vd->second->options;
|
||||
auto& str = it.value().get_ref<const std::string&>();
|
||||
auto optionIt =
|
||||
std::find_if(options.begin(), options.end(),
|
||||
[&](const std::string& v) { return v == str; });
|
||||
if (optionIt != options.end())
|
||||
value.data.v_enum = optionIt - options.begin();
|
||||
} else if (it->is_number()) {
|
||||
auto& values = vd->second->optionValues;
|
||||
double num = it.value();
|
||||
auto valueIt = std::find_if(
|
||||
values.begin(), values.end(),
|
||||
[&](double v) { return std::fabs(v - num) < 1e-4; });
|
||||
if (valueIt != values.end())
|
||||
value.data.v_enum = valueIt - values.begin();
|
||||
}
|
||||
value.data.v_enum = it.value();
|
||||
break;
|
||||
}
|
||||
case HAL_INT:
|
||||
value.data.v_int = it.value();
|
||||
break;
|
||||
@@ -88,15 +109,50 @@ void HALSimWSProviderSimDevice::OnNetValueChanged(const wpi::json& json) {
|
||||
}
|
||||
}
|
||||
|
||||
void HALSimWSProviderSimDevice::OnValueCreatedStatic(
|
||||
const char* name, void* param, HAL_SimValueHandle handle, int32_t direction,
|
||||
const struct HAL_Value* value) {
|
||||
(reinterpret_cast<HALSimWSProviderSimDevice*>(param))
|
||||
->OnValueCreated(name, handle, direction, value);
|
||||
}
|
||||
|
||||
void HALSimWSProviderSimDevice::OnValueCreated(const char* name,
|
||||
HAL_SimValueHandle handle,
|
||||
HAL_Bool readonly,
|
||||
int32_t direction,
|
||||
const struct HAL_Value* value) {
|
||||
wpi::Twine key = wpi::Twine(readonly ? "<" : "<>") + name;
|
||||
const char* prefix = "";
|
||||
if (name[0] != '<' && name[0] != '>') {
|
||||
switch (direction) {
|
||||
case HAL_SimValueInput:
|
||||
prefix = ">";
|
||||
break;
|
||||
case HAL_SimValueOutput:
|
||||
prefix = "<";
|
||||
break;
|
||||
case HAL_SimValueBidir:
|
||||
prefix = "<>";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::string key = (wpi::Twine(prefix) + name).str();
|
||||
auto data = std::make_unique<SimDeviceValueData>();
|
||||
data->device = this;
|
||||
data->handle = handle;
|
||||
data->key = key.str();
|
||||
data->key = key;
|
||||
if (value->type == HAL_ENUM) {
|
||||
int32_t numOptions = 0;
|
||||
|
||||
const char** options = HALSIM_GetSimValueEnumOptions(handle, &numOptions);
|
||||
data->options.reserve(numOptions);
|
||||
for (int32_t i = 0; i < numOptions; ++i)
|
||||
data->options.emplace_back(options[i]);
|
||||
|
||||
const double* values =
|
||||
HALSIM_GetSimValueEnumDoubleValues(handle, &numOptions);
|
||||
data->optionValues.assign(values, values + numOptions);
|
||||
}
|
||||
data->valueType = value->type;
|
||||
|
||||
auto param = data.get();
|
||||
@@ -109,7 +165,14 @@ void HALSimWSProviderSimDevice::OnValueCreated(const char* name,
|
||||
int32_t cbKey = HALSIM_RegisterSimValueChangedCallback(
|
||||
handle, param, HALSimWSProviderSimDevice::OnValueChangedStatic, true);
|
||||
|
||||
m_simValueChangedCbKeys[key.str()] = cbKey;
|
||||
m_simValueChangedCbKeys[key] = cbKey;
|
||||
}
|
||||
|
||||
void HALSimWSProviderSimDevice::OnValueChangedStatic(
|
||||
const char* name, void* param, HAL_SimValueHandle handle, int32_t direction,
|
||||
const struct HAL_Value* value) {
|
||||
auto valueData = (reinterpret_cast<SimDeviceValueData*>(param));
|
||||
valueData->device->OnValueChanged(valueData, value);
|
||||
}
|
||||
|
||||
void HALSimWSProviderSimDevice::OnValueChanged(SimDeviceValueData* valueData,
|
||||
@@ -123,9 +186,14 @@ void HALSimWSProviderSimDevice::OnValueChanged(SimDeviceValueData* valueData,
|
||||
case HAL_DOUBLE:
|
||||
ProcessHalCallback({{valueData->key, value->data.v_double}});
|
||||
break;
|
||||
case HAL_ENUM:
|
||||
ProcessHalCallback({{valueData->key, value->data.v_enum}});
|
||||
case HAL_ENUM: {
|
||||
int v = value->data.v_enum;
|
||||
if (v >= 0 && v < static_cast<int>(valueData->optionValues.size()))
|
||||
ProcessHalCallback({{valueData->key, valueData->optionValues[v]}});
|
||||
else if (v >= 0 && v < static_cast<int>(valueData->options.size()))
|
||||
ProcessHalCallback({{valueData->key, valueData->options[v]}});
|
||||
break;
|
||||
}
|
||||
case HAL_INT:
|
||||
ProcessHalCallback({{valueData->key, value->data.v_int}});
|
||||
break;
|
||||
@@ -142,7 +210,7 @@ void HALSimWSProviderSimDevice::ProcessHalCallback(const wpi::json& payload) {
|
||||
auto ws = m_ws.lock();
|
||||
if (ws) {
|
||||
wpi::json netValue = {
|
||||
{"type", "SimDevices"}, {"device", m_deviceId}, {"data", payload}};
|
||||
{"type", m_type}, {"device", m_deviceId}, {"data", payload}};
|
||||
ws->OnSimValueChanged(netValue);
|
||||
}
|
||||
}
|
||||
@@ -151,10 +219,19 @@ HALSimWSProviderSimDevices::~HALSimWSProviderSimDevices() { CancelCallbacks(); }
|
||||
|
||||
void HALSimWSProviderSimDevices::DeviceCreatedCallback(
|
||||
const char* name, HAL_SimDeviceHandle handle) {
|
||||
auto key = (wpi::Twine("SimDevices/") + name).str();
|
||||
auto dev = std::make_shared<HALSimWSProviderSimDevice>(
|
||||
handle, key, wpi::Twine(name).str());
|
||||
m_providers.Add(key, dev);
|
||||
// Map "Accel:Foo" -> type=Accel, device=Foo
|
||||
auto [type, id] = wpi::StringRef{name}.split(':');
|
||||
std::shared_ptr<HALSimWSProviderSimDevice> dev;
|
||||
if (id.empty()) {
|
||||
auto key = ("SimDevice/" + type).str();
|
||||
dev = std::make_shared<HALSimWSProviderSimDevice>(handle, key, "SimDevice",
|
||||
type);
|
||||
m_providers.Add(key, dev);
|
||||
} else {
|
||||
auto key = (type + "/" + id).str();
|
||||
dev = std::make_shared<HALSimWSProviderSimDevice>(handle, key, type, id);
|
||||
m_providers.Add(key, dev);
|
||||
}
|
||||
|
||||
if (m_ws) {
|
||||
m_exec->Call([this, dev]() { dev->OnNetworkConnected(GetWSConnection()); });
|
||||
|
||||
@@ -47,7 +47,7 @@ class HALSimWSBaseProvider {
|
||||
std::string m_key;
|
||||
|
||||
std::string m_type;
|
||||
std::string m_deviceId = "";
|
||||
std::string m_deviceId;
|
||||
};
|
||||
|
||||
} // namespace wpilibws
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/* Copyright (c) 2020 FIRST. All Rights Reserved. */
|
||||
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
||||
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
||||
/* the project. */
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "WSHalProviders.h"
|
||||
|
||||
namespace wpilibws {
|
||||
class HALSimWSProviderBuiltInAccelerometer : public HALSimWSHalProvider {
|
||||
public:
|
||||
HALSimWSProviderBuiltInAccelerometer();
|
||||
static void Initialize(WSRegisterFunc webRegisterFunc);
|
||||
|
||||
using HALSimWSHalProvider::HALSimWSHalProvider;
|
||||
~HALSimWSProviderBuiltInAccelerometer();
|
||||
|
||||
void OnNetValueChanged(const wpi::json& json) override;
|
||||
|
||||
protected:
|
||||
void RegisterCallbacks() override;
|
||||
void CancelCallbacks() override;
|
||||
void DoCancelCallbacks();
|
||||
|
||||
private:
|
||||
int32_t m_activeCbKey = 0;
|
||||
int32_t m_rangeCbKey = 0;
|
||||
int32_t m_xCbKey = 0;
|
||||
int32_t m_yCbKey = 0;
|
||||
int32_t m_zCbKey = 0;
|
||||
};
|
||||
} // namespace wpilibws
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <hal/SimDevice.h>
|
||||
#include <hal/simulation/SimDeviceData.h>
|
||||
@@ -27,14 +28,17 @@ struct SimDeviceValueData {
|
||||
HALSimWSProviderSimDevice* device;
|
||||
HAL_SimValueHandle handle;
|
||||
std::string key;
|
||||
std::vector<std::string> options;
|
||||
std::vector<double> optionValues;
|
||||
HAL_Type valueType;
|
||||
};
|
||||
|
||||
class HALSimWSProviderSimDevice : public HALSimWSBaseProvider {
|
||||
public:
|
||||
HALSimWSProviderSimDevice(HAL_SimDeviceHandle handle, const std::string& key,
|
||||
const std::string& type,
|
||||
const std::string& deviceId)
|
||||
: HALSimWSBaseProvider(key, "SimDevices"), m_handle(handle) {
|
||||
: HALSimWSBaseProvider(key, type), m_handle(handle) {
|
||||
m_deviceId = deviceId;
|
||||
}
|
||||
|
||||
@@ -51,20 +55,14 @@ class HALSimWSProviderSimDevice : public HALSimWSBaseProvider {
|
||||
|
||||
private:
|
||||
static void OnValueCreatedStatic(const char* name, void* param,
|
||||
HAL_SimValueHandle handle, HAL_Bool readonly,
|
||||
const struct HAL_Value* value) {
|
||||
(reinterpret_cast<HALSimWSProviderSimDevice*>(param))
|
||||
->OnValueCreated(name, handle, readonly, value);
|
||||
}
|
||||
HAL_SimValueHandle handle, int32_t direction,
|
||||
const struct HAL_Value* value);
|
||||
void OnValueCreated(const char* name, HAL_SimValueHandle handle,
|
||||
HAL_Bool readonly, const struct HAL_Value* value);
|
||||
int32_t direction, const struct HAL_Value* value);
|
||||
|
||||
static void OnValueChangedStatic(const char* name, void* param,
|
||||
HAL_SimValueHandle handle, HAL_Bool readonly,
|
||||
const struct HAL_Value* value) {
|
||||
auto valueData = (reinterpret_cast<SimDeviceValueData*>(param));
|
||||
valueData->device->OnValueChanged(valueData, value);
|
||||
}
|
||||
HAL_SimValueHandle handle, int32_t direction,
|
||||
const struct HAL_Value* value);
|
||||
void OnValueChanged(SimDeviceValueData* valueData,
|
||||
const struct HAL_Value* value);
|
||||
|
||||
|
||||
15
simulation/halsim_ws_server/README.md
Normal file
15
simulation/halsim_ws_server/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# HAL WebSockets Server
|
||||
|
||||
This is an extension that provides a server version of a WebSockets API for transmitting robot hardware interface state over a network. See the [Robot Hardware Interface WebSockets API specification](../halsim_ws_core/doc/hardware_ws_api.md) for more details on the protocol.
|
||||
|
||||
## Configuration
|
||||
|
||||
The WebSockets server has a number of configuration options available through environment variables.
|
||||
|
||||
``HALSIMWS_SYSROOT``: The local directory to serve non-websocket HTTP content from (e.g. HTML files) starting from `/`. Defaults to the `sim` subdirectory of the current working directory.
|
||||
|
||||
``HALSIMWS_USERROOT``: The local directory to serve non-websocket HTTP content from (e.g. HTML files) starting from `/user/`. Defaults to the `sim/user` subdirectory of the current working directory.
|
||||
|
||||
``HALSIMWS_PORT``: The port number to listen at. Defaults to 3300.
|
||||
|
||||
``HALSIMWS_URI``: The URI path to use for WebSockets connections. Defaults to ``"/wpilibws"``.
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include <WSProviderContainer.h>
|
||||
#include <WSProvider_Analog.h>
|
||||
#include <WSProvider_BuiltInAccelerometer.h>
|
||||
#include <WSProvider_DIO.h>
|
||||
#include <WSProvider_DriverStation.h>
|
||||
#include <WSProvider_Encoder.h>
|
||||
@@ -37,6 +38,7 @@ bool HALSimWSServer::Initialize() {
|
||||
|
||||
HALSimWSProviderAnalogIn::Initialize(registerFunc);
|
||||
HALSimWSProviderAnalogOut::Initialize(registerFunc);
|
||||
HALSimWSProviderBuiltInAccelerometer::Initialize(registerFunc);
|
||||
HALSimWSProviderDIO::Initialize(registerFunc);
|
||||
HALSimWSProviderDigitalPWM::Initialize(registerFunc);
|
||||
HALSimWSProviderDriverStation::Initialize(registerFunc);
|
||||
|
||||
@@ -16,13 +16,14 @@ using namespace frc;
|
||||
|
||||
ADXL345_I2C::ADXL345_I2C(I2C::Port port, Range range, int deviceAddress)
|
||||
: m_i2c(port, deviceAddress),
|
||||
m_simDevice("ADXL345_I2C", port, deviceAddress) {
|
||||
m_simDevice("Accel:ADXL345_I2C", port, deviceAddress) {
|
||||
if (m_simDevice) {
|
||||
m_simRange =
|
||||
m_simDevice.CreateEnum("Range", true, {"2G", "4G", "8G", "16G"}, 0);
|
||||
m_simX = m_simDevice.CreateDouble("X Accel", false, 0.0);
|
||||
m_simY = m_simDevice.CreateDouble("Y Accel", false, 0.0);
|
||||
m_simZ = m_simDevice.CreateDouble("Z Accel", false, 0.0);
|
||||
m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
|
||||
{"2G", "4G", "8G", "16G"},
|
||||
{2.0, 4.0, 8.0, 16.0}, 0);
|
||||
m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
|
||||
m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
|
||||
m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
|
||||
}
|
||||
// Turn on the measurements
|
||||
m_i2c.Write(kPowerCtlRegister, kPowerCtl_Measure);
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
using namespace frc;
|
||||
|
||||
ADXL345_SPI::ADXL345_SPI(SPI::Port port, ADXL345_SPI::Range range)
|
||||
: m_spi(port), m_simDevice("ADXL345_SPI", port) {
|
||||
: m_spi(port), m_simDevice("Accel:ADXL345_SPI", port) {
|
||||
if (m_simDevice) {
|
||||
m_simRange =
|
||||
m_simDevice.CreateEnum("Range", true, {"2G", "4G", "8G", "16G"}, 0);
|
||||
m_simX = m_simDevice.CreateDouble("X Accel", false, 0.0);
|
||||
m_simY = m_simDevice.CreateDouble("Y Accel", false, 0.0);
|
||||
m_simZ = m_simDevice.CreateDouble("Z Accel", false, 0.0);
|
||||
m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
|
||||
{"2G", "4G", "8G", "16G"},
|
||||
{2.0, 4.0, 8.0, 16.0}, 0);
|
||||
m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
|
||||
m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
|
||||
m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
|
||||
}
|
||||
m_spi.SetClockRate(500000);
|
||||
m_spi.SetMSBFirst();
|
||||
|
||||
@@ -35,13 +35,14 @@ static constexpr int kPowerCtl_Measure = 0x02;
|
||||
ADXL362::ADXL362(Range range) : ADXL362(SPI::Port::kOnboardCS1, range) {}
|
||||
|
||||
ADXL362::ADXL362(SPI::Port port, Range range)
|
||||
: m_spi(port), m_simDevice("ADXL362", port) {
|
||||
: m_spi(port), m_simDevice("Accel:ADXL362", port) {
|
||||
if (m_simDevice) {
|
||||
m_simRange =
|
||||
m_simDevice.CreateEnum("Range", true, {"2G", "4G", "8G", "16G"}, 0);
|
||||
m_simX = m_simDevice.CreateDouble("X Accel", false, 0.0);
|
||||
m_simY = m_simDevice.CreateDouble("Y Accel", false, 0.0);
|
||||
m_simZ = m_simDevice.CreateDouble("Z Accel", false, 0.0);
|
||||
m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
|
||||
{"2G", "4G", "8G", "16G"},
|
||||
{2.0, 4.0, 8.0, 16.0}, 0);
|
||||
m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
|
||||
m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
|
||||
m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
|
||||
}
|
||||
|
||||
m_spi.SetClockRate(3000000);
|
||||
|
||||
@@ -32,10 +32,11 @@ static constexpr int kPIDRegister = 0x0C;
|
||||
ADXRS450_Gyro::ADXRS450_Gyro() : ADXRS450_Gyro(SPI::kOnboardCS0) {}
|
||||
|
||||
ADXRS450_Gyro::ADXRS450_Gyro(SPI::Port port)
|
||||
: m_spi(port), m_port(port), m_simDevice("ADXRS450_Gyro", port) {
|
||||
: m_spi(port), m_port(port), m_simDevice("Gyro:ADXRS450", port) {
|
||||
if (m_simDevice) {
|
||||
m_simAngle = m_simDevice.CreateDouble("Angle", false, 0.0);
|
||||
m_simRate = m_simDevice.CreateDouble("Rate", false, 0.0);
|
||||
m_simAngle =
|
||||
m_simDevice.CreateDouble("angle_x", hal::SimDevice::kInput, 0.0);
|
||||
m_simRate = m_simDevice.CreateDouble("rate_x", hal::SimDevice::kInput, 0.0);
|
||||
}
|
||||
|
||||
m_spi.SetClockRate(3000000);
|
||||
|
||||
@@ -54,13 +54,14 @@ DutyCycleEncoder::DutyCycleEncoder(std::shared_ptr<DigitalSource> digitalSource)
|
||||
}
|
||||
|
||||
void DutyCycleEncoder::Init() {
|
||||
m_simDevice = hal::SimDevice{"DutyCycleEncoder", m_dutyCycle->GetFPGAIndex()};
|
||||
m_simDevice = hal::SimDevice{"DutyCycle:DutyCycleEncoder",
|
||||
m_dutyCycle->GetSourceChannel()};
|
||||
|
||||
if (m_simDevice) {
|
||||
m_simPosition = m_simDevice.CreateDouble("Position", false, 0.0);
|
||||
m_simDistancePerRotation =
|
||||
m_simDevice.CreateDouble("DistancePerRotation", false, 1.0);
|
||||
m_simIsConnected = m_simDevice.CreateBoolean("Connected", false, true);
|
||||
m_simPosition =
|
||||
m_simDevice.CreateDouble("position", hal::SimDevice::kInput, 0.0);
|
||||
m_simIsConnected =
|
||||
m_simDevice.CreateBoolean("connected", hal::SimDevice::kInput, true);
|
||||
} else {
|
||||
m_analogTrigger = std::make_unique<AnalogTrigger>(m_dutyCycle.get());
|
||||
m_analogTrigger->SetLimitsDutyCycle(0.25, 0.75);
|
||||
@@ -100,7 +101,6 @@ units::turn_t DutyCycleEncoder::Get() const {
|
||||
|
||||
void DutyCycleEncoder::SetDistancePerRotation(double distancePerRotation) {
|
||||
m_distancePerRotation = distancePerRotation;
|
||||
m_simDistancePerRotation.Set(distancePerRotation);
|
||||
}
|
||||
|
||||
double DutyCycleEncoder::GetDistancePerRotation() const {
|
||||
|
||||
@@ -183,7 +183,6 @@ class DutyCycleEncoder : public ErrorBase,
|
||||
|
||||
hal::SimDevice m_simDevice;
|
||||
hal::SimDouble m_simPosition;
|
||||
hal::SimDouble m_simDistancePerRotation;
|
||||
hal::SimBoolean m_simIsConnected;
|
||||
};
|
||||
} // namespace frc
|
||||
|
||||
@@ -94,12 +94,13 @@ public class ADXL345_I2C implements Accelerometer, Sendable, AutoCloseable {
|
||||
m_i2c = new I2C(port, deviceAddress);
|
||||
|
||||
// simulation
|
||||
m_simDevice = SimDevice.create("ADXL345_I2C", port.value, deviceAddress);
|
||||
m_simDevice = SimDevice.create("Accel:ADXL345_I2C", port.value, deviceAddress);
|
||||
if (m_simDevice != null) {
|
||||
m_simRange = m_simDevice.createEnum("Range", true, new String[] {"2G", "4G", "8G", "16G"}, 0);
|
||||
m_simX = m_simDevice.createDouble("X Accel", false, 0.0);
|
||||
m_simY = m_simDevice.createDouble("Y Accel", false, 0.0);
|
||||
m_simZ = m_simDevice.createDouble("Z Accel", false, 0.0);
|
||||
m_simRange = m_simDevice.createEnumDouble("range", SimDevice.Direction.kOutput,
|
||||
new String[] {"2G", "4G", "8G", "16G"}, new double[] {2.0, 4.0, 8.0, 16.0}, 0);
|
||||
m_simX = m_simDevice.createDouble("x", SimDevice.Direction.kInput, 0.0);
|
||||
m_simY = m_simDevice.createDouble("y", SimDevice.Direction.kInput, 0.0);
|
||||
m_simZ = m_simDevice.createDouble("z", SimDevice.Direction.kInput, 0.0);
|
||||
}
|
||||
|
||||
// Turn on the measurements
|
||||
|
||||
@@ -85,12 +85,13 @@ public class ADXL345_SPI implements Accelerometer, Sendable, AutoCloseable {
|
||||
public ADXL345_SPI(SPI.Port port, Range range) {
|
||||
m_spi = new SPI(port);
|
||||
// simulation
|
||||
m_simDevice = SimDevice.create("ADXL345_SPI", port.value);
|
||||
m_simDevice = SimDevice.create("Accel:ADXL345_SPI", port.value);
|
||||
if (m_simDevice != null) {
|
||||
m_simRange = m_simDevice.createEnum("Range", true, new String[] {"2G", "4G", "8G", "16G"}, 0);
|
||||
m_simX = m_simDevice.createDouble("X Accel", false, 0.0);
|
||||
m_simY = m_simDevice.createDouble("Y Accel", false, 0.0);
|
||||
m_simZ = m_simDevice.createDouble("Z Accel", false, 0.0);
|
||||
m_simRange = m_simDevice.createEnumDouble("range", SimDevice.Direction.kOutput,
|
||||
new String[] {"2G", "4G", "8G", "16G"}, new double[] {2.0, 4.0, 8.0, 16.0}, 0);
|
||||
m_simX = m_simDevice.createDouble("x", SimDevice.Direction.kInput, 0.0);
|
||||
m_simY = m_simDevice.createDouble("y", SimDevice.Direction.kInput, 0.0);
|
||||
m_simZ = m_simDevice.createDouble("z", SimDevice.Direction.kInput, 0.0);
|
||||
}
|
||||
init(range);
|
||||
SendableRegistry.addLW(this, "ADXL345_SPI", port.value);
|
||||
|
||||
@@ -93,12 +93,13 @@ public class ADXL362 implements Accelerometer, Sendable, AutoCloseable {
|
||||
m_spi = new SPI(port);
|
||||
|
||||
// simulation
|
||||
m_simDevice = SimDevice.create("ADXL362", port.value);
|
||||
m_simDevice = SimDevice.create("Accel:ADXL362", port.value);
|
||||
if (m_simDevice != null) {
|
||||
m_simRange = m_simDevice.createEnum("Range", true, new String[] {"2G", "4G", "8G", "16G"}, 0);
|
||||
m_simX = m_simDevice.createDouble("X Accel", false, 0.0);
|
||||
m_simY = m_simDevice.createDouble("Y Accel", false, 0.0);
|
||||
m_simZ = m_simDevice.createDouble("Z Accel", false, 0.0);
|
||||
m_simRange = m_simDevice.createEnumDouble("range", SimDevice.Direction.kOutput,
|
||||
new String[] {"2G", "4G", "8G", "16G"}, new double[] {2.0, 4.0, 8.0, 16.0}, 0);
|
||||
m_simX = m_simDevice.createDouble("x", SimDevice.Direction.kInput, 0.0);
|
||||
m_simY = m_simDevice.createDouble("y", SimDevice.Direction.kInput, 0.0);
|
||||
m_simZ = m_simDevice.createDouble("z", SimDevice.Direction.kInput, 0.0);
|
||||
}
|
||||
|
||||
m_spi.setClockRate(3000000);
|
||||
|
||||
@@ -69,11 +69,11 @@ public class ADXRS450_Gyro extends GyroBase implements Gyro, PIDSource, Sendable
|
||||
m_port = port;
|
||||
|
||||
// simulation
|
||||
m_simDevice = SimDevice.create("ADXRS450_Gyro", port.value);
|
||||
m_simDevice = SimDevice.create("Gyro:ADXRS450", port.value);
|
||||
if (m_simDevice != null) {
|
||||
m_simConnected = m_simDevice.createBoolean("Connected", false, true);
|
||||
m_simAngle = m_simDevice.createDouble("Angle", false, 0.0);
|
||||
m_simRate = m_simDevice.createDouble("Rate", false, 0.0);
|
||||
m_simConnected = m_simDevice.createBoolean("connected", SimDevice.Direction.kInput, true);
|
||||
m_simAngle = m_simDevice.createDouble("angle", SimDevice.Direction.kInput, 0.0);
|
||||
m_simRate = m_simDevice.createDouble("rate", SimDevice.Direction.kInput, 0.0);
|
||||
}
|
||||
|
||||
m_spi.setClockRate(3000000);
|
||||
|
||||
@@ -32,7 +32,6 @@ public class DutyCycleEncoder implements Sendable, AutoCloseable {
|
||||
|
||||
protected SimDevice m_simDevice;
|
||||
protected SimDouble m_simPosition;
|
||||
protected SimDouble m_simDistancePerRotation;
|
||||
protected SimBoolean m_simIsConnected;
|
||||
|
||||
/**
|
||||
@@ -69,12 +68,11 @@ public class DutyCycleEncoder implements Sendable, AutoCloseable {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
m_simDevice = SimDevice.create("DutyCycleEncoder", m_dutyCycle.getFPGAIndex());
|
||||
m_simDevice = SimDevice.create("DutyCycle:DutyCycleEncoder", m_dutyCycle.getSourceChannel());
|
||||
|
||||
if (m_simDevice != null) {
|
||||
m_simPosition = m_simDevice.createDouble("Position", false, 0.0);
|
||||
m_simDistancePerRotation = m_simDevice.createDouble("DistancePerRotation", false, 1.0);
|
||||
m_simIsConnected = m_simDevice.createBoolean("Connected", false, true);
|
||||
m_simPosition = m_simDevice.createDouble("position", SimDevice.Direction.kInput, 0.0);
|
||||
m_simIsConnected = m_simDevice.createBoolean("connected", SimDevice.Direction.kInput, true);
|
||||
} else {
|
||||
m_counter = new Counter();
|
||||
m_analogTrigger = new AnalogTrigger(m_dutyCycle);
|
||||
@@ -142,10 +140,6 @@ public class DutyCycleEncoder implements Sendable, AutoCloseable {
|
||||
*/
|
||||
public void setDistancePerRotation(double distancePerRotation) {
|
||||
m_distancePerRotation = distancePerRotation;
|
||||
|
||||
if (m_simDistancePerRotation != null) {
|
||||
m_simDistancePerRotation.set(distancePerRotation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user