Update for jart/json.cpp change

This commit is contained in:
Peter Johnson
2026-03-29 15:38:18 -07:00
parent de3e211fdb
commit 9ca93fa190
120 changed files with 1240 additions and 1087 deletions

View File

@@ -177,8 +177,8 @@ void HALSimWS::OnNetValueChanged(const wpi::util::json& msg) {
// generate the key
try {
auto& type = msg.at("type").get_ref<const std::string&>();
auto& device = msg.at("device").get_ref<const std::string&>();
auto& type = msg.at("type").get_string();
auto& device = msg.at("device").get_string();
wpi::util::SmallString<64> key;
key.append(type);
@@ -191,7 +191,7 @@ void HALSimWS::OnNetValueChanged(const wpi::util::json& msg) {
if (provider) {
provider->OnNetValueChanged(msg.at("data"));
}
} catch (wpi::util::json::exception& e) {
} catch (std::logic_error& e) {
wpi::util::print(stderr, "Error with incoming message: {}\n", e.what());
}
}

View File

@@ -48,18 +48,15 @@ void HALSimWSClientConnection::Initialize() {
return;
}
wpi::util::json j;
try {
j = wpi::util::json::parse(msg);
} catch (const wpi::util::json::parse_error& e) {
std::string err("JSON parse failed: ");
err += e.what();
auto j = wpi::util::json::parse(msg);
if (!j) {
auto err = fmt::format("JSON parse failed: {}", j.error());
wpi::util::print(stderr, "{}\n", err);
m_websocket->Fail(1003, err);
return;
}
m_client->OnNetValueChanged(j);
m_client->OnNetValueChanged(*j);
});
m_websocket->closed.connect([this](uint16_t, auto) {
@@ -79,11 +76,11 @@ void HALSimWSClientConnection::OnSimValueChanged(const wpi::util::json& msg) {
// Skip sending if this message is not in the allowed filter list
try {
auto& type = msg.at("type").get_ref<const std::string&>();
auto& type = msg.at("type").get_string();
if (!m_client->CanSendMessage(type)) {
return;
}
} catch (wpi::util::json::exception& e) {
} catch (std::logic_error& e) {
wpi::util::print(stderr, "Error with message: {}\n", e.what());
}

View File

@@ -16,7 +16,10 @@
#include "wpi/net/uv/Tcp.hpp"
#include "wpi/net/uv/Timer.hpp"
#include "wpi/util/StringMap.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibws {

View File

@@ -12,8 +12,10 @@
#include "wpi/net/WebSocket.hpp"
#include "wpi/net/uv/Buffer.hpp"
#include "wpi/net/uv/Stream.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibws {

View File

@@ -27,8 +27,10 @@ void HALSimWSHalProvider::OnNetworkDisconnected() {
void HALSimWSHalProvider::ProcessHalCallback(const wpi::util::json& payload) {
auto ws = m_ws.lock();
if (ws) {
wpi::util::json netValue = {
{"type", m_type}, {"device", m_deviceId}, {"data", payload}};
auto netValue = wpi::util::json::object();
netValue["type"] = m_type;
netValue["device"] = m_deviceId;
netValue["data"] = payload;
ws->OnSimValueChanged(netValue);
}
}

View File

@@ -4,19 +4,20 @@
#include "wpi/halsim/ws_core/WSProvider_AddressableLED.hpp"
#include <utility>
#include <vector>
#include "wpi/hal/Ports.h"
#include "wpi/hal/simulation/AddressableLEDData.h"
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterAddressableLED##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderAddressableLED*>(param) \
->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
}, \
#define REGISTER(halsim, jsonid, ctype, haltype) \
HALSIM_RegisterAddressableLED##halsim##Callback( \
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderAddressableLED*>(param) \
->ProcessHalCallback(wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
namespace wpilibws {
void HALSimWSProviderAddressableLED::Initialize(
@@ -43,15 +44,17 @@ void HALSimWSProviderAddressableLED::RegisterCallbacks() {
const HAL_AddressableLEDData* data =
reinterpret_cast<const HAL_AddressableLEDData*>(buffer);
std::vector<wpi::util::json> jsonData;
auto jsonData = wpi::util::json::array();
for (size_t i = 0; i < numLeds; ++i) {
jsonData.push_back(
{{"r", data[i].r}, {"g", data[i].g}, {"b", data[i].b}});
jsonData.emplace_back(
wpi::util::json::object("r", static_cast<int64_t>(data[i].r), "g",
static_cast<int64_t>(data[i].g), "b",
static_cast<int64_t>(data[i].b)));
}
wpi::util::json payload;
payload["<data"] = jsonData;
payload["<data"] = std::move(jsonData);
provider->ProcessHalCallback(payload);
},

View File

@@ -12,7 +12,8 @@
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)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
@@ -54,9 +55,8 @@ void HALSimWSProviderAnalogIn::DoCancelCallbacks() {
}
void HALSimWSProviderAnalogIn::OnNetValueChanged(const wpi::util::json& json) {
wpi::util::json::const_iterator it;
if ((it = json.find(">voltage")) != json.end()) {
HALSIM_SetAnalogInVoltage(m_channel, it.value());
if (auto val = json.lookup(">voltage"); val && val->is_number()) {
HALSIM_SetAnalogInVoltage(m_channel, val->get_number());
}
}

View File

@@ -12,7 +12,8 @@
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)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
@@ -51,9 +52,8 @@ void HALSimWSProviderDIO::DoCancelCallbacks() {
}
void HALSimWSProviderDIO::OnNetValueChanged(const wpi::util::json& json) {
wpi::util::json::const_iterator it;
if ((it = json.find("<>value")) != json.end()) {
HALSIM_SetDIOValue(m_channel, static_cast<bool>(it.value()));
if (auto val = json.lookup("<>value"); val && val->is_bool()) {
HALSIM_SetDIOValue(m_channel, val->get_bool());
}
}

View File

@@ -12,13 +12,13 @@
#include "wpi/hal/simulation/DriverStationData.h"
#include "wpi/util/string.hpp"
#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)}}); \
}, \
#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(wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
namespace wpilibws {
@@ -55,7 +55,7 @@ void HALSimWSProviderDriverStation::RegisterCallbacks() {
m_newDataCbKey = HALSIM_RegisterDriverStationNewDataCallback(
[](const char* name, void* param, const struct HAL_Value* value) {
static_cast<HALSimWSProviderDriverStation*>(param)->ProcessHalCallback(
{{">new_data", true}});
wpi::util::json::object(">new_data", true));
},
this, true);
@@ -86,7 +86,7 @@ void HALSimWSProviderDriverStation::RegisterCallbacks() {
break;
}
static_cast<HALSimWSProviderDriverStation*>(param)->ProcessHalCallback(
{{">station", station}});
wpi::util::json::object(">station", station));
},
this, true);
@@ -124,25 +124,25 @@ void HALSimWSProviderDriverStation::OnNetValueChanged(
return;
}
wpi::util::json::const_iterator it;
if ((it = json.find(">enabled")) != json.end()) {
HALSIM_SetDriverStationEnabled(it.value());
if (auto val = json.lookup(">enabled"); val && val->is_bool()) {
HALSIM_SetDriverStationEnabled(val->get_bool());
}
if ((it = json.find(">robotMode")) != json.end()) {
HALSIM_SetDriverStationRobotMode(it.value());
if (auto val = json.lookup(">robotMode"); val && val->is_int()) {
HALSIM_SetDriverStationRobotMode(
static_cast<HAL_RobotMode>(val->get_int()));
}
if ((it = json.find(">estop")) != json.end()) {
HALSIM_SetDriverStationEStop(it.value());
if (auto val = json.lookup(">estop"); val && val->is_bool()) {
HALSIM_SetDriverStationEStop(val->get_bool());
}
if ((it = json.find(">fms")) != json.end()) {
HALSIM_SetDriverStationFmsAttached(it.value());
if (auto val = json.lookup(">fms"); val && val->is_bool()) {
HALSIM_SetDriverStationFmsAttached(val->get_bool());
}
if ((it = json.find(">ds")) != json.end()) {
HALSIM_SetDriverStationDsAttached(it.value());
if (auto val = json.lookup(">ds"); val && val->is_bool()) {
HALSIM_SetDriverStationDsAttached(val->get_bool());
}
if ((it = json.find(">station")) != json.end()) {
auto& station = it.value().get_ref<const std::string&>();
if (auto val = json.lookup(">station"); val && val->is_string()) {
auto& station = val->get_string();
if (station == "red1") {
HALSIM_SetDriverStationAllianceStationId(HAL_ALLIANCE_STATION_RED_1);
} else if (station == "red2") {
@@ -158,17 +158,17 @@ void HALSimWSProviderDriverStation::OnNetValueChanged(
}
}
if ((it = json.find(">match_time")) != json.end()) {
HALSIM_SetDriverStationMatchTime(it.value());
if (auto val = json.lookup(">match_time"); val && val->is_number()) {
HALSIM_SetDriverStationMatchTime(val->get_number());
}
if ((it = json.find(">game_data")) != json.end()) {
std::string message = it.value().get_ref<const std::string&>();
if (auto val = json.lookup(">game_data"); val && val->is_string()) {
std::string message = val->get_string();
auto str = wpi::util::make_string(message);
HALSIM_SetGameDataString(&str);
}
// Only notify usercode if we get the new data message
if ((it = json.find(">new_data")) != json.end()) {
if (json.contains(">new_data")) {
HALSIM_NotifyDriverStationNewData();
}
}

View File

@@ -12,7 +12,8 @@
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)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
@@ -36,7 +37,7 @@ void HALSimWSProviderEncoder::RegisterCallbacks() {
auto provider = static_cast<HALSimWSProviderEncoder*>(param);
bool init = static_cast<bool>(value->data.v_boolean);
wpi::util::json payload = {{"<init", init}};
auto payload = wpi::util::json::object("<init", init);
if (init) {
payload["<channel_a"] =
@@ -52,9 +53,9 @@ void HALSimWSProviderEncoder::RegisterCallbacks() {
m_channel,
[](const char* name, void* param, const struct HAL_Value* value) {
auto provider = static_cast<HALSimWSProviderEncoder*>(param);
provider->ProcessHalCallback(
{{">count", static_cast<int32_t>(value->data.v_int +
provider->m_countOffset)}});
provider->ProcessHalCallback(wpi::util::json::object(
">count",
static_cast<int32_t>(value->data.v_int + provider->m_countOffset)));
},
this, true);
m_periodCbKey = REGISTER(Period, ">period", double, double);
@@ -96,13 +97,12 @@ void HALSimWSProviderEncoder::DoCancelCallbacks() {
}
void HALSimWSProviderEncoder::OnNetValueChanged(const wpi::util::json& json) {
wpi::util::json::const_iterator it;
if ((it = json.find(">count")) != json.end()) {
HALSIM_SetEncoderCount(m_channel,
static_cast<int32_t>(it.value()) - m_countOffset);
if (auto val = json.lookup(">count"); val && val->is_int()) {
HALSIM_SetEncoderCount(m_channel, val->get_int() - m_countOffset);
}
if ((it = json.find(">period")) != json.end()) {
HALSIM_SetEncoderPeriod(m_channel, it.value());
if (auto val = json.lookup(">period"); val && val->is_number()) {
HALSIM_SetEncoderPeriod(m_channel, val->get_number());
}
}

View File

@@ -27,14 +27,14 @@ void HALSimWSProviderHAL::RegisterCallbacks() {
m_simPeriodicBeforeCbKey = HALSIM_RegisterSimPeriodicBeforeCallback(
[](void* param) {
static_cast<HALSimWSProviderHAL*>(param)->ProcessHalCallback(
{{">sim_periodic_before", true}});
wpi::util::json::object(">sim_periodic_before", true));
},
this);
m_simPeriodicAfterCbKey = HALSIM_RegisterSimPeriodicAfterCallback(
[](void* param) {
static_cast<HALSimWSProviderHAL*>(param)->ProcessHalCallback(
{{">sim_periodic_after", true}});
wpi::util::json::object(">sim_periodic_after", true));
},
this);
}

View File

@@ -8,6 +8,7 @@
#include <atomic>
#include <vector>
#include "wpi/hal/DriverStationTypes.h"
#include "wpi/hal/Ports.h"
#include "wpi/hal/simulation/DriverStationData.h"
@@ -103,31 +104,34 @@ void HALSimWSProviderJoystick::OnNetValueChanged(const wpi::util::json& json) {
return;
}
wpi::util::json::const_iterator it;
if ((it = json.find(">axes")) != json.end()) {
if (auto val = json.lookup(">axes"); val && val->is_array()) {
auto& arr = val->get_array();
HAL_JoystickAxes axes{};
wpi::util::json::size_type axesCount = std::min(
it.value().size(),
static_cast<wpi::util::json::size_type>(HAL_MAX_JOYSTICK_AXES));
size_t axesCount =
std::min(arr.size(), static_cast<size_t>(HAL_MAX_JOYSTICK_AXES));
axes.available = (1 << axesCount) - 1;
for (wpi::util::json::size_type i = 0; i < axesCount; i++) {
axes.axes[i] = it.value()[i];
for (size_t i = 0; i < axesCount; i++) {
if (arr[i].is_number()) {
axes.axes[i] = arr[i].get_number();
} else {
axes.axes[i] = 0.0;
}
}
HALSIM_SetJoystickAxes(m_channel, &axes);
}
if ((it = json.find(">buttons")) != json.end()) {
if (auto val = json.lookup(">buttons"); val && val->is_array()) {
HAL_JoystickButtons buttons{};
wpi::util::json::size_type buttonsCount = std::min(
it.value().size(), static_cast<wpi::util::json::size_type>(64));
auto& arr = val->get_array();
size_t buttonsCount = std::min(arr.size(), static_cast<size_t>(64));
if (buttonsCount < 64) {
buttons.available = (1ULL << buttonsCount) - 1;
} else {
buttons.available = (std::numeric_limits<uint64_t>::max)();
}
for (wpi::util::json::size_type i = 0; i < buttonsCount; i++) {
if (it.value()[i]) {
for (size_t i = 0; i < buttonsCount; i++) {
if (arr[i].is_bool() && arr[i].get_bool()) {
buttons.buttons |= 1llu << i;
}
}
@@ -135,14 +139,18 @@ void HALSimWSProviderJoystick::OnNetValueChanged(const wpi::util::json& json) {
HALSIM_SetJoystickButtons(m_channel, &buttons);
}
if ((it = json.find(">povs")) != json.end()) {
if (auto val = json.lookup(">povs"); val && val->is_array()) {
HAL_JoystickPOVs povs{};
wpi::util::json::size_type povsCount = std::min(
it.value().size(),
static_cast<wpi::util::json::size_type>(HAL_MAX_JOYSTICK_POVS));
auto& arr = val->get_array();
size_t povsCount =
std::min(arr.size(), static_cast<size_t>(HAL_MAX_JOYSTICK_POVS));
povs.available = (1 << povsCount) - 1;
for (wpi::util::json::size_type i = 0; i < povsCount; i++) {
povs.povs[i] = it.value()[i];
for (size_t i = 0; i < povsCount; i++) {
if (arr[i].is_int()) {
povs.povs[i] = static_cast<HAL_JoystickPOV>(arr[i].get_int());
} else {
povs.povs[i] = HAL_JOYSTICK_POV_CENTERED;
}
}
HALSIM_SetJoystickPOVs(m_channel, &povs);

View File

@@ -12,7 +12,8 @@
m_channel, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderPCM*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
namespace wpilibws {

View File

@@ -12,7 +12,8 @@
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)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
namespace wpilibws {

View File

@@ -11,7 +11,8 @@
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)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)
@@ -54,22 +55,20 @@ void HALSimWSProviderRoboRIO::DoCancelCallbacks() {
}
void HALSimWSProviderRoboRIO::OnNetValueChanged(const wpi::util::json& json) {
wpi::util::json::const_iterator it;
if ((it = json.find(">vin_voltage")) != json.end()) {
HALSIM_SetRoboRioVInVoltage(it.value());
if (auto val = json.lookup(">vin_voltage"); val && val->is_number()) {
HALSIM_SetRoboRioVInVoltage(val->get_number());
}
if ((it = json.find(">3v3_voltage")) != json.end()) {
HALSIM_SetRoboRioUserVoltage3V3(it.value());
if (auto val = json.lookup(">3v3_voltage"); val && val->is_number()) {
HALSIM_SetRoboRioUserVoltage3V3(val->get_number());
}
if ((it = json.find(">3v3_current")) != json.end()) {
HALSIM_SetRoboRioUserCurrent3V3(it.value());
if (auto val = json.lookup(">3v3_current"); val && val->is_number()) {
HALSIM_SetRoboRioUserCurrent3V3(val->get_number());
}
if ((it = json.find(">3v3_active")) != json.end()) {
HALSIM_SetRoboRioUserActive3V3(static_cast<bool>(it.value()));
if (auto val = json.lookup(">3v3_active"); val && val->is_bool()) {
HALSIM_SetRoboRioUserActive3V3(val->get_bool());
}
if ((it = json.find(">3v3_faults")) != json.end()) {
HALSIM_SetRoboRioUserFaults3V3(it.value());
if (auto val = json.lookup(">3v3_faults"); val && val->is_number()) {
HALSIM_SetRoboRioUserFaults3V3(val->get_number());
}
}

View File

@@ -62,53 +62,59 @@ void HALSimWSProviderSimDevice::CancelCallbacks() {
}
void HALSimWSProviderSimDevice::OnNetValueChanged(const wpi::util::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());
for (auto&& [key, jvalue] : json.get_object()) {
auto vd = m_valueHandles.find(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;
if (jvalue.is_bool()) {
value.data.v_boolean = jvalue.get_bool() ? 1 : 0;
}
break;
case HAL_DOUBLE:
value.data.v_double = it.value();
value.data.v_double -= vd->second.doubleOffset;
if (jvalue.is_number()) {
value.data.v_double = jvalue.get_double();
value.data.v_double -= vd->second.doubleOffset;
}
break;
case HAL_ENUM: {
if (it->is_string()) {
if (jvalue.is_string()) {
auto& options = vd->second.options;
auto& str = it.value().get_ref<const std::string&>();
auto& str = jvalue.get_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()) {
} else if (jvalue.is_float() || jvalue.is_double()) {
auto& values = vd->second.optionValues;
double num = it.value();
double num = jvalue.get_number();
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();
}
} else if (jvalue.is_int()) {
value.data.v_enum = jvalue.get_int();
}
value.data.v_enum = it.value();
break;
}
case HAL_INT:
value.data.v_int = it.value();
value.data.v_int -= vd->second.intOffset;
if (jvalue.is_int()) {
value.data.v_int = jvalue.get_int();
value.data.v_int -= vd->second.intOffset;
}
break;
case HAL_LONG:
value.data.v_long = it.value();
value.data.v_long -= vd->second.intOffset;
if (jvalue.is_int()) {
value.data.v_long = jvalue.get_int();
value.data.v_long -= vd->second.intOffset;
}
break;
default:
break;
@@ -193,29 +199,31 @@ void HALSimWSProviderSimDevice::OnValueChanged(SimDeviceValueData* valueData,
if (ws) {
switch (value->type) {
case HAL_BOOLEAN:
ProcessHalCallback(
{{valueData->key, static_cast<bool>(value->data.v_boolean)}});
ProcessHalCallback(wpi::util::json::object(
valueData->key, static_cast<bool>(value->data.v_boolean)));
break;
case HAL_DOUBLE:
ProcessHalCallback(
{{valueData->key, value->data.v_double + valueData->doubleOffset}});
ProcessHalCallback(wpi::util::json::object(
valueData->key, value->data.v_double + valueData->doubleOffset));
break;
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]}});
ProcessHalCallback(wpi::util::json::object(
valueData->key, valueData->optionValues[v]));
} else if (v >= 0 && v < static_cast<int>(valueData->options.size())) {
ProcessHalCallback({{valueData->key, valueData->options[v]}});
ProcessHalCallback(
wpi::util::json::object(valueData->key, valueData->options[v]));
}
break;
}
case HAL_INT:
ProcessHalCallback(
{{valueData->key, value->data.v_int + valueData->intOffset}});
ProcessHalCallback(wpi::util::json::object(
valueData->key, value->data.v_int + valueData->intOffset));
break;
case HAL_LONG:
ProcessHalCallback(
{{valueData->key, value->data.v_long + valueData->intOffset}});
ProcessHalCallback(wpi::util::json::object(
valueData->key, value->data.v_long + valueData->intOffset));
break;
default:
break;
@@ -254,8 +262,10 @@ void HALSimWSProviderSimDevice::ProcessHalCallback(
const wpi::util::json& payload) {
auto ws = m_ws.lock();
if (ws) {
wpi::util::json netValue = {
{"type", m_type}, {"device", m_deviceId}, {"data", payload}};
auto netValue = wpi::util::json::object();
netValue["type"] = m_type;
netValue["device"] = m_deviceId;
netValue["data"] = payload;
ws->OnSimValueChanged(netValue);
}
}

View File

@@ -18,7 +18,8 @@
m_pcmIndex, m_solenoidIndex, \
[](const char* name, void* param, const struct HAL_Value* value) { \
static_cast<HALSimWSProviderSolenoid*>(param)->ProcessHalCallback( \
{{jsonid, static_cast<ctype>(value->data.v_##haltype)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)

View File

@@ -12,7 +12,8 @@
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)}}); \
wpi::util::json::object( \
jsonid, static_cast<ctype>(value->data.v_##haltype))); \
}, \
this, true)

View File

@@ -6,7 +6,9 @@
#include <memory>
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibws {

View File

@@ -13,8 +13,10 @@
#include "wpi/hal/simulation/NotifyListener.h"
#include "wpi/halsim/ws_core/WSBaseProvider.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibws {

View File

@@ -54,16 +54,12 @@ void HALSimHttpConnection::ProcessWsUpgrade() {
return;
}
wpi::util::json j;
try {
j = wpi::util::json::parse(msg);
} catch (const wpi::util::json::parse_error& e) {
std::string err("JSON parse failed: ");
err += e.what();
m_websocket->Fail(400, err);
auto j = wpi::util::json::parse(msg);
if (!j) {
m_websocket->Fail(400, fmt::format("JSON parse failed: {}", j.error()));
return;
}
m_server->OnNetValueChanged(j);
m_server->OnNetValueChanged(*j);
});
m_websocket->closed.connect([this](uint16_t, auto) {
@@ -80,11 +76,11 @@ void HALSimHttpConnection::ProcessWsUpgrade() {
void HALSimHttpConnection::OnSimValueChanged(const wpi::util::json& msg) {
// Skip sending if this message is not in the allowed filter list
try {
auto& type = msg.at("type").get_ref<const std::string&>();
auto& type = msg.at("type").get_string();
if (!m_server->CanSendMessage(type)) {
return;
}
} catch (wpi::util::json::exception& e) {
} catch (std::logic_error& e) {
wpi::util::print(stderr, "Error with message: {}\n", e.what());
}

View File

@@ -162,8 +162,8 @@ void HALSimWeb::OnNetValueChanged(const wpi::util::json& msg) {
// generate the key
try {
auto& type = msg.at("type").get_ref<const std::string&>();
auto& device = msg.at("device").get_ref<const std::string&>();
auto& type = msg.at("type").get_string();
auto& device = msg.at("device").get_string();
wpi::util::SmallString<64> key;
key.append(type);
@@ -176,7 +176,7 @@ void HALSimWeb::OnNetValueChanged(const wpi::util::json& msg) {
if (provider) {
provider->OnNetValueChanged(msg.at("data"));
}
} catch (wpi::util::json::exception& e) {
} catch (std::logic_error& e) {
wpi::util::print(stderr, "Error with incoming message: {}\n", e.what());
}
}

View File

@@ -4,7 +4,6 @@
#pragma once
#include <cinttypes>
#include <memory>
#include <string_view>
#include <utility>
@@ -12,10 +11,11 @@
#include "wpi/halsim/ws_core/HALSimBaseWebSocketConnection.hpp"
#include "wpi/halsim/ws_server/HALSimWeb.hpp"
#include "wpi/net/HttpWebSocketServerConnection.hpp"
#include "wpi/net/uv/AsyncFunction.hpp"
#include "wpi/net/uv/Buffer.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibws {

View File

@@ -16,7 +16,10 @@
#include "wpi/net/uv/Loop.hpp"
#include "wpi/net/uv/Tcp.hpp"
#include "wpi/util/StringMap.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibws {

View File

@@ -44,18 +44,15 @@ void WebServerClientTest::InitializeWebSocket(const std::string& host, int port,
});
m_websocket->text.connect([this](auto msg, bool) {
wpi::util::json j;
try {
j = wpi::util::json::parse(msg);
} catch (const wpi::util::json::parse_error& e) {
std::string err("JSON parse failed: ");
err += e.what();
auto j = wpi::util::json::parse(msg);
if (!j) {
auto err = fmt::format("JSON parse failed: {}", j.error());
wpi::util::print(stderr, "{}\n", err);
m_websocket->Fail(1003, err);
return;
}
// Save last message received
m_json = j;
m_json = *j;
});
m_websocket->closed.connect([this](uint16_t, auto) {

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <gtest/gtest.h>
@@ -78,14 +79,13 @@ TEST_F(WebServerIntegrationTest, DISABLED_DigitalOutput) {
bool test_value = true; // Default value from HAL initialization
try {
auto& msg = m_webserverClient->GetLastMessage();
test_type = msg.at("type").get_ref<const std::string&>();
test_device = msg.at("device").get_ref<const std::string&>();
test_type = msg.at("type").get_string();
test_device = msg.at("device").get_string();
auto& data = msg.at("data");
wpi::util::json::const_iterator it = data.find("<>value");
if (it != data.end()) {
test_value = it.value();
if (auto val = data.lookup("<>value"); val && val->is_bool()) {
test_value = val->get_bool();
}
} catch (wpi::util::json::exception& e) {
} catch (std::logic_error& e) {
wpi::util::print(stderr, "Error with incoming message: {}\n", e.what());
}
@@ -109,10 +109,11 @@ TEST_F(WebServerIntegrationTest, DISABLED_DigitalInput) {
return;
}
if (IsConnectedClientWS()) {
wpi::util::json msg = {{"type", "DIO"},
{"device", std::to_string(PIN)},
{"data", {{"<>value", EXPECTED_VALUE}}}};
wpi::util::print("***** Input JSON: {}\n", msg.dump());
wpi::util::json msg = wpi::util::json::object();
msg["type"] = "DIO";
msg["device"] = std::to_string(PIN);
msg["data"] = wpi::util::json::object("<>value", EXPECTED_VALUE);
wpi::util::print("***** Input JSON: {}\n", msg.to_string());
m_webserverClient->SendMessage(msg);
done = true;
}
@@ -143,11 +144,14 @@ TEST_F(WebServerIntegrationTest, DriverStation) {
return;
}
if (IsConnectedClientWS()) {
wpi::util::json msg = {
{"type", "DriverStation"},
{"device", ""},
{"data", {{">enabled", EXPECTED_VALUE}, {">new_data", true}}}};
wpi::util::print("***** Input JSON: {}\n", msg.dump());
auto data = wpi::util::json::object();
data[">enabled"] = EXPECTED_VALUE;
data[">new_data"] = true;
wpi::util::json msg = wpi::util::json::object();
msg["type"] = "DriverStation";
msg["device"] = "";
msg["data"] = std::move(data);
wpi::util::print("***** Input JSON: {}\n", msg.to_string());
m_webserverClient->SendMessage(msg);
done = true;
}

View File

@@ -107,8 +107,8 @@ void HALSimXRP::ParsePacket(std::span<const uint8_t> packet) {
void HALSimXRP::OnNetValueChanged(const wpi::util::json& msg) {
try {
auto& type = msg.at("type").get_ref<const std::string&>();
auto& device = msg.at("device").get_ref<const std::string&>();
auto& type = msg.at("type").get_string();
auto& device = msg.at("device").get_string();
wpi::util::SmallString<64> key;
key.append(type);
@@ -121,16 +121,17 @@ void HALSimXRP::OnNetValueChanged(const wpi::util::json& msg) {
if (provider) {
provider->OnNetValueChanged(msg.at("data"));
}
} catch (wpi::util::json::exception& e) {
} catch (std::logic_error& e) {
wpi::util::print(stderr, "Error with incoming message: {}\n", e.what());
}
}
void HALSimXRP::OnSimValueChanged(const wpi::util::json& simData) {
// We'll use a signal from robot code to send all the data
if (simData["type"] == "HAL") {
auto halData = simData["data"];
if (halData.find(">sim_periodic_after") != halData.end()) {
auto type = simData.lookup("type");
if (type->is_string() && type->get_string() == "HAL") {
auto halData = simData.lookup("data");
if (halData && halData->contains(">sim_periodic_after")) {
SendStateToXRP();
}
} else {

View File

@@ -6,6 +6,7 @@
#include <bit>
#include <string>
#include <utility>
#include <fmt/format.h>
@@ -33,21 +34,23 @@ XRP::XRP()
}
void XRP::HandleWPILibUpdate(const wpi::util::json& data) {
if (data.count("type") == 0) {
auto type = data.lookup("type");
if (!type || !type->is_string()) {
return;
}
if (data["type"] == "DriverStation") {
auto& typeStr = type->get_string();
if (typeStr == "DriverStation") {
HandleDriverStationSimValueChanged(data);
} else if (data["type"] == "XRPMotor") {
} else if (typeStr == "XRPMotor") {
HandleMotorSimValueChanged(data);
} else if (data["type"] == "XRPServo") {
} else if (typeStr == "XRPServo") {
HandleServoSimValueChanged(data);
} else if (data["type"] == "DIO") {
} else if (typeStr == "DIO") {
HandleDIOSimValueChanged(data);
} else if (data["type"] == "Gyro") {
} else if (typeStr == "Gyro") {
HandleGyroSimValueChanged(data);
} else if (data["type"] == "Encoder") {
} else if (typeStr == "Encoder") {
HandleEncoderSimValueChanged(data);
}
}
@@ -108,56 +111,84 @@ void XRP::SetupXRPSendBuffer(wpi::net::raw_uv_ostream& buf) {
// WPILib Sim Handlers
void XRP::HandleDriverStationSimValueChanged(const wpi::util::json& data) {
auto dsData = data["data"];
if (dsData.find(">enabled") != dsData.end()) {
m_robot_enabled = dsData[">enabled"];
auto dsData = data.lookup("data");
if (!dsData || !dsData->is_object()) {
return;
}
auto enabled = dsData->lookup(">enabled");
if (enabled && enabled->is_bool()) {
m_robot_enabled = enabled->get_bool();
}
}
void XRP::HandleMotorSimValueChanged(const wpi::util::json& data) {
int deviceId = -1;
auto motorData = data["data"];
auto motorData = data.lookup("data");
if (!motorData || !motorData->is_object()) {
return;
}
if (data["device"] == "motorL") {
auto device = data.lookup("device");
if (!device || !device->is_string()) {
return;
}
auto& deviceStr = device->get_string();
if (deviceStr == "motorL") {
deviceId = 0;
} else if (data["device"] == "motorR") {
} else if (deviceStr == "motorR") {
deviceId = 1;
} else if (data["device"] == "motor3") {
} else if (deviceStr == "motor3") {
deviceId = 2;
} else if (data["device"] == "motor4") {
} else if (deviceStr == "motor4") {
deviceId = 3;
}
if (deviceId != -1 && motorData.find("<duty_cycle") != motorData.end()) {
m_motor_outputs[deviceId] = motorData["<duty_cycle"];
auto dutyCycle = motorData->lookup("<duty_cycle");
if (deviceId != -1 && dutyCycle && dutyCycle->is_number()) {
m_motor_outputs[deviceId] = dutyCycle->get_number();
}
}
void XRP::HandleServoSimValueChanged(const wpi::util::json& data) {
int deviceId = -1;
auto servoData = data["data"];
auto servoData = data.lookup("data");
if (!servoData || !servoData->is_object()) {
return;
}
if (data["device"] == "servo1") {
auto device = data.lookup("device");
if (!device || !device->is_string()) {
return;
}
auto& deviceStr = device->get_string();
if (deviceStr == "servo1") {
deviceId = 4;
} else if (data["device"] == "servo2") {
} else if (deviceStr == "servo2") {
deviceId = 5;
} else if (data["device"] == "servo3") {
} else if (deviceStr == "servo3") {
deviceId = 6;
} else if (data["device"] == "servo4") {
} else if (deviceStr == "servo4") {
deviceId = 7;
}
if (deviceId != -1 && servoData.find("<position") != servoData.end()) {
m_servo_outputs[deviceId] = servoData["<position"];
auto position = servoData->lookup("<position");
if (deviceId != -1 && position && position->is_number()) {
m_servo_outputs[deviceId] = position->get_number();
}
}
void XRP::HandleDIOSimValueChanged(const wpi::util::json& data) {
int deviceId = -1;
auto dioData = data["data"];
auto dioData = data.lookup("data");
auto device = data.lookup("device");
if (!device || !device->is_string()) {
return;
}
try {
deviceId = std::stoi(data["device"].get<std::string>());
deviceId = std::stoi(device->get_string());
} catch (const std::invalid_argument&) {
deviceId = -1;
}
@@ -167,26 +198,31 @@ void XRP::HandleDIOSimValueChanged(const wpi::util::json& data) {
return;
}
if (dioData.find("<init") != dioData.end() && dioData["<init"]) {
auto init = dioData->lookup("<init");
if (init && init->is_bool() && init->get_bool()) {
// All DIOs are initialized as inputs by default
m_digital_inputs.emplace(deviceId, false);
}
if (dioData.find("<input") != dioData.end() && dioData["<input"] == false) {
auto input = dioData->lookup("<input");
if (input && input->is_bool() && !input->get_bool()) {
// We're registering an output device
// Remove from the digital inputs list (if present)
m_digital_inputs.erase(deviceId);
m_digital_outputs.emplace(deviceId, false);
}
if (dioData.find("<>value") != dioData.end() &&
m_digital_outputs.count(deviceId) > 0) {
m_digital_outputs[deviceId] = dioData["<>value"];
auto value = dioData->lookup("<>value");
if (value && value->is_bool() && m_digital_outputs.count(deviceId) > 0) {
m_digital_outputs[deviceId] = value->get_bool();
}
}
void XRP::HandleGyroSimValueChanged(const wpi::util::json& data) {
m_gyro_name = data["device"].get<std::string>();
auto name = data.lookup("device");
if (name && name->is_string()) {
m_gyro_name = name->get_string();
}
}
void XRP::HandleEncoderSimValueChanged(const wpi::util::json& data) {
@@ -196,10 +232,18 @@ void XRP::HandleEncoderSimValueChanged(const wpi::util::json& data) {
// 8/9 -> Encoder 2
// 10/11 -> Encoder 3
int deviceId = -1;
auto encData = data["data"];
auto encData = data.lookup("data");
if (!encData || !encData->is_object()) {
return;
}
auto device = data.lookup("device");
if (!device || !device->is_string()) {
return;
}
try {
deviceId = std::stoi(data["device"].get<std::string>());
deviceId = std::stoi(device->get_string());
} catch (const std::invalid_argument&) {
deviceId = -1;
}
@@ -208,10 +252,14 @@ void XRP::HandleEncoderSimValueChanged(const wpi::util::json& data) {
return;
}
if (encData.find("<init") != encData.end() && encData["<init"]) {
auto init = encData->lookup("<init");
auto jchA = encData->lookup("<channel_a");
auto jchB = encData->lookup("<channel_b");
if (init && init->is_bool() && init->get_bool() && jchA && jchA->is_int() &&
jchB && jchB->is_int()) {
// The <channel_a and <channel_b values come with the init message
int chA = encData["<channel_a"];
int chB = encData["<channel_b"];
int chA = jchA->get_int();
int chB = jchB->get_int();
if ((chA == 4 && chB == 5) || (chA == 5 && chB == 4)) {
m_encoder_channel_map.emplace(0, deviceId);
@@ -302,9 +350,14 @@ void XRP::ReadGyroTag(std::span<const uint8_t> packet) {
wpi::util::json gyroJson;
gyroJson["type"] = "Gyro";
gyroJson["device"] = m_gyro_name;
gyroJson["data"] = {{">rate_x", rate_x}, {">rate_y", rate_y},
{">rate_z", rate_z}, {">angle_x", angle_x},
{">angle_y", angle_y}, {">angle_z", angle_z}};
auto data = wpi::util::json::object();
data[">rate_x"] = rate_x;
data[">rate_y"] = rate_y;
data[">rate_z"] = rate_z;
data[">angle_x"] = angle_x;
data[">angle_y"] = angle_y;
data[">angle_z"] = angle_z;
gyroJson["data"] = std::move(data);
// Update WPILib
m_wpilib_update_func(gyroJson);
@@ -318,7 +371,7 @@ void XRP::ReadDIOTag(std::span<const uint8_t> packet) {
wpi::util::json dioJson;
dioJson["type"] = "DIO";
dioJson["device"] = std::to_string(packet[2]);
dioJson["data"] = {{"<>value", packet[3] == 1}};
dioJson["data"] = wpi::util::json::object("<>value", packet[3] == 1);
m_wpilib_update_func(dioJson);
}
@@ -348,7 +401,7 @@ void XRP::ReadEncoderTag(std::span<const uint8_t> packet) {
wpi::util::json encJson;
encJson["type"] = "Encoder";
encJson["device"] = std::to_string(wpilibEncoderChannel);
encJson["data"] = {{">count", count}};
encJson["data"] = wpi::util::json::object(">count", count);
if (containsPeriod) {
// Older versions of XRP firmware do not provide Encoder Period.
@@ -375,8 +428,7 @@ void XRP::ReadEncoderTag(std::span<const uint8_t> packet) {
period = -period;
}
encJson["data"].push_back(
{wpi::util::json(">period"), wpi::util::json(period)});
encJson["data"].emplace_back(wpi::util::json::object(">period", period));
}
m_wpilib_update_func(encJson);
@@ -396,7 +448,7 @@ void XRP::ReadAnalogTag(std::span<const uint8_t> packet) {
wpi::util::json analogJson;
analogJson["type"] = "AI";
analogJson["device"] = std::to_string(analogId);
analogJson["data"] = {{">voltage", voltage}};
analogJson["data"] = wpi::util::json::object(">voltage", voltage);
m_wpilib_update_func(analogJson);
}

View File

@@ -18,7 +18,10 @@
#include "wpi/net/uv/Loop.hpp"
#include "wpi/net/uv/Timer.hpp"
#include "wpi/net/uv/Udp.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibxrp {

View File

@@ -10,7 +10,6 @@
#include <string>
#include "wpi/net/raw_uv_ostream.hpp"
#include "wpi/util/json_fwd.hpp"
#define XRP_TAG_MOTOR 0x12
#define XRP_TAG_SERVO 0x13
@@ -20,6 +19,10 @@
#define XRP_TAG_ACCEL 0x17
#define XRP_TAG_ENCODER 0x18
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpilibxrp {
using WPILibUpdateFunc = std::function<void(const wpi::util::json&)>;