[sim] Add WebSocket extension (client/server) (#2589)

This allows access to HAL-level simulation data via a WebSocket connection.

The server additionally serves local files.

The following environment variables can be used for configuration:
HALSIMWS_USERROOT (server) - local directory to use for file serving for /user/ URIs, defaults to ./sim/user
HALSIMWS_SYSROOT (server) - local directory to use for file serving for all other URIs, defaults to ./sim
HALSIMWS_URI (client or server) - WebSocket URI, defaults to /wpilibws
HALSIMWS_PORT (client or server) - port number, defaults to 8080
HALSIMWS_HOST (client) - host to connect to, defaults to localhost

Co-authored-by: Zhiquan Yeo <zyeo8@bloomberg.net>
Co-authored-by: Peter Johnson <johnson.peter@gmail.com>
Co-authored-by: jpokornyiii <jpokornyiii@gmail.com>
This commit is contained in:
Zhiquan Yeo
2020-08-20 01:14:03 -04:00
committed by GitHub
parent e127bac7fd
commit 932bfcf374
51 changed files with 3696 additions and 1 deletions

View File

@@ -0,0 +1,20 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 "WSBaseProvider.h"
namespace wpilibws {
HALSimWSBaseProvider::HALSimWSBaseProvider(const std::string& key,
const std::string& type)
: m_key(key), m_type(type) {}
void HALSimWSBaseProvider::OnNetValueChanged(const wpi::json& json) {
// empty
}
} // namespace wpilibws

View File

@@ -0,0 +1,40 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 "WSHalProviders.h"
namespace wpilibws {
void HALSimWSHalProvider::OnNetworkConnected(
std::shared_ptr<HALSimBaseWebSocketConnection> ws) {
{
// store a weak reference to the websocket object
m_ws = ws;
}
RegisterCallbacks();
}
void HALSimWSHalProvider::OnNetworkDisconnected() { CancelCallbacks(); }
void HALSimWSHalProvider::ProcessHalCallback(const wpi::json& payload) {
auto ws = m_ws.lock();
if (ws) {
wpi::json netValue = {
{"type", m_type}, {"device", m_deviceId}, {"data", payload}};
ws->OnSimValueChanged(netValue);
}
}
HALSimWSHalChanProvider::HALSimWSHalChanProvider(int32_t channel,
const std::string& key,
const std::string& type)
: HALSimWSHalProvider(key, type), m_channel(channel) {
m_deviceId = std::to_string(channel);
}
} // namespace wpilibws

View File

@@ -0,0 +1,128 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_Analog.h"
#include <hal/Ports.h>
#include <hal/simulation/AnalogInData.h>
#include <hal/simulation/AnalogOutData.h>
#define REGISTER_AIN(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterAnalogIn##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderAnalogIn*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
#define REGISTER_AIN_ACCUM(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterAnalogInAccumulator##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderAnalogIn*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
#define REGISTER_AOUT(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterAnalogOut##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderAnalogOut*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderAnalogIn::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderAnalogIn>("AI", HAL_GetNumAnalogInputs(),
webRegisterFunc);
}
HALSimWSProviderAnalogIn::~HALSimWSProviderAnalogIn() { CancelCallbacks(); }
void HALSimWSProviderAnalogIn::RegisterCallbacks() {
m_initCbKey = REGISTER_AIN(Initialized, "<init", bool, boolean);
m_avgbitsCbKey = REGISTER_AIN(AverageBits, "<avg_bits", int32_t, int);
m_oversampleCbKey =
REGISTER_AIN(OversampleBits, "<oversample_bits", int32_t, int);
m_voltageCbKey = REGISTER_AIN(Voltage, ">voltage", double, double);
m_accumInitCbKey =
REGISTER_AIN_ACCUM(Initialized, "<accum_init", bool, boolean);
m_accumValueCbKey = REGISTER_AIN_ACCUM(Value, ">accum_value", int64_t,
long); // NOLINT(runtime/int)
m_accumCountCbKey = REGISTER_AIN_ACCUM(Count, ">accum_count", int64_t,
long); // NOLINT(runtime/int)
m_accumCenterCbKey =
REGISTER_AIN_ACCUM(Center, "<accum_center", int32_t, int);
m_accumDeadbandCbKey =
REGISTER_AIN_ACCUM(Deadband, "<accum_deadband", int32_t, int);
}
void HALSimWSProviderAnalogIn::CancelCallbacks() {
// Cancel callbacks
HALSIM_CancelAnalogInInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelAnalogInAverageBitsCallback(m_channel, m_avgbitsCbKey);
HALSIM_CancelAnalogInOversampleBitsCallback(m_channel, m_oversampleCbKey);
HALSIM_CancelAnalogInVoltageCallback(m_channel, m_voltageCbKey);
HALSIM_CancelAnalogInAccumulatorInitializedCallback(m_channel,
m_accumInitCbKey);
HALSIM_CancelAnalogInAccumulatorValueCallback(m_channel, m_accumValueCbKey);
HALSIM_CancelAnalogInAccumulatorCountCallback(m_channel, m_accumCountCbKey);
HALSIM_CancelAnalogInAccumulatorCenterCallback(m_channel, m_accumCenterCbKey);
HALSIM_CancelAnalogInAccumulatorDeadbandCallback(m_channel,
m_accumDeadbandCbKey);
// Reset callback IDs
m_initCbKey = 0;
m_avgbitsCbKey = 0;
m_oversampleCbKey = 0;
m_voltageCbKey = 0;
m_accumInitCbKey = 0;
m_accumValueCbKey = 0;
m_accumCountCbKey = 0;
m_accumCenterCbKey = 0;
m_accumDeadbandCbKey = 0;
}
void HALSimWSProviderAnalogIn::OnNetValueChanged(const wpi::json& json) {
wpi::json::const_iterator it;
if ((it = json.find(">voltage")) != json.end()) {
HALSIM_SetAnalogInVoltage(m_channel, it.value());
}
if ((it = json.find(">accum_value")) != json.end()) {
HALSIM_SetAnalogInAccumulatorValue(m_channel, it.value());
}
if ((it = json.find(">accum_count")) != json.end()) {
HALSIM_SetAnalogInAccumulatorCount(m_channel, it.value());
}
}
void HALSimWSProviderAnalogOut::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderAnalogOut>("AO", HAL_GetNumAnalogOutputs(),
webRegisterFunc);
}
HALSimWSProviderAnalogOut::~HALSimWSProviderAnalogOut() { CancelCallbacks(); }
void HALSimWSProviderAnalogOut::RegisterCallbacks() {
m_initCbKey = REGISTER_AOUT(Initialized, "<init", bool, boolean);
m_voltageCbKey = REGISTER_AOUT(Voltage, "<voltage", double, double);
}
void HALSimWSProviderAnalogOut::CancelCallbacks() {
HALSIM_CancelAnalogOutInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelAnalogOutVoltageCallback(m_channel, m_voltageCbKey);
m_initCbKey = 0;
m_voltageCbKey = 0;
}
} // namespace wpilibws

View File

@@ -0,0 +1,57 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_DIO.h"
#include <hal/Ports.h>
#include <hal/simulation/DIOData.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterDIO##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderDIO*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderDIO::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderDIO>("DIO", HAL_GetNumDigitalChannels(),
webRegisterFunc);
}
HALSimWSProviderDIO::~HALSimWSProviderDIO() { CancelCallbacks(); }
void HALSimWSProviderDIO::RegisterCallbacks() {
m_initCbKey = REGISTER(Initialized, "<init", bool, boolean);
m_valueCbKey = REGISTER(Value, "<>value", bool, boolean);
m_pulseLengthCbKey = REGISTER(PulseLength, "<pulse_length", double, double);
m_inputCbKey = REGISTER(IsInput, "<input", bool, boolean);
}
void HALSimWSProviderDIO::CancelCallbacks() {
HALSIM_CancelDIOInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelDIOValueCallback(m_channel, m_valueCbKey);
HALSIM_CancelDIOPulseLengthCallback(m_channel, m_pulseLengthCbKey);
HALSIM_CancelDIOIsInputCallback(m_channel, m_inputCbKey);
m_initCbKey = 0;
m_valueCbKey = 0;
m_pulseLengthCbKey = 0;
m_inputCbKey = 0;
}
void HALSimWSProviderDIO::OnNetValueChanged(const wpi::json& json) {
wpi::json::const_iterator it;
if ((it = json.find("<>value")) != json.end()) {
HALSIM_SetDIOValue(m_channel, static_cast<bool>(it.value()));
}
}
} // namespace wpilibws

View File

@@ -0,0 +1,150 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_DriverStation.h"
#include <algorithm>
#include <hal/DriverStation.h>
#include <hal/Ports.h>
#include <hal/simulation/DriverStationData.h>
#include <wpi/raw_ostream.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterDriverStation##halsim##Callback( \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderDriverStation*>(param) \
->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderDriverStation::Initialize(WSRegisterFunc webRegisterFunc) {
CreateSingleProvider<HALSimWSProviderDriverStation>("DriverStation",
webRegisterFunc);
}
HALSimWSProviderDriverStation::~HALSimWSProviderDriverStation() {
CancelCallbacks();
}
void HALSimWSProviderDriverStation::RegisterCallbacks() {
m_enabledCbKey = REGISTER(Enabled, ">enabled", bool, boolean);
m_autonomousCbKey = REGISTER(Autonomous, ">autonomous", bool, boolean);
m_testCbKey = REGISTER(Test, ">test", bool, boolean);
m_estopCbKey = REGISTER(EStop, ">estop", bool, boolean);
m_fmsCbKey = REGISTER(FmsAttached, ">fms", bool, boolean);
m_dsCbKey = REGISTER(DsAttached, ">ds", bool, boolean);
// Special case for new data, since the HAL_Value is empty
m_newDataCbKey = HALSIM_RegisterDriverStationNewDataCallback(
[](const char* name, void* param, const struct HAL_Value* value) {
static_cast<HALSimWSProviderDriverStation*>(param)->ProcessHalCallback(
{{">new_data", true}});
},
this, true);
m_allianceCbKey = HALSIM_RegisterDriverStationAllianceStationIdCallback(
[](const char* name, void* param, const struct HAL_Value* value) {
std::string station;
switch (static_cast<HAL_AllianceStationID>(value->data.v_enum)) {
case HAL_AllianceStationID_kRed1:
station = "red1";
break;
case HAL_AllianceStationID_kBlue1:
station = "blue1";
break;
case HAL_AllianceStationID_kRed2:
station = "red2";
break;
case HAL_AllianceStationID_kBlue2:
station = "blue2";
break;
case HAL_AllianceStationID_kRed3:
station = "red3";
break;
case HAL_AllianceStationID_kBlue3:
station = "blue3";
break;
}
static_cast<HALSimWSProviderDriverStation*>(param)->ProcessHalCallback(
{{">station", station}});
},
this, true);
m_matchTimeCbKey = REGISTER(MatchTime, "<match_time", double, double);
}
void HALSimWSProviderDriverStation::CancelCallbacks() {
HALSIM_CancelDriverStationEnabledCallback(m_enabledCbKey);
HALSIM_CancelDriverStationAutonomousCallback(m_autonomousCbKey);
HALSIM_CancelDriverStationTestCallback(m_testCbKey);
HALSIM_CancelDriverStationEStopCallback(m_estopCbKey);
HALSIM_CancelDriverStationFmsAttachedCallback(m_fmsCbKey);
HALSIM_CancelDriverStationDsAttachedCallback(m_dsCbKey);
HALSIM_CancelDriverStationNewDataCallback(m_newDataCbKey);
HALSIM_CancelDriverStationAllianceStationIdCallback(m_allianceCbKey);
HALSIM_CancelDriverStationMatchTimeCallback(m_matchTimeCbKey);
m_enabledCbKey = 0;
m_autonomousCbKey = 0;
m_testCbKey = 0;
m_estopCbKey = 0;
m_fmsCbKey = 0;
m_dsCbKey = 0;
m_newDataCbKey = 0;
m_allianceCbKey = 0;
m_matchTimeCbKey = 0;
}
void HALSimWSProviderDriverStation::OnNetValueChanged(const wpi::json& json) {
wpi::json::const_iterator it;
if ((it = json.find(">enabled")) != json.end()) {
HALSIM_SetDriverStationEnabled(it.value());
}
if ((it = json.find(">autonomous")) != json.end()) {
HALSIM_SetDriverStationAutonomous(it.value());
}
if ((it = json.find(">test")) != json.end()) {
HALSIM_SetDriverStationTest(it.value());
}
if ((it = json.find(">estop")) != json.end()) {
HALSIM_SetDriverStationEStop(it.value());
}
if ((it = json.find(">fms")) != json.end()) {
HALSIM_SetDriverStationFmsAttached(it.value());
}
if ((it = json.find(">ds")) != json.end()) {
HALSIM_SetDriverStationDsAttached(it.value());
}
if ((it = json.find(">station")) != json.end()) {
auto& station = it.value().get_ref<const std::string&>();
if (station == "red1") {
HALSIM_SetDriverStationAllianceStationId(HAL_AllianceStationID_kRed1);
} else if (station == "red2") {
HALSIM_SetDriverStationAllianceStationId(HAL_AllianceStationID_kRed2);
} else if (station == "red3") {
HALSIM_SetDriverStationAllianceStationId(HAL_AllianceStationID_kRed3);
} else if (station == "blue1") {
HALSIM_SetDriverStationAllianceStationId(HAL_AllianceStationID_kBlue1);
} else if (station == "blue2") {
HALSIM_SetDriverStationAllianceStationId(HAL_AllianceStationID_kBlue2);
} else if (station == "blue3") {
HALSIM_SetDriverStationAllianceStationId(HAL_AllianceStationID_kBlue3);
}
}
// Only notify usercode if we get the new data message
if ((it = json.find(">new_data")) != json.end()) {
HALSIM_NotifyDriverStationNewData();
}
}
} // namespace wpilibws

View File

@@ -0,0 +1,87 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_Encoder.h"
#include <hal/Ports.h>
#include <hal/simulation/EncoderData.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterEncoder##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderEncoder*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderEncoder::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderEncoder>("Encoder", HAL_GetNumEncoders(),
webRegisterFunc);
}
HALSimWSProviderEncoder::~HALSimWSProviderEncoder() { CancelCallbacks(); }
void HALSimWSProviderEncoder::RegisterCallbacks() {
// Special case for initialization since we will need to send
// the digital channels down the line as well
m_initCbKey = HALSIM_RegisterEncoderInitializedCallback(
m_channel,
[](const char* name, void* param, const struct HAL_Value* value) {
auto provider = static_cast<HALSimWSProviderEncoder*>(param);
bool init = static_cast<bool>(value->data.v_boolean);
wpi::json payload = {{"<init", init}};
if (init) {
payload["<channel_a"] =
HALSIM_GetEncoderDigitalChannelA(provider->GetChannel());
payload["<channel_b"] =
HALSIM_GetEncoderDigitalChannelB(provider->GetChannel());
}
provider->ProcessHalCallback(payload);
},
this, true);
m_countCbKey = REGISTER(Count, ">count", int32_t, int);
m_periodCbKey = REGISTER(Period, ">period", double, double);
m_resetCbKey = REGISTER(Reset, "<reset", bool, boolean);
m_reverseDirectionCbKey =
REGISTER(ReverseDirection, "<reverse_direction", bool, boolean);
m_samplesCbKey = REGISTER(SamplesToAverage, "<samples_to_avg", int32_t, int);
}
void HALSimWSProviderEncoder::CancelCallbacks() {
HALSIM_CancelEncoderInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelEncoderCountCallback(m_channel, m_countCbKey);
HALSIM_CancelEncoderPeriodCallback(m_channel, m_periodCbKey);
HALSIM_CancelEncoderResetCallback(m_channel, m_resetCbKey);
HALSIM_CancelEncoderReverseDirectionCallback(m_channel,
m_reverseDirectionCbKey);
HALSIM_CancelEncoderSamplesToAverageCallback(m_channel, m_samplesCbKey);
m_initCbKey = 0;
m_countCbKey = 0;
m_periodCbKey = 0;
m_resetCbKey = 0;
m_reverseDirectionCbKey = 0;
m_samplesCbKey = 0;
}
void HALSimWSProviderEncoder::OnNetValueChanged(const wpi::json& json) {
wpi::json::const_iterator it;
if ((it = json.find(">count")) != json.end()) {
HALSIM_SetEncoderCount(m_channel, it.value());
}
if ((it = json.find(">period")) != json.end()) {
HALSIM_SetEncoderPeriod(m_channel, it.value());
}
}
} // namespace wpilibws

View File

@@ -0,0 +1,110 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_Joystick.h"
#include <hal/Ports.h>
#include <hal/simulation/DriverStationData.h>
namespace wpilibws {
void HALSimWSProviderJoystick::Initialize(WSRegisterFunc webregisterFunc) {
CreateProviders<HALSimWSProviderJoystick>("Joystick", HAL_kMaxJoysticks,
webregisterFunc);
}
HALSimWSProviderJoystick::~HALSimWSProviderJoystick() { CancelCallbacks(); }
void HALSimWSProviderJoystick::RegisterCallbacks() {
m_dsNewDataCbKey = HALSIM_RegisterDriverStationNewDataCallback(
[](const char* name, void* param, const struct HAL_Value* value) {
auto provider = static_cast<HALSimWSProviderJoystick*>(param);
// Grab all joystick data and send it at once
wpi::json payload;
// Axes data
HAL_JoystickAxes axes{};
std::vector<double> axesValues;
HALSIM_GetJoystickAxes(provider->GetChannel(), &axes);
for (int i = 0; i < axes.count; i++) {
axesValues.push_back(axes.axes[i]);
}
// POVs data
HAL_JoystickPOVs povs{};
std::vector<int16_t> povsValues;
HALSIM_GetJoystickPOVs(provider->GetChannel(), &povs);
for (int i = 0; i < povs.count; i++) {
povsValues.push_back(povs.povs[i]);
}
// Button data
HAL_JoystickButtons buttons{};
std::vector<bool> buttonsValues;
for (int i = 0; i < buttons.count; i++) {
buttonsValues.push_back(((buttons.buttons >> i) & 0x1) == 1);
}
payload[">axes"] = axesValues;
payload[">povs"] = povsValues;
payload[">buttons"] = buttonsValues;
provider->ProcessHalCallback(payload);
},
this, true);
}
void HALSimWSProviderJoystick::CancelCallbacks() {
HALSIM_CancelDriverStationNewDataCallback(m_dsNewDataCbKey);
m_dsNewDataCbKey = 0;
}
void HALSimWSProviderJoystick::OnNetValueChanged(const wpi::json& json) {
wpi::json::const_iterator it;
if ((it = json.find(">axes")) != json.end()) {
HAL_JoystickAxes axes{};
axes.count =
std::min(it.value().size(), (wpi::json::size_type)HAL_kMaxJoystickAxes);
for (int i = 0; i < axes.count; i++) {
axes.axes[i] = it.value()[i];
}
HALSIM_SetJoystickAxes(m_channel, &axes);
}
if ((it = json.find(">buttons")) != json.end()) {
HAL_JoystickButtons buttons{};
buttons.count = std::min(it.value().size(), (wpi::json::size_type)32);
for (int i = 0; i < buttons.count; i++) {
if (it.value()[i]) {
buttons.buttons |= 1 << (i - 1);
}
}
HALSIM_SetJoystickButtons(m_channel, &buttons);
}
if ((it = json.find(">povs")) != json.end()) {
HAL_JoystickPOVs povs{};
povs.count =
std::min(it.value().size(), (wpi::json::size_type)HAL_kMaxJoystickPOVs);
for (int i = 0; i < povs.count; i++) {
povs.povs[i] = it.value()[i];
}
HALSIM_SetJoystickPOVs(m_channel, &povs);
}
// We won't send any updates to user code until the new data message
// is received by the driver station provider
}
} // namespace wpilibws

View File

@@ -0,0 +1,55 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_PWM.h"
#include <hal/Ports.h>
#include <hal/simulation/PWMData.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterPWM##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderPWM*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderPWM::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderPWM>("PWM", HAL_GetNumPWMChannels(),
webRegisterFunc);
}
HALSimWSProviderPWM::~HALSimWSProviderPWM() { CancelCallbacks(); }
void HALSimWSProviderPWM::RegisterCallbacks() {
m_initCbKey = REGISTER(Initialized, "<init", bool, boolean);
m_speedCbKey = REGISTER(Speed, "<speed", double, double);
m_positionCbKey = REGISTER(Position, "<position", double, double);
m_rawCbKey = REGISTER(RawValue, "<raw", int32_t, int);
m_periodScaleCbKey = REGISTER(PeriodScale, "<period_scale", int32_t, int);
m_zeroLatchCbKey = REGISTER(ZeroLatch, "<zero_latch", bool, boolean);
}
void HALSimWSProviderPWM::CancelCallbacks() {
HALSIM_CancelPWMInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelPWMSpeedCallback(m_channel, m_speedCbKey);
HALSIM_CancelPWMPositionCallback(m_channel, m_positionCbKey);
HALSIM_CancelPWMRawValueCallback(m_channel, m_rawCbKey);
HALSIM_CancelPWMPeriodScaleCallback(m_channel, m_periodScaleCbKey);
HALSIM_CancelPWMZeroLatchCallback(m_channel, m_zeroLatchCbKey);
m_initCbKey = 0;
m_speedCbKey = 0;
m_positionCbKey = 0;
m_rawCbKey = 0;
m_periodScaleCbKey = 0;
m_zeroLatchCbKey = 0;
}
} // namespace wpilibws

View File

@@ -0,0 +1,49 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_Relay.h"
#include <hal/Ports.h>
#include <hal/simulation/RelayData.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterRelay##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderRelay*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderRelay::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderRelay>("Relay", HAL_GetNumRelayHeaders(),
webRegisterFunc);
}
HALSimWSProviderRelay::~HALSimWSProviderRelay() { CancelCallbacks(); }
void HALSimWSProviderRelay::RegisterCallbacks() {
m_initFwdCbKey = REGISTER(InitializedForward, "<init_fwd", bool, boolean);
m_initRevCbKey = REGISTER(InitializedReverse, "<init_rev", bool, boolean);
m_fwdCbKey = REGISTER(Forward, "<fwd", bool, boolean);
m_revCbKey = REGISTER(Reverse, "<rev", bool, boolean);
}
void HALSimWSProviderRelay::CancelCallbacks() {
HALSIM_CancelRelayInitializedForwardCallback(m_channel, m_initFwdCbKey);
HALSIM_CancelRelayInitializedReverseCallback(m_channel, m_initRevCbKey);
HALSIM_CancelRelayForwardCallback(m_channel, m_fwdCbKey);
HALSIM_CancelRelayReverseCallback(m_channel, m_revCbKey);
m_initFwdCbKey = 0;
m_initRevCbKey = 0;
m_fwdCbKey = 0;
m_revCbKey = 0;
}
} // namespace wpilibws

View File

@@ -0,0 +1,140 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_RoboRIO.h"
#include <hal/Ports.h>
#include <hal/simulation/RoboRioData.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterRoboRio##halsim##Callback( \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderRoboRIO*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderRoboRIO::Initialize(WSRegisterFunc webRegisterFunc) {
CreateSingleProvider<HALSimWSProviderRoboRIO>("RoboRIO", webRegisterFunc);
}
HALSimWSProviderRoboRIO::~HALSimWSProviderRoboRIO() { CancelCallbacks(); }
void HALSimWSProviderRoboRIO::RegisterCallbacks() {
m_fpgaCbKey = REGISTER(FPGAButton, ">fpga_button", bool, boolean);
m_vinVoltageCbKey = REGISTER(VInVoltage, ">vin_voltage", double, double);
m_vinCurrentCbKey = REGISTER(VInCurrent, ">vin_current", double, double);
m_6vVoltageCbKey = REGISTER(UserVoltage6V, ">6v_voltage", double, double);
m_6vCurrentCbKey = REGISTER(UserCurrent6V, ">6v_current", double, double);
m_6vActiveCbKey = REGISTER(UserActive6V, ">6v_active", bool, boolean);
m_6vFaultsCbKey = REGISTER(UserFaults6V, ">6v_faults", int32_t, int);
m_5vVoltageCbKey = REGISTER(UserVoltage5V, ">5v_voltage", double, double);
m_5vCurrentCbKey = REGISTER(UserCurrent5V, ">5v_current", double, double);
m_5vActiveCbKey = REGISTER(UserActive5V, ">5v_active", bool, boolean);
m_5vFaultsCbKey = REGISTER(UserFaults5V, ">5v_faults", int32_t, int);
m_3v3VoltageCbKey = REGISTER(UserVoltage3V3, ">3v3_voltage", double, double);
m_3v3CurrentCbKey = REGISTER(UserCurrent3V3, ">3v3_current", double, double);
m_3v3ActiveCbKey = REGISTER(UserActive3V3, ">3v3_active", bool, boolean);
m_3v3FaultsCbKey = REGISTER(UserFaults3V3, ">3v3_faults", int32_t, int);
}
void HALSimWSProviderRoboRIO::CancelCallbacks() {
HALSIM_CancelRoboRioFPGAButtonCallback(m_fpgaCbKey);
HALSIM_CancelRoboRioVInVoltageCallback(m_vinVoltageCbKey);
HALSIM_CancelRoboRioVInCurrentCallback(m_vinCurrentCbKey);
HALSIM_CancelRoboRioUserVoltage6VCallback(m_6vVoltageCbKey);
HALSIM_CancelRoboRioUserCurrent6VCallback(m_6vCurrentCbKey);
HALSIM_CancelRoboRioUserActive6VCallback(m_6vActiveCbKey);
HALSIM_CancelRoboRioUserFaults6VCallback(m_6vFaultsCbKey);
HALSIM_CancelRoboRioUserVoltage5VCallback(m_5vVoltageCbKey);
HALSIM_CancelRoboRioUserCurrent5VCallback(m_5vCurrentCbKey);
HALSIM_CancelRoboRioUserActive5VCallback(m_5vActiveCbKey);
HALSIM_CancelRoboRioUserFaults5VCallback(m_5vFaultsCbKey);
HALSIM_CancelRoboRioUserVoltage3V3Callback(m_3v3VoltageCbKey);
HALSIM_CancelRoboRioUserCurrent3V3Callback(m_3v3CurrentCbKey);
HALSIM_CancelRoboRioUserActive3V3Callback(m_3v3ActiveCbKey);
HALSIM_CancelRoboRioUserFaults3V3Callback(m_3v3FaultsCbKey);
m_fpgaCbKey = 0;
m_vinVoltageCbKey = 0;
m_vinCurrentCbKey = 0;
m_6vActiveCbKey = 0;
m_6vCurrentCbKey = 0;
m_6vFaultsCbKey = 0;
m_6vVoltageCbKey = 0;
m_5vActiveCbKey = 0;
m_5vCurrentCbKey = 0;
m_5vFaultsCbKey = 0;
m_5vVoltageCbKey = 0;
m_3v3ActiveCbKey = 0;
m_3v3CurrentCbKey = 0;
m_3v3FaultsCbKey = 0;
m_3v3VoltageCbKey = 0;
}
void HALSimWSProviderRoboRIO::OnNetValueChanged(const wpi::json& json) {
wpi::json::const_iterator it;
if ((it = json.find(">fpga_button")) != json.end()) {
HALSIM_SetRoboRioFPGAButton(static_cast<bool>(it.value()));
}
if ((it = json.find(">vin_voltage")) != json.end()) {
HALSIM_SetRoboRioVInVoltage(it.value());
}
if ((it = json.find(">vin_current")) != json.end()) {
HALSIM_SetRoboRioVInCurrent(it.value());
}
if ((it = json.find(">6v_voltage")) != json.end()) {
HALSIM_SetRoboRioUserVoltage6V(it.value());
}
if ((it = json.find(">6v_current")) != json.end()) {
HALSIM_SetRoboRioUserCurrent6V(it.value());
}
if ((it = json.find(">6v_active")) != json.end()) {
HALSIM_SetRoboRioUserActive6V(static_cast<bool>(it.value()));
}
if ((it = json.find(">6v_faults")) != json.end()) {
HALSIM_SetRoboRioUserFaults6V(it.value());
}
if ((it = json.find(">5v_voltage")) != json.end()) {
HALSIM_SetRoboRioUserVoltage5V(it.value());
}
if ((it = json.find(">5v_current")) != json.end()) {
HALSIM_SetRoboRioUserCurrent5V(it.value());
}
if ((it = json.find(">5v_active")) != json.end()) {
HALSIM_SetRoboRioUserActive5V(static_cast<bool>(it.value()));
}
if ((it = json.find(">5v_faults")) != json.end()) {
HALSIM_SetRoboRioUserFaults5V(it.value());
}
if ((it = json.find(">3v3_voltage")) != json.end()) {
HALSIM_SetRoboRioUserVoltage3V3(it.value());
}
if ((it = json.find(">3v3_current")) != json.end()) {
HALSIM_SetRoboRioUserCurrent3V3(it.value());
}
if ((it = json.find(">3v3_active")) != json.end()) {
HALSIM_SetRoboRioUserActive3V3(static_cast<bool>(it.value()));
}
if ((it = json.find(">3v3_faults")) != json.end()) {
HALSIM_SetRoboRioUserFaults3V3(it.value());
}
}
} // namespace wpilibws

View File

@@ -0,0 +1,197 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_SimDevice.h"
#include <hal/Ports.h>
namespace wpilibws {
HALSimWSProviderSimDevice::~HALSimWSProviderSimDevice() { CancelCallbacks(); }
void HALSimWSProviderSimDevice::OnNetworkConnected(
std::shared_ptr<HALSimBaseWebSocketConnection> ws) {
auto storedWS = m_ws.lock();
if (ws == storedWS) {
return;
}
// Otherwise, run the disconnection code first so we get
// back to a clean slate (only if the stored websocket is
// not null)
if (storedWS) {
OnNetworkDisconnected();
}
m_ws = ws;
m_simValueCreatedCbKey = HALSIM_RegisterSimValueCreatedCallback(
m_handle, this, HALSimWSProviderSimDevice::OnValueCreatedStatic, 1);
}
void HALSimWSProviderSimDevice::OnNetworkDisconnected() {
// Cancel all callbacks
CancelCallbacks();
m_ws.reset();
}
void HALSimWSProviderSimDevice::CancelCallbacks() {
HALSIM_CancelSimValueCreatedCallback(m_simValueCreatedCbKey);
m_simValueCreatedCbKey = 0;
for (auto& kv : m_simValueChangedCbKeys) {
HALSIM_CancelSimValueChangedCallback(kv.getValue());
}
m_simValueChangedCbKeys.clear();
}
void HALSimWSProviderSimDevice::OnNetValueChanged(const wpi::json& json) {
auto it = json.cbegin();
auto end = json.cend();
std::shared_lock lock(m_vhLock);
for (; it != end; ++it) {
auto vd = m_valueHandles.find(it.key());
if (vd != m_valueHandles.end()) {
HAL_Value value;
value.type = vd->second->valueType;
switch (value.type) {
case HAL_BOOLEAN:
value.data.v_boolean = static_cast<bool>(it.value()) ? 1 : 0;
break;
case HAL_DOUBLE:
value.data.v_double = it.value();
break;
case HAL_ENUM:
value.data.v_enum = it.value();
break;
case HAL_INT:
value.data.v_int = it.value();
break;
case HAL_LONG:
value.data.v_long = it.value();
break;
default:
break;
}
HAL_SetSimValue(vd->second->handle, &value);
}
}
}
void HALSimWSProviderSimDevice::OnValueCreated(const char* name,
HAL_SimValueHandle handle,
HAL_Bool readonly,
const struct HAL_Value* value) {
wpi::Twine key = wpi::Twine(readonly ? "<" : "<>") + name;
auto data = std::make_unique<SimDeviceValueData>();
data->device = this;
data->handle = handle;
data->key = key.str();
data->valueType = value->type;
auto param = data.get();
{
std::unique_lock lock(m_vhLock);
m_valueHandles[data->key] = std::move(data);
}
int32_t cbKey = HALSIM_RegisterSimValueChangedCallback(
handle, param, HALSimWSProviderSimDevice::OnValueChangedStatic, true);
m_simValueChangedCbKeys[key.str()] = cbKey;
}
void HALSimWSProviderSimDevice::OnValueChanged(SimDeviceValueData* valueData,
const struct HAL_Value* value) {
auto ws = m_ws.lock();
if (ws) {
switch (value->type) {
case HAL_BOOLEAN:
ProcessHalCallback({{valueData->key, value->data.v_boolean}});
break;
case HAL_DOUBLE:
ProcessHalCallback({{valueData->key, value->data.v_double}});
break;
case HAL_ENUM:
ProcessHalCallback({{valueData->key, value->data.v_enum}});
break;
case HAL_INT:
ProcessHalCallback({{valueData->key, value->data.v_int}});
break;
case HAL_LONG:
ProcessHalCallback({{valueData->key, value->data.v_long}});
break;
default:
break;
}
}
}
void HALSimWSProviderSimDevice::ProcessHalCallback(const wpi::json& payload) {
auto ws = m_ws.lock();
if (ws) {
wpi::json netValue = {
{"type", "SimDevices"}, {"device", m_deviceId}, {"data", payload}};
ws->OnSimValueChanged(netValue);
}
}
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);
if (m_ws) {
m_exec->Call([this, dev]() { dev->OnNetworkConnected(GetWSConnection()); });
}
}
void HALSimWSProviderSimDevices::DeviceFreedCallback(
const char* name, HAL_SimDeviceHandle handle) {
m_providers.Delete(name);
}
void HALSimWSProviderSimDevices::Initialize(
std::shared_ptr<wpi::uv::Loop> loop) {
m_deviceCreatedCbKey = HALSIM_RegisterSimDeviceCreatedCallback(
"", this, HALSimWSProviderSimDevices::DeviceCreatedCallbackStatic, 1);
m_deviceFreedCbKey = HALSIM_RegisterSimDeviceFreedCallback(
"", this, HALSimWSProviderSimDevices::DeviceFreedCallbackStatic);
m_exec = UvExecFn::Create(loop, [](auto out, LoopFn func) {
func();
out.set_value();
});
}
void HALSimWSProviderSimDevices::CancelCallbacks() {
HALSIM_CancelSimDeviceCreatedCallback(m_deviceCreatedCbKey);
HALSIM_CancelSimDeviceFreedCallback(m_deviceFreedCbKey);
m_deviceCreatedCbKey = 0;
m_deviceFreedCbKey = 0;
}
void HALSimWSProviderSimDevices::OnNetworkConnected(
std::shared_ptr<HALSimBaseWebSocketConnection> hws) {
m_ws = hws;
}
void HALSimWSProviderSimDevices::OnNetworkDisconnected() { m_ws = nullptr; }
} // namespace wpilibws

View File

@@ -0,0 +1,47 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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_dPWM.h"
#include <hal/Ports.h>
#include <hal/simulation/DigitalPWMData.h>
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterDigitalPWM##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderDigitalPWM*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderDigitalPWM::Initialize(WSRegisterFunc webRegisterFunc) {
CreateProviders<HALSimWSProviderDigitalPWM>(
"dPWM", HAL_GetNumDigitalPWMOutputs(), webRegisterFunc);
}
HALSimWSProviderDigitalPWM::~HALSimWSProviderDigitalPWM() { CancelCallbacks(); }
void HALSimWSProviderDigitalPWM::RegisterCallbacks() {
m_initCbKey = REGISTER(Initialized, "<init", bool, boolean);
m_dutyCycleCbKey = REGISTER(DutyCycle, "<duty_cycle", double, double);
m_pinCbKey = REGISTER(Pin, "<dio_pin", int32_t, int);
}
void HALSimWSProviderDigitalPWM::CancelCallbacks() {
HALSIM_CancelDigitalPWMInitializedCallback(m_channel, m_initCbKey);
HALSIM_CancelDigitalPWMDutyCycleCallback(m_channel, m_dutyCycleCbKey);
HALSIM_CancelDigitalPWMPinCallback(m_channel, m_pinCbKey);
m_initCbKey = 0;
m_dutyCycleCbKey = 0;
m_pinCbKey = 0;
}
} // namespace wpilibws

View File

@@ -0,0 +1,24 @@
/*----------------------------------------------------------------------------*/
/* 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 <memory>
#include <wpi/json.h>
namespace wpilibws {
class HALSimBaseWebSocketConnection {
public:
virtual void OnSimValueChanged(const wpi::json& msg) = 0;
protected:
virtual ~HALSimBaseWebSocketConnection() = default;
};
} // namespace wpilibws

View File

@@ -0,0 +1,53 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <functional>
#include <memory>
#include <string>
#include <wpi/json.h>
#include "HALSimBaseWebSocketConnection.h"
namespace wpilibws {
class HALSimWSBaseProvider {
public:
explicit HALSimWSBaseProvider(const std::string& key,
const std::string& type = "");
virtual ~HALSimWSBaseProvider() {}
HALSimWSBaseProvider(const HALSimWSBaseProvider&) = delete;
HALSimWSBaseProvider& operator=(const HALSimWSBaseProvider&) = delete;
// Called when the websocket connects. This will cause providers
// to register their HAL callbacks
virtual void OnNetworkConnected(
std::shared_ptr<HALSimBaseWebSocketConnection> ws) = 0;
// Called when the websocket disconnects. This will cause provider
// to cancel their HAL callbacks
virtual void OnNetworkDisconnected() = 0;
// network -> sim
virtual void OnNetValueChanged(const wpi::json& json);
const std::string GetDeviceType() { return m_type; }
const std::string GetDeviceId() { return m_deviceId; }
protected:
// sim -> network
std::weak_ptr<HALSimBaseWebSocketConnection> m_ws;
std::string m_key;
std::string m_type;
std::string m_deviceId = "";
};
} // namespace wpilibws

View File

@@ -0,0 +1,68 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <functional>
#include <memory>
#include <string>
#include <hal/simulation/NotifyListener.h>
#include <wpi/json.h>
#include <wpi/mutex.h>
#include "WSBaseProvider.h"
namespace wpilibws {
typedef void (*HALCbRegisterIndexedFunc)(int32_t index,
HAL_NotifyCallback callback,
void* param, HAL_Bool initialNotify);
typedef void (*HALCbRegisterSingleFunc)(HAL_NotifyCallback callback,
void* param, HAL_Bool initialNotify);
// provider generates diffs based on values
class HALSimWSHalProvider : public HALSimWSBaseProvider {
public:
using HALSimWSBaseProvider::HALSimWSBaseProvider;
void OnNetworkConnected(std::shared_ptr<HALSimBaseWebSocketConnection> ws);
void OnNetworkDisconnected();
void ProcessHalCallback(const wpi::json& payload);
protected:
virtual void RegisterCallbacks() = 0;
virtual void CancelCallbacks() = 0;
};
// provider generates per-channel diffs
class HALSimWSHalChanProvider : public HALSimWSHalProvider {
public:
explicit HALSimWSHalChanProvider(int32_t channel, const std::string& key,
const std::string& type);
int32_t GetChannel() { return m_channel; }
protected:
int32_t m_channel;
};
using WSRegisterFunc = std::function<void(
const std::string&, std::shared_ptr<HALSimWSBaseProvider>)>;
template <typename T>
void CreateProviders(const std::string& prefix, int32_t numChannels,
WSRegisterFunc webRegisterFunc);
template <typename T>
void CreateSingleProvider(const std::string& key,
WSRegisterFunc webRegisterFunc);
#include "WSHalProviders.inl"
} // namespace wpilibws

View File

@@ -0,0 +1,29 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include <string>
#include <utility>
template <typename T>
void CreateProviders(const std::string& prefix, int numChannels,
WSRegisterFunc webRegisterFunc) {
for (int32_t i = 0; i < numChannels; i++) {
auto key = (prefix + "/" + wpi::Twine(i)).str();
auto ptr = std::make_unique<T>(i, key, prefix);
webRegisterFunc(key, std::move(ptr));
}
}
template <typename T>
void CreateSingleProvider(const std::string& key,
WSRegisterFunc webRegisterFunc) {
auto ptr = std::make_unique<T>(key, key);
webRegisterFunc(key, std::move(ptr));
}

View File

@@ -0,0 +1,58 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <functional>
#include <memory>
#include <shared_mutex>
#include <wpi/StringMap.h>
#include "WSBaseProvider.h"
namespace wpilibws {
class ProviderContainer {
public:
using ProviderPtr = std::shared_ptr<HALSimWSBaseProvider>;
using IterFn = std::function<void(ProviderPtr)>;
ProviderContainer() {}
ProviderContainer(const ProviderContainer&) = delete;
ProviderContainer& operator=(const ProviderContainer&) = delete;
void Add(wpi::StringRef key, std::shared_ptr<HALSimWSBaseProvider> provider) {
std::unique_lock lock(m_mutex);
m_providers[key] = provider;
}
void Delete(wpi::StringRef key) {
std::unique_lock lock(m_mutex);
m_providers.erase(key);
}
void ForEach(IterFn fn) {
std::shared_lock lock(m_mutex);
for (auto& kv : m_providers) {
fn(kv.getValue());
}
}
ProviderPtr Get(wpi::StringRef key) {
std::shared_lock lock(m_mutex);
auto fiter = m_providers.find(key);
return fiter != m_providers.end() ? fiter->second : ProviderPtr();
}
private:
std::shared_mutex m_mutex;
wpi::StringMap<std::shared_ptr<HALSimWSBaseProvider>> m_providers;
};
} // namespace wpilibws

View File

@@ -0,0 +1,57 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderAnalogIn : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderAnalogIn();
void OnNetValueChanged(const wpi::json& json) override;
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initCbKey = 0;
int32_t m_avgbitsCbKey = 0;
int32_t m_oversampleCbKey = 0;
int32_t m_voltageCbKey = 0;
int32_t m_accumInitCbKey = 0;
int32_t m_accumValueCbKey = 0;
int32_t m_accumCountCbKey = 0;
int32_t m_accumCenterCbKey = 0;
int32_t m_accumDeadbandCbKey = 0;
};
class HALSimWSProviderAnalogOut : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderAnalogOut();
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initCbKey = 0;
int32_t m_voltageCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderDIO : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderDIO();
void OnNetValueChanged(const wpi::json& json) override;
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initCbKey = 0;
int32_t m_valueCbKey = 0;
int32_t m_pulseLengthCbKey = 0;
int32_t m_inputCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,41 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderDriverStation : public HALSimWSHalProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalProvider::HALSimWSHalProvider;
~HALSimWSProviderDriverStation();
void OnNetValueChanged(const wpi::json& json) override;
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_enabledCbKey = 0;
int32_t m_autonomousCbKey = 0;
int32_t m_testCbKey = 0;
int32_t m_estopCbKey = 0;
int32_t m_fmsCbKey = 0;
int32_t m_dsCbKey = 0;
int32_t m_allianceCbKey = 0;
int32_t m_matchTimeCbKey = 0;
int32_t m_newDataCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,38 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderEncoder : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderEncoder();
void OnNetValueChanged(const wpi::json& json) override;
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initCbKey = 0;
int32_t m_countCbKey = 0;
int32_t m_periodCbKey = 0;
int32_t m_resetCbKey = 0;
int32_t m_reverseDirectionCbKey = 0;
int32_t m_samplesCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,33 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderJoystick : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderJoystick();
void OnNetValueChanged(const wpi::json& json) override;
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_dsNewDataCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,36 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderPWM : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderPWM();
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initCbKey = 0;
int32_t m_speedCbKey = 0;
int32_t m_positionCbKey = 0;
int32_t m_rawCbKey = 0;
int32_t m_periodScaleCbKey = 0;
int32_t m_zeroLatchCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,34 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderRelay : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderRelay();
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initFwdCbKey = 0;
int32_t m_initRevCbKey = 0;
int32_t m_fwdCbKey = 0;
int32_t m_revCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,47 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderRoboRIO : public HALSimWSHalProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalProvider::HALSimWSHalProvider;
~HALSimWSProviderRoboRIO();
void OnNetValueChanged(const wpi::json& json) override;
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_fpgaCbKey = 0;
int32_t m_vinVoltageCbKey = 0;
int32_t m_vinCurrentCbKey = 0;
int32_t m_6vVoltageCbKey = 0;
int32_t m_6vCurrentCbKey = 0;
int32_t m_6vActiveCbKey = 0;
int32_t m_6vFaultsCbKey = 0;
int32_t m_5vVoltageCbKey = 0;
int32_t m_5vCurrentCbKey = 0;
int32_t m_5vActiveCbKey = 0;
int32_t m_5vFaultsCbKey = 0;
int32_t m_3v3VoltageCbKey = 0;
int32_t m_3v3CurrentCbKey = 0;
int32_t m_3v3ActiveCbKey = 0;
int32_t m_3v3FaultsCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,128 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include <string>
#include <hal/SimDevice.h>
#include <hal/simulation/SimDeviceData.h>
#include <wpi/StringMap.h>
#include <wpi/uv/AsyncFunction.h>
#include "WSBaseProvider.h"
#include "WSProviderContainer.h"
namespace wpilibws {
class HALSimWSProviderSimDevice;
class HALSimWSProviderSimDevices;
struct SimDeviceValueData {
HALSimWSProviderSimDevice* device;
HAL_SimValueHandle handle;
std::string key;
HAL_Type valueType;
};
class HALSimWSProviderSimDevice : public HALSimWSBaseProvider {
public:
HALSimWSProviderSimDevice(HAL_SimDeviceHandle handle, const std::string& key,
const std::string& deviceId)
: HALSimWSBaseProvider(key, "SimDevices"), m_handle(handle) {
m_deviceId = deviceId;
}
~HALSimWSProviderSimDevice();
void OnNetworkConnected(
std::shared_ptr<HALSimBaseWebSocketConnection> ws) override;
void OnNetworkDisconnected() override;
void OnNetValueChanged(const wpi::json& json) override;
void ProcessHalCallback(const wpi::json& payload);
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);
}
void OnValueCreated(const char* name, HAL_SimValueHandle handle,
HAL_Bool readonly, 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);
}
void OnValueChanged(SimDeviceValueData* valueData,
const struct HAL_Value* value);
void CancelCallbacks();
wpi::StringMap<std::unique_ptr<SimDeviceValueData>> m_valueHandles;
std::shared_mutex m_vhLock;
HAL_SimDeviceHandle m_handle;
std::shared_ptr<HALSimWSProviderSimDevices> m_simDevicesBase;
int32_t m_simValueCreatedCbKey = 0;
wpi::StringMap<int32_t> m_simValueChangedCbKeys;
};
class HALSimWSProviderSimDevices {
public:
using LoopFn = std::function<void(void)>;
using UvExecFn = wpi::uv::AsyncFunction<void(LoopFn)>;
explicit HALSimWSProviderSimDevices(ProviderContainer& providers)
: m_providers(providers) {}
~HALSimWSProviderSimDevices();
void Initialize(std::shared_ptr<wpi::uv::Loop> loop);
void OnNetworkConnected(std::shared_ptr<HALSimBaseWebSocketConnection> hws);
void OnNetworkDisconnected();
std::shared_ptr<HALSimBaseWebSocketConnection> GetWSConnection() {
return m_ws;
}
private:
static void DeviceCreatedCallbackStatic(const char* name, void* param,
HAL_SimDeviceHandle handle) {
(reinterpret_cast<HALSimWSProviderSimDevices*>(param))
->DeviceCreatedCallback(name, handle);
}
void DeviceCreatedCallback(const char* name, HAL_SimDeviceHandle handle);
static void DeviceFreedCallbackStatic(const char* name, void* param,
HAL_SimDeviceHandle handle) {
(reinterpret_cast<HALSimWSProviderSimDevices*>(param))
->DeviceFreedCallback(name, handle);
}
void DeviceFreedCallback(const char* name, HAL_SimDeviceHandle handle);
void CancelCallbacks();
ProviderContainer& m_providers;
std::shared_ptr<HALSimBaseWebSocketConnection> m_ws;
std::shared_ptr<UvExecFn> m_exec;
int32_t m_deviceCreatedCbKey = 0;
int32_t m_deviceFreedCbKey = 0;
};
} // namespace wpilibws

View File

@@ -0,0 +1,33 @@
/*----------------------------------------------------------------------------*/
/* Copyright (c) 2017-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 <memory>
#include "WSHalProviders.h"
namespace wpilibws {
class HALSimWSProviderDigitalPWM : public HALSimWSHalChanProvider {
public:
static void Initialize(WSRegisterFunc webRegisterFunc);
using HALSimWSHalChanProvider::HALSimWSHalChanProvider;
~HALSimWSProviderDigitalPWM();
protected:
void RegisterCallbacks() override;
void CancelCallbacks() override;
private:
int32_t m_initCbKey = 0;
int32_t m_dutyCycleCbKey = 0;
int32_t m_pinCbKey = 0;
};
} // namespace wpilibws