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

@@ -52,10 +52,10 @@ bool AprilTag::Generate16h5AprilTagImage(wpi::util::RawFrame* frame, int id) {
}
void wpi::apriltag::to_json(wpi::util::json& json, const AprilTag& apriltag) {
json = wpi::util::json{{"ID", apriltag.ID}, {"pose", apriltag.pose}};
json = wpi::util::json::object("ID", apriltag.ID, "pose", apriltag.pose);
}
void wpi::apriltag::from_json(const wpi::util::json& json, AprilTag& apriltag) {
apriltag.ID = json.at("ID").get<int>();
apriltag.ID = json.at("ID").get_int();
apriltag.pose = json.at("pose").get<wpi::math::Pose3d>();
}

View File

@@ -22,16 +22,16 @@ AprilTagFieldLayout::AprilTagFieldLayout(std::string_view path) {
throw std::runtime_error(fmt::format("Cannot open file: {}", path));
}
wpi::util::json json =
wpi::util::json::parse(fileBuffer.value()->GetCharBuffer());
auto buf = fileBuffer.value()->GetCharBuffer();
auto json = wpi::util::json::parse_or_throw({buf.data(), buf.size()});
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
for (auto&& jtag : json.at("tags").get_array()) {
auto tag = jtag.get<AprilTag>();
m_apriltags[tag.ID] = tag;
}
m_fieldWidth =
wpi::units::meter_t{json.at("field").at("width").get<double>()};
m_fieldWidth = wpi::units::meter_t{json.at("field").at("width").get_number()};
m_fieldLength =
wpi::units::meter_t{json.at("field").at("length").get<double>()};
wpi::units::meter_t{json.at("field").at("length").get_number()};
}
AprilTagFieldLayout::AprilTagFieldLayout(std::vector<AprilTag> apriltags,
@@ -113,23 +113,24 @@ void wpi::apriltag::to_json(wpi::util::json& json,
tagVector.push_back(pair.second);
}
json = wpi::util::json{{"field",
{{"length", layout.m_fieldLength.value()},
{"width", layout.m_fieldWidth.value()}}},
{"tags", tagVector}};
auto field = wpi::util::json::object("length", layout.m_fieldLength.value(),
"width", layout.m_fieldWidth.value());
json = wpi::util::json::object("field", std::move(field), "tags",
std::move(tagVector));
}
void wpi::apriltag::from_json(const wpi::util::json& json,
AprilTagFieldLayout& layout) {
layout.m_apriltags.clear();
for (const auto& tag : json.at("tags").get<std::vector<AprilTag>>()) {
for (auto&& jtag : json.at("tags").get_array()) {
auto tag = jtag.get<AprilTag>();
layout.m_apriltags[tag.ID] = tag;
}
layout.m_fieldLength =
wpi::units::meter_t{json.at("field").at("length").get<double>()};
wpi::units::meter_t{json.at("field").at("length").get_number()};
layout.m_fieldWidth =
wpi::units::meter_t{json.at("field").at("width").get<double>()};
wpi::units::meter_t{json.at("field").at("width").get_number()};
}
// Use namespace declaration for forward declaration
@@ -174,6 +175,6 @@ AprilTagFieldLayout AprilTagFieldLayout::LoadField(AprilTagField field) {
throw std::invalid_argument("Invalid Field");
}
wpi::util::json json = wpi::util::json::parse(fieldString);
wpi::util::json json = wpi::util::json::parse_or_throw(fieldString);
return json.get<AprilTagFieldLayout>();
}

View File

@@ -7,7 +7,10 @@
#include "wpi/math/geometry/Pose3d.hpp"
#include "wpi/util/RawFrame.hpp"
#include "wpi/util/SymbolExports.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::apriltag {

View File

@@ -14,7 +14,10 @@
#include "wpi/math/geometry/Pose3d.hpp"
#include "wpi/units/length.hpp"
#include "wpi/util/SymbolExports.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::apriltag {
/**

View File

@@ -69,8 +69,8 @@ bool ReadCameraConfig(const wpi::util::json& config) {
// name
try {
c.name = config.at("name").get<std::string>();
} catch (const wpi::util::json::exception& e) {
c.name = config.at("name").get_string();
} catch (const std::logic_error& e) {
wpi::util::print(stderr,
"config error in '{}': could not read camera name: {}\n",
configFile, e.what());
@@ -79,8 +79,8 @@ bool ReadCameraConfig(const wpi::util::json& config) {
// path
try {
c.path = config.at("path").get<std::string>();
} catch (const wpi::util::json::exception& e) {
c.path = config.at("path").get_string();
} catch (const std::logic_error& e) {
wpi::util::print(
stderr, "config error in '{}': camera '{}': could not read path: {}\n",
configFile, c.name, e.what());
@@ -103,17 +103,16 @@ bool ReadConfig() {
}
// parse file
wpi::util::json j;
try {
j = wpi::util::json::parse(fileBuffer.value()->GetCharBuffer());
} catch (const wpi::util::json::parse_error& e) {
wpi::util::print(stderr, "config error in '{}': byte {}: {}\n", configFile,
e.byte, e.what());
auto buf = fileBuffer.value()->GetCharBuffer();
auto j = wpi::util::json::parse({buf.data(), buf.size()});
if (!j) {
wpi::util::print(stderr, "config error in '{}': {}\n", configFile,
j.error());
return false;
}
// top level must be an object
if (!j.is_object()) {
if (!j->is_object()) {
wpi::util::print(stderr, "config error in '{}': must be JSON object\n",
configFile);
return false;
@@ -121,8 +120,8 @@ bool ReadConfig() {
// team number
try {
team = j.at("team").get<unsigned int>();
} catch (const wpi::util::json::exception& e) {
team = j->at("team").get_int();
} catch (const std::logic_error& e) {
wpi::util::print(stderr,
"config error in '{}': could not read team number: {}\n",
configFile, e.what());
@@ -130,9 +129,9 @@ bool ReadConfig() {
}
// ntmode (optional)
if (j.count("ntmode") != 0) {
if (auto ntmode = j->lookup("ntmode")) {
try {
auto str = j.at("ntmode").get<std::string>();
auto str = ntmode->get_string();
if (wpi::util::equals_lower(str, "client")) {
server = false;
} else if (wpi::util::equals_lower(str, "server")) {
@@ -143,7 +142,7 @@ bool ReadConfig() {
"config error in '{}': could not understand ntmode value '{}'\n",
configFile, str);
}
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
wpi::util::print(stderr,
"config error in '{}': could not read ntmode: {}\n",
configFile, e.what());
@@ -152,12 +151,12 @@ bool ReadConfig() {
// cameras
try {
for (auto&& camera : j.at("cameras")) {
for (auto&& camera : j->at("cameras").get_array()) {
if (!ReadCameraConfig(camera)) {
return false;
}
}
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
wpi::util::print(stderr,
"config error in '{}': could not read cameras: {}\n",
configFile, e.what());

View File

@@ -6,6 +6,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "wpi/util/Logger.hpp"
@@ -244,11 +245,11 @@ bool PropertyContainer::SetPropertiesJson(const wpi::util::json& config,
wpi::util::Logger& logger,
std::string_view logName,
CS_Status* status) {
for (auto&& prop : config) {
for (auto&& prop : config.get_array()) {
std::string name;
try {
name = prop.at("name").get<std::string>();
} catch (const wpi::util::json::exception& e) {
name = prop.at("name").get_string();
} catch (const std::logic_error& e) {
WPI_WARNING(logger, "{}: SetConfigJson: could not read property name: {}",
logName, e.what());
continue;
@@ -257,22 +258,22 @@ bool PropertyContainer::SetPropertiesJson(const wpi::util::json& config,
try {
auto& v = prop.at("value");
if (v.is_string()) {
std::string val = v.get<std::string>();
std::string val = v.get_string();
WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to '{}'",
logName, name, val);
SetStringProperty(n, val, status);
} else if (v.is_boolean()) {
bool val = v.get<bool>();
} else if (v.is_bool()) {
bool val = v.get_bool();
WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}",
logName, name, val);
SetProperty(n, val, status);
} else {
int val = v.get<int>();
int val = v.get_int();
WPI_INFO(logger, "{}: SetConfigJson: setting property '{}' to {}",
logName, name, val);
SetProperty(n, val, status);
}
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
WPI_WARNING(logger,
"{}: SetConfigJson: could not read property value: {}",
logName, e.what());
@@ -289,22 +290,22 @@ wpi::util::json PropertyContainer::GetPropertiesJsonObject(CS_Status* status) {
for (int p : EnumerateProperties(propVec, status)) {
wpi::util::json prop;
wpi::util::SmallString<128> strBuf;
prop.emplace("name", GetPropertyName(p, strBuf, status));
prop["name"] = GetPropertyName(p, strBuf, status);
switch (GetPropertyKind(p)) {
case CS_PROP_BOOLEAN:
prop.emplace("value", static_cast<bool>(GetProperty(p, status)));
prop["value"] = static_cast<bool>(GetProperty(p, status));
break;
case CS_PROP_INTEGER:
case CS_PROP_ENUM:
prop.emplace("value", GetProperty(p, status));
prop["value"] = GetProperty(p, status);
break;
case CS_PROP_STRING:
prop.emplace("value", GetStringProperty(p, strBuf, status));
prop["value"] = GetStringProperty(p, strBuf, status);
break;
default:
continue;
}
j.emplace_back(prop);
j.emplace_back(std::move(prop));
}
return j;

View File

@@ -15,13 +15,13 @@
#include "PropertyImpl.hpp"
#include "wpi/cs/cscore_c.h"
#include "wpi/util/StringMap.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
namespace wpi::util {
class Logger;
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi::util
namespace wpi::cs {

View File

@@ -5,6 +5,7 @@
#include "SinkImpl.hpp"
#include <string>
#include <utility>
#include "Instance.hpp"
#include "Notifier.hpp"
@@ -128,20 +129,18 @@ std::string_view SinkImpl::GetError(
}
bool SinkImpl::SetConfigJson(std::string_view config, CS_Status* status) {
wpi::util::json j;
try {
j = wpi::util::json::parse(config);
} catch (const wpi::util::json::parse_error& e) {
SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what());
auto j = wpi::util::json::parse(config);
if (!j) {
SWARNING("SetConfigJson: parse error: {}", j.error());
*status = CS_PROPERTY_WRITE_FAILED;
return false;
}
return SetConfigJson(j, status);
return SetConfigJson(*j, status);
}
bool SinkImpl::SetConfigJson(const wpi::util::json& config, CS_Status* status) {
if (config.count("properties") != 0) {
SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
if (auto properties = config.lookup("properties")) {
SetPropertiesJson(*properties, m_logger, GetName(), status);
}
return true;
@@ -150,7 +149,7 @@ bool SinkImpl::SetConfigJson(const wpi::util::json& config, CS_Status* status) {
std::string SinkImpl::GetConfigJson(CS_Status* status) {
std::string rv;
wpi::util::raw_string_ostream os(rv);
GetConfigJsonObject(status).dump(os, 4);
GetConfigJsonObject(status).marshal(os, true, 4);
os.flush();
return rv;
}
@@ -160,7 +159,7 @@ wpi::util::json SinkImpl::GetConfigJsonObject(CS_Status* status) {
wpi::util::json props = GetPropertiesJsonObject(status);
if (props.is_array()) {
j.emplace("properties", props);
j["properties"] = std::move(props);
}
return j;

View File

@@ -10,7 +10,10 @@
#include "SourceImpl.hpp"
#include "wpi/util/Logger.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::cs {

View File

@@ -180,15 +180,13 @@ bool SourceImpl::SetFPS(int fps, CS_Status* status) {
}
bool SourceImpl::SetConfigJson(std::string_view config, CS_Status* status) {
wpi::util::json j;
try {
j = wpi::util::json::parse(config);
} catch (const wpi::util::json::parse_error& e) {
SWARNING("SetConfigJson: parse error at byte {}: {}", e.byte, e.what());
auto j = wpi::util::json::parse(config);
if (!j) {
SWARNING("SetConfigJson: parse error: {}", j.error());
*status = CS_PROPERTY_WRITE_FAILED;
return false;
}
return SetConfigJson(j, status);
return SetConfigJson(*j, status);
}
bool SourceImpl::SetConfigJson(const wpi::util::json& config,
@@ -196,9 +194,9 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
VideoMode mode;
// pixel format
if (config.count("pixel format") != 0) {
if (auto pixelFormat = config.lookup("pixel format")) {
try {
auto str = config.at("pixel format").get<std::string>();
auto str = pixelFormat->get_string();
if (wpi::util::equals_lower(str, "mjpeg")) {
mode.pixelFormat = wpi::util::PixelFormat::MJPEG;
} else if (wpi::util::equals_lower(str, "yuyv")) {
@@ -219,34 +217,34 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
SWARNING("SetConfigJson: could not understand pixel format value '{}'",
str);
}
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read pixel format: {}", e.what());
}
}
// width
if (config.count("width") != 0) {
if (auto width = config.lookup("width")) {
try {
mode.width = config.at("width").get<unsigned int>();
} catch (const wpi::util::json::exception& e) {
mode.width = width->get_int();
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read width: {}", e.what());
}
}
// height
if (config.count("height") != 0) {
if (auto height = config.lookup("height")) {
try {
mode.height = config.at("height").get<unsigned int>();
} catch (const wpi::util::json::exception& e) {
mode.height = height->get_int();
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read height: {}", e.what());
}
}
// fps
if (config.count("fps") != 0) {
if (auto fps = config.lookup("fps")) {
try {
mode.fps = config.at("fps").get<unsigned int>();
} catch (const wpi::util::json::exception& e) {
mode.fps = fps->get_int();
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read fps: {}", e.what());
}
}
@@ -277,22 +275,21 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
}
// brightness
if (config.count("brightness") != 0) {
if (auto brightness = config.lookup("brightness")) {
try {
int val = config.at("brightness").get<int>();
int val = brightness->get_int();
SINFO("SetConfigJson: setting brightness to {}", val);
SetBrightness(val, status);
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read brightness: {}", e.what());
}
}
// white balance
if (config.count("white balance") != 0) {
if (auto whiteBalance = config.lookup("white balance")) {
try {
auto& setting = config.at("white balance");
if (setting.is_string()) {
auto str = setting.get<std::string>();
if (whiteBalance->is_string()) {
auto str = whiteBalance->get_string();
if (wpi::util::equals_lower(str, "auto")) {
SINFO("SetConfigJson: setting white balance to {}", "auto");
SetWhiteBalanceAuto(status);
@@ -305,21 +302,20 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
str);
}
} else {
int val = setting.get<int>();
int val = whiteBalance->get_int();
SINFO("SetConfigJson: setting white balance to {}", val);
SetWhiteBalanceManual(val, status);
}
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read white balance: {}", e.what());
}
}
// exposure
if (config.count("exposure") != 0) {
if (auto exposure = config.lookup("exposure")) {
try {
auto& setting = config.at("exposure");
if (setting.is_string()) {
auto str = setting.get<std::string>();
if (exposure->is_string()) {
auto str = exposure->get_string();
if (wpi::util::equals_lower(str, "auto")) {
SINFO("SetConfigJson: setting exposure to {}", "auto");
SetExposureAuto(status);
@@ -331,18 +327,18 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
str);
}
} else {
int val = setting.get<int>();
int val = exposure->get_int();
SINFO("SetConfigJson: setting exposure to {}", val);
SetExposureManual(val, status);
}
} catch (const wpi::util::json::exception& e) {
} catch (const std::logic_error& e) {
SWARNING("SetConfigJson: could not read exposure: {}", e.what());
}
}
// properties
if (config.count("properties") != 0) {
SetPropertiesJson(config.at("properties"), m_logger, GetName(), status);
if (auto properties = config.lookup("properties")) {
SetPropertiesJson(*properties, m_logger, GetName(), status);
}
return true;
@@ -351,8 +347,7 @@ bool SourceImpl::SetConfigJson(const wpi::util::json& config,
std::string SourceImpl::GetConfigJson(CS_Status* status) {
std::string rv;
wpi::util::raw_string_ostream os(rv);
GetConfigJsonObject(status).dump(os, 4);
os.flush();
GetConfigJsonObject(status).marshal(os, true, 4);
return rv;
}
@@ -390,22 +385,22 @@ wpi::util::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
break;
}
if (!pixelFormat.empty()) {
j.emplace("pixel format", pixelFormat);
j["pixel format"] = pixelFormat;
}
// width
if (m_mode.width != 0) {
j.emplace("width", m_mode.width);
j["width"] = m_mode.width;
}
// height
if (m_mode.height != 0) {
j.emplace("height", m_mode.height);
j["height"] = m_mode.height;
}
// fps
if (m_mode.fps != 0) {
j.emplace("fps", m_mode.fps);
j["fps"] = m_mode.fps;
}
// TODO: output brightness, white balance, and exposure?
@@ -413,7 +408,7 @@ wpi::util::json SourceImpl::GetConfigJsonObject(CS_Status* status) {
// properties
wpi::util::json props = GetPropertiesJsonObject(status);
if (props.is_array()) {
j.emplace("properties", props);
j["properties"] = std::move(props);
}
return j;

View File

@@ -20,9 +20,12 @@
#include "wpi/util/PixelFormat.hpp"
#include "wpi/util/RawFrame.h"
#include "wpi/util/condition_variable.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::cs {
class Notifier;

View File

@@ -12,7 +12,10 @@
#include "wpi/cs/VideoProperty.hpp"
#include "wpi/cs/VideoSource.hpp"
#include "wpi/cs/cscore_cpp.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::cs {

View File

@@ -18,7 +18,6 @@
#include "wpi/cs/cscore_c.h"
#include "wpi/util/PixelFormat.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#ifdef _WIN32
// Disable uninitialized variable warnings
@@ -26,6 +25,10 @@
#pragma warning(disable : 26495)
#endif
namespace wpi::util {
class json;
} // namespace wpi::util
/** CameraServer (cscore) namespace */
namespace wpi::cs {

View File

@@ -67,63 +67,55 @@ static bool JsonToWindow(const wpi::util::json& jfile, const char* filename) {
std::string iniStr;
wpi::util::raw_string_ostream ini{iniStr};
for (auto&& jsection : jfile.items()) {
if (jsection.key() == "Docking") {
for (auto&& [sect_key, sect_value] : jfile.get_object()) {
if (sect_key == "Docking") {
continue;
}
if (!jsection.value().is_object()) {
ImGui::LogText("%s section %s is not object", filename,
jsection.key().c_str());
if (!sect_value.is_object()) {
ImGui::LogText("%s section %s is not object", filename, sect_key.c_str());
return false;
}
for (auto&& jsubsection : jsection.value().items()) {
if (!jsubsection.value().is_object()) {
for (auto&& [sub_key, sub_value] : sect_value.get_object()) {
if (!sub_value.is_object()) {
ImGui::LogText("%s section %s subsection %s is not object", filename,
jsection.key().c_str(), jsubsection.key().c_str());
sect_key.c_str(), sub_key.c_str());
return false;
}
ini << '[' << jsection.key() << "][" << jsubsection.key() << "]\n";
for (auto&& jkv : jsubsection.value().items()) {
try {
auto& value = jkv.value().get_ref<const std::string&>();
ini << jkv.key() << '=' << value << "\n";
} catch (wpi::util::json::exception&) {
ini << '[' << sect_key << "][" << sub_key << "]\n";
for (auto&& [key, value] : sub_value.get_object()) {
if (!value.is_string()) {
ImGui::LogText("%s section %s subsection %s value %s is not string",
filename, jsection.key().c_str(),
jsubsection.key().c_str(), jkv.key().c_str());
filename, sect_key.c_str(), sub_key.c_str(),
key.c_str());
return false;
}
ini << key << '=' << value.get_string() << "\n";
}
ini << '\n';
}
}
// emit Docking section last
auto docking = jfile.find("Docking");
if (docking != jfile.end()) {
for (auto&& jsubsection : docking->items()) {
if (!jsubsection.value().is_array()) {
if (auto docking = jfile.lookup("Docking")) {
for (auto&& [sub_key, sub_value] : docking->get_object()) {
if (!sub_value.is_array()) {
ImGui::LogText("%s section %s subsection %s is not array", filename,
"Docking", jsubsection.key().c_str());
"Docking", sub_key.c_str());
return false;
}
ini << "[Docking][" << jsubsection.key() << "]\n";
for (auto&& jv : jsubsection.value()) {
try {
auto& value = jv.get_ref<const std::string&>();
ini << value << "\n";
} catch (wpi::util::json::exception&) {
ini << "[Docking][" << sub_key << "]\n";
for (auto&& value : sub_value.get_array()) {
if (!value.is_string()) {
ImGui::LogText("%s section %s subsection %s value is not string",
filename, "Docking", jsubsection.key().c_str());
filename, "Docking", sub_key.c_str());
return false;
}
ini << value.get_string() << "\n";
}
ini << '\n';
}
}
ini.flush();
ImGui::LoadIniSettingsFromMemory(iniStr.data(), iniStr.size());
return true;
}
@@ -135,12 +127,12 @@ static bool LoadWindowStorageImpl(const std::string& filename) {
fileBuffer.error().message().c_str());
return false;
}
try {
return JsonToWindow(
wpi::util::json::parse(fileBuffer.value()->GetCharBuffer()),
filename.c_str());
} catch (wpi::util::json::parse_error& e) {
ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
auto buffer = fileBuffer.value()->GetCharBuffer();
auto j = wpi::util::json::parse({buffer.data(), buffer.size()});
if (j) {
return JsonToWindow(*j, filename.c_str());
} else {
ImGui::LogText("Error loading %s: %s", filename.c_str(), j.error());
return false;
}
}
@@ -154,12 +146,12 @@ static bool LoadStorageRootImpl(Context* ctx, const std::string& filename,
return false;
}
auto [it, createdStorage] = ctx->storageRoots.try_emplace(rootName);
try {
it->second.FromJson(
wpi::util::json::parse(fileBuffer.value()->GetCharBuffer()),
filename.c_str());
} catch (wpi::util::json::parse_error& e) {
ImGui::LogText("Error loading %s: %s", filename.c_str(), e.what());
auto buffer = fileBuffer.value()->GetCharBuffer();
auto j = wpi::util::json::parse({buffer.data(), buffer.size()});
if (j) {
it->second.FromJson(*j, filename.c_str());
} else {
ImGui::LogText("Error loading %s: %s", filename.c_str(), j.error());
if (createdStorage) {
ctx->storageRoots.erase(it);
}
@@ -257,7 +249,7 @@ bool SaveWindowStorageImpl(const std::string& filename) {
ec.message().c_str());
return false;
}
WindowToJson().dump(os, 2);
WindowToJson().marshal(os, true, 2);
os << '\n';
return true;
}
@@ -271,7 +263,7 @@ static bool SaveStorageRootImpl(Context* ctx, const std::string& filename,
ec.message().c_str());
return false;
}
storage.ToJson().dump(os, 2);
storage.ToJson().marshal(os, true, 2);
os << '\n';
return true;
}

View File

@@ -348,7 +348,7 @@ void Storage::EraseChildren() {
static bool JsonArrayToStorage(Storage::Value* valuePtr,
const wpi::util::json& jarr,
const char* filename) {
auto& arr = jarr.get_ref<const wpi::util::json::array_t&>();
auto& arr = jarr.get_array();
if (arr.empty()) {
ImGui::LogText("empty array in %s, ignoring", filename);
return false;
@@ -356,42 +356,45 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr,
// guess array type from first element
switch (arr[0].type()) {
case wpi::util::json::value_t::boolean:
case wpi::util::json::Type::Bool:
if (valuePtr->type != Storage::Value::kBoolArray) {
valuePtr->Reset(Storage::Value::kBoolArray);
valuePtr->boolArray = new std::vector<int>();
valuePtr->boolArrayDefault = nullptr;
}
break;
case wpi::util::json::value_t::number_float:
case wpi::util::json::Type::Float:
case wpi::util::json::Type::Double:
if (valuePtr->type != Storage::Value::kDoubleArray) {
valuePtr->Reset(Storage::Value::kDoubleArray);
valuePtr->doubleArray = new std::vector<double>();
valuePtr->doubleArrayDefault = nullptr;
}
break;
case wpi::util::json::value_t::number_integer:
case wpi::util::json::value_t::number_unsigned:
case wpi::util::json::Type::Int:
if (valuePtr->type != Storage::Value::kInt64Array) {
valuePtr->Reset(Storage::Value::kInt64Array);
valuePtr->int64Array = new std::vector<int64_t>();
valuePtr->int64ArrayDefault = nullptr;
}
break;
case wpi::util::json::value_t::string:
case wpi::util::json::Type::Uint:
ImGui::LogText("too large of integer in %s, ignoring", filename);
return false;
case wpi::util::json::Type::String:
if (valuePtr->type != Storage::Value::kStringArray) {
valuePtr->Reset(Storage::Value::kStringArray);
valuePtr->stringArray = new std::vector<std::string>();
valuePtr->stringArrayDefault = nullptr;
}
break;
case wpi::util::json::value_t::object:
case wpi::util::json::Type::Object:
if (valuePtr->type != Storage::Value::kChildArray) {
valuePtr->Reset(Storage::Value::kChildArray);
valuePtr->childArray = new std::vector<std::unique_ptr<Storage>>();
}
break;
case wpi::util::json::value_t::array:
case wpi::util::json::Type::Array:
ImGui::LogText("nested array in %s, ignoring", filename);
return false;
default:
@@ -402,47 +405,47 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr,
// loop over array to store elements
for (auto jvalue : arr) {
switch (jvalue.type()) {
case wpi::util::json::value_t::boolean:
case wpi::util::json::Type::Bool:
if (valuePtr->type == Storage::Value::kBoolArray) {
valuePtr->boolArray->push_back(jvalue.get<bool>());
valuePtr->boolArray->push_back(jvalue.get_bool());
} else {
goto error;
}
break;
case wpi::util::json::value_t::number_float:
case wpi::util::json::Type::Float:
if (valuePtr->type == Storage::Value::kDoubleArray) {
valuePtr->doubleArray->push_back(jvalue.get<double>());
valuePtr->doubleArray->push_back(jvalue.get_float());
} else {
goto error;
}
break;
case wpi::util::json::value_t::number_integer:
case wpi::util::json::Type::Double:
if (valuePtr->type == Storage::Value::kDoubleArray) {
valuePtr->doubleArray->push_back(jvalue.get_double());
} else {
goto error;
}
break;
case wpi::util::json::Type::Int:
if (valuePtr->type == Storage::Value::kInt64Array) {
valuePtr->int64Array->push_back(jvalue.get<int64_t>());
valuePtr->int64Array->push_back(jvalue.get_int());
} else if (valuePtr->type == Storage::Value::kDoubleArray) {
valuePtr->doubleArray->push_back(jvalue.get<int64_t>());
valuePtr->doubleArray->push_back(jvalue.get_int());
} else {
goto error;
}
break;
case wpi::util::json::value_t::number_unsigned:
if (valuePtr->type == Storage::Value::kInt64Array) {
valuePtr->int64Array->push_back(jvalue.get<uint64_t>());
} else if (valuePtr->type == Storage::Value::kDoubleArray) {
valuePtr->doubleArray->push_back(jvalue.get<uint64_t>());
} else {
goto error;
}
break;
case wpi::util::json::value_t::string:
case wpi::util::json::Type::Uint:
ImGui::LogText("too large of integer in %s, ignoring", filename);
return false;
case wpi::util::json::Type::String:
if (valuePtr->type == Storage::Value::kStringArray) {
valuePtr->stringArray->emplace_back(
jvalue.get_ref<const std::string&>());
valuePtr->stringArray->emplace_back(jvalue.get_string());
} else {
goto error;
}
break;
case wpi::util::json::value_t::object:
case wpi::util::json::Type::Object:
if (valuePtr->type == Storage::Value::kChildArray) {
valuePtr->childArray->emplace_back(std::make_unique<Storage>());
valuePtr->childArray->back()->FromJson(jvalue, filename);
@@ -450,7 +453,7 @@ static bool JsonArrayToStorage(Storage::Value* valuePtr,
goto error;
}
break;
case wpi::util::json::value_t::array:
case wpi::util::json::Type::Array:
ImGui::LogText("nested array in %s, ignoring", filename);
return false;
default:
@@ -474,53 +477,52 @@ bool Storage::FromJson(const wpi::util::json& json, const char* filename) {
ImGui::LogText("non-object in %s", filename);
return false;
}
for (auto&& jkv : json.items()) {
auto& valuePtr = m_values[jkv.key()];
for (auto&& [key, jvalue] : json.get_object()) {
auto& valuePtr = m_values[key];
bool created = false;
if (!valuePtr) {
valuePtr = std::make_unique<Value>();
created = true;
}
auto& jvalue = jkv.value();
switch (jvalue.type()) {
case wpi::util::json::value_t::boolean:
case wpi::util::json::Type::Bool:
valuePtr->Reset(Value::kBool);
valuePtr->boolVal = jvalue.get<bool>();
valuePtr->boolVal = jvalue.get_bool();
break;
case wpi::util::json::value_t::number_float:
case wpi::util::json::Type::Float:
valuePtr->Reset(Value::kDouble);
valuePtr->doubleVal = jvalue.get<double>();
valuePtr->doubleVal = jvalue.get_float();
break;
case wpi::util::json::value_t::number_integer:
case wpi::util::json::Type::Double:
valuePtr->Reset(Value::kDouble);
valuePtr->doubleVal = jvalue.get_double();
break;
case wpi::util::json::Type::Int:
valuePtr->Reset(Value::kInt64);
valuePtr->int64Val = jvalue.get<int64_t>();
valuePtr->int64Val = jvalue.get_int();
break;
case wpi::util::json::value_t::number_unsigned:
valuePtr->Reset(Value::kInt64);
valuePtr->int64Val = jvalue.get<uint64_t>();
break;
case wpi::util::json::value_t::string:
case wpi::util::json::Type::String:
valuePtr->Reset(Value::kString);
valuePtr->stringVal = jvalue.get_ref<const std::string&>();
valuePtr->stringVal = jvalue.get_string();
break;
case wpi::util::json::value_t::object:
case wpi::util::json::Type::Object:
if (valuePtr->type != Value::kChild) {
valuePtr->Reset(Value::kChild);
valuePtr->child = new Storage;
}
valuePtr->child->FromJson(jvalue, filename); // recurse
break;
case wpi::util::json::value_t::array:
case wpi::util::json::Type::Array:
if (!JsonArrayToStorage(valuePtr.get(), jvalue, filename)) {
if (created) {
m_values.erase(jkv.key());
m_values.erase(key);
}
}
break;
default:
ImGui::LogText("null value in %s, ignoring", filename);
if (created) {
m_values.erase(jkv.key());
m_values.erase(key);
}
break;
}
@@ -545,8 +547,9 @@ wpi::util::json StorageToJsonArray<std::unique_ptr<Storage>>(
jarr.emplace_back(v->ToJson());
}
// remove any trailing empty items
while (!jarr.empty() && jarr.back().empty()) {
jarr.get_ref<wpi::util::json::array_t&>().pop_back();
auto& jarrArr = jarr.get_array();
while (!jarrArr.empty() && jarrArr.back().empty()) {
jarrArr.pop_back();
}
return jarr;
}
@@ -602,7 +605,7 @@ wpi::util::json Storage::ToJson() const {
default:
continue;
}
j.emplace(kv.first, std::move(jelem));
j[kv.first] = std::move(jelem);
}
return j;
}

View File

@@ -451,16 +451,14 @@ void FieldInfo::LoadImage() {
bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
// parse file
wpi::util::json j;
try {
j = wpi::util::json::parse(is);
} catch (const wpi::util::json::parse_error& e) {
wpi::util::print(stderr, "GUI: JSON: could not parse: {}\n", e.what());
auto j = wpi::util::json::parse({is.data(), is.size()});
if (!j) {
wpi::util::print(stderr, "GUI: JSON: could not parse: {}\n", j.error());
return false;
}
// top level must be an object
if (!j.is_object()) {
if (!j->is_object()) {
std::fputs("GUI: JSON: does not contain a top object\n", stderr);
return false;
}
@@ -468,8 +466,8 @@ bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
// image filename
std::string image;
try {
image = j.at("field-image").get<std::string>();
} catch (const wpi::util::json::exception& e) {
image = j->at("field-image").get_string();
} catch (const std::logic_error& e) {
wpi::util::print(stderr, "GUI: JSON: could not read field-image: {}\n",
e.what());
return false;
@@ -478,11 +476,11 @@ bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
// corners
int top, left, bottom, right;
try {
top = j.at("field-corners").at("top-left").at(1).get<int>();
left = j.at("field-corners").at("top-left").at(0).get<int>();
bottom = j.at("field-corners").at("bottom-right").at(1).get<int>();
right = j.at("field-corners").at("bottom-right").at(0).get<int>();
} catch (const wpi::util::json::exception& e) {
top = j->at("field-corners").at("top-left").at(1).get_int();
left = j->at("field-corners").at("top-left").at(0).get_int();
bottom = j->at("field-corners").at("bottom-right").at(1).get_int();
right = j->at("field-corners").at("bottom-right").at(0).get_int();
} catch (const std::logic_error& e) {
wpi::util::print(stderr, "GUI: JSON: could not read field-corners: {}\n",
e.what());
return false;
@@ -492,9 +490,9 @@ bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
float width;
float height;
try {
width = j.at("field-size").at(0).get<float>();
height = j.at("field-size").at(1).get<float>();
} catch (const wpi::util::json::exception& e) {
width = j->at("field-size").at(0).get_float();
height = j->at("field-size").at(1).get_float();
} catch (const std::logic_error& e) {
wpi::util::print(stderr, "GUI: JSON: could not read field-size: {}\n",
e.what());
return false;
@@ -503,8 +501,8 @@ bool FieldInfo::LoadJson(std::span<const char> is, std::string_view filename) {
// units for size
std::string unit;
try {
unit = j.at("field-unit").get<std::string>();
} catch (const wpi::util::json::exception& e) {
unit = j->at("field-unit").get_string();
} catch (const std::logic_error& e) {
wpi::util::print(stderr, "GUI: JSON: could not read field-unit: {}\n",
e.what());
return false;

View File

@@ -16,7 +16,10 @@
#include "wpi/util/StringMap.hpp"
#include "wpi/util/iterator_range.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::glass {

View File

@@ -24,7 +24,7 @@ NTStringChooserModel::NTStringChooserModel(wpi::nt::NetworkTableInstance inst,
.Subscribe("")},
m_selectedPub{m_inst.GetStringTopic(fmt::format("{}/selected", path))
.PublishEx(wpi::nt::StringTopic::TYPE_STRING,
{{"retained", true}})},
wpi::util::json::object("retained", true))},
m_active{
m_inst.GetStringTopic(fmt::format("{}/active", path)).Subscribe("")},
m_options{m_inst.GetStringArrayTopic(fmt::format("{}/options", path))

View File

@@ -8,7 +8,6 @@
#include <cinttypes>
#include <cstring>
#include <functional>
#include <initializer_list>
#include <map>
#include <memory>
#include <span>
@@ -110,18 +109,16 @@ void NetworkTablesModel::Entry::UpdateInfo(wpi::nt::TopicInfo&& info_) {
properties = info.GetProperties();
persistent = false;
auto it = properties.find("persistent");
if (it != properties.end()) {
if (auto v = it->get_ptr<const bool*>()) {
persistent = *v;
if (auto prop = properties.lookup("persistent")) {
if (prop->is_bool()) {
persistent = prop->get_bool();
}
}
retained = false;
it = properties.find("retained");
if (it != properties.end()) {
if (auto v = it->get_ptr<const bool*>()) {
retained = *v;
if (auto prop = properties.lookup("retained")) {
if (prop->is_bool()) {
retained = prop->get_bool();
}
}
}
@@ -642,7 +639,7 @@ static void UpdateJsonValueSource(NetworkTablesModel& model,
const wpi::util::json& j,
std::string_view name, int64_t time) {
switch (j.type()) {
case wpi::util::json::value_t::object: {
case wpi::util::json::Type::Object: {
if (!out->valueChildrenMap) {
out->valueChildren.clear();
out->valueChildrenMap = true;
@@ -652,19 +649,19 @@ static void UpdateJsonValueSource(NetworkTablesModel& model,
elems[out->valueChildren[i].name] = i;
}
bool added = false;
for (auto&& kv : j.items()) {
auto it = elems.find(kv.key());
for (auto&& [key, value] : j.get_object()) {
auto it = elems.find(key);
if (it != elems.end()) {
auto& child = out->valueChildren[it->second];
UpdateJsonValueSource(model, &child, kv.value(), child.path, time);
UpdateJsonValueSource(model, &child, value, child.path, time);
elems.erase(it);
} else {
added = true;
out->valueChildren.emplace_back();
auto& child = out->valueChildren.back();
child.name = kv.key();
child.name = key;
child.path = fmt::format("{}/{}", name, child.name);
UpdateJsonValueSource(model, &child, kv.value(), child.path, time);
UpdateJsonValueSource(model, &child, value, child.path, time);
}
}
// erase unmatched keys
@@ -680,12 +677,13 @@ static void UpdateJsonValueSource(NetworkTablesModel& model,
}
break;
}
case wpi::util::json::value_t::array: {
case wpi::util::json::Type::Array: {
auto& arr = j.get_array();
if (out->valueChildrenMap) {
out->valueChildren.clear();
out->valueChildrenMap = false;
}
out->valueChildren.resize(j.size());
out->valueChildren.resize(arr.size());
unsigned int i = 0;
for (auto&& child : out->valueChildren) {
if (child.name.empty()) {
@@ -693,29 +691,24 @@ static void UpdateJsonValueSource(NetworkTablesModel& model,
child.path = fmt::format("{}{}", name, child.name);
}
// recurse
UpdateJsonValueSource(model, &child, j[i++], child.path, time);
UpdateJsonValueSource(model, &child, arr[i++], child.path, time);
}
break;
}
case wpi::util::json::value_t::string:
out->value =
wpi::nt::Value::MakeString(j.get_ref<const std::string&>(), time);
case wpi::util::json::Type::String:
out->value = wpi::nt::Value::MakeString(j.get_string(), time);
out->UpdateFromValue(model, name, "");
break;
case wpi::util::json::value_t::boolean:
out->value = wpi::nt::Value::MakeBoolean(j.get<bool>(), time);
case wpi::util::json::Type::Bool:
out->value = wpi::nt::Value::MakeBoolean(j.get_bool(), time);
out->UpdateFromValue(model, name, "");
break;
case wpi::util::json::value_t::number_integer:
out->value = wpi::nt::Value::MakeInteger(j.get<int64_t>(), time);
case wpi::util::json::Type::Int:
out->value = wpi::nt::Value::MakeInteger(j.get_int(), time);
out->UpdateFromValue(model, name, "");
break;
case wpi::util::json::value_t::number_unsigned:
out->value = wpi::nt::Value::MakeInteger(j.get<uint64_t>(), time);
out->UpdateFromValue(model, name, "");
break;
case wpi::util::json::value_t::number_float:
out->value = wpi::nt::Value::MakeDouble(j.get<double>(), time);
case wpi::util::json::Type::Float:
out->value = wpi::nt::Value::MakeDouble(j.get_double(), time);
out->UpdateFromValue(model, name, "");
break;
default:
@@ -877,12 +870,8 @@ void NetworkTablesModel::ValueSource::UpdateFromValue(
}
case NT_STRING:
if (typeStr == "json") {
try {
UpdateJsonValueSource(model, this,
wpi::util::json::parse(value.GetString()), name,
value.last_change());
} catch (wpi::util::json::exception&) {
// ignore
if (auto j = wpi::util::json::parse(value.GetString())) {
UpdateJsonValueSource(model, this, *j, name, value.last_change());
}
} else {
valueChildren.clear();

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -13,8 +13,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -22,6 +20,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -15,8 +15,6 @@
#include <string_view>
#include <vector>
#include "wpi/util/json_fwd.hpp"
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
@@ -24,6 +22,7 @@
namespace wpi::util {
template <typename T>
class SmallVectorImpl;
class json;
} // namespace wpi
namespace wpi::nt {

View File

@@ -19,18 +19,17 @@ using namespace wpi::nt;
static std::string ConnInfoToJson(bool connected, const ConnectionInfo& info) {
std::string str;
wpi::util::raw_string_ostream os{str};
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"connected\":" << (connected ? "true" : "false");
os << ",\"remote_id\":\"";
s.dump_escaped(info.remote_id, false);
os << "\",\"remote_ip\":\"";
s.dump_escaped(info.remote_ip, false);
os << "\",\"remote_port\":";
s.dump_integer(static_cast<uint64_t>(info.remote_port));
os << "{\"connected\":";
wpi::util::json::stringify_bool(os, connected);
os << ",\"remote_id\":";
wpi::util::json::stringify_string(os, info.remote_id);
os << ",\"remote_ip\":";
wpi::util::json::stringify_string(os, info.remote_ip);
os << ",\"remote_port\":";
wpi::util::json::stringify_int(os, info.remote_port);
os << ",\"protocol_version\":";
s.dump_integer(static_cast<uint64_t>(info.protocol_version));
wpi::util::json::stringify_int(os, info.protocol_version);
os << "}";
os.flush();
return str;
}

View File

@@ -735,7 +735,7 @@ Java_org_wpilib_networktables_NetworkTablesJNI_getTopicProperty
(JNIEnv* env, jclass, jint topic, jstring name)
{
return MakeJString(
env, wpi::nt::GetTopicProperty(topic, JStringRef{env, name}).dump());
env, wpi::nt::GetTopicProperty(topic, JStringRef{env, name}).to_string());
}
/*
@@ -747,15 +747,13 @@ JNIEXPORT void JNICALL
Java_org_wpilib_networktables_NetworkTablesJNI_setTopicProperty
(JNIEnv* env, jclass, jint topic, jstring name, jstring value)
{
wpi::util::json j;
try {
j = wpi::util::json::parse(std::string_view{JStringRef{env, value}});
} catch (wpi::util::json::parse_error& err) {
auto j = wpi::util::json::parse(std::string_view{JStringRef{env, value}});
if (!j) {
illegalArgEx.Throw(
env, fmt::format("could not parse value JSON: {}", err.what()));
env, fmt::format("could not parse value JSON: {}", j.error()));
return;
}
wpi::nt::SetTopicProperty(topic, JStringRef{env, name}, j);
wpi::nt::SetTopicProperty(topic, JStringRef{env, name}, *j);
}
/*
@@ -779,7 +777,7 @@ JNIEXPORT jstring JNICALL
Java_org_wpilib_networktables_NetworkTablesJNI_getTopicProperties
(JNIEnv* env, jclass, jint topic)
{
return MakeJString(env, wpi::nt::GetTopicProperties(topic).dump());
return MakeJString(env, wpi::nt::GetTopicProperties(topic).to_string());
}
/*
@@ -791,19 +789,18 @@ JNIEXPORT void JNICALL
Java_org_wpilib_networktables_NetworkTablesJNI_setTopicProperties
(JNIEnv* env, jclass, jint topic, jstring properties)
{
wpi::util::json j;
try {
j = wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
} catch (wpi::util::json::parse_error& err) {
auto j =
wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
if (!j) {
illegalArgEx.Throw(
env, fmt::format("could not parse properties JSON: {}", err.what()));
env, fmt::format("could not parse properties JSON: {}", j.error()));
return;
}
if (!j.is_object()) {
if (!j->is_object()) {
illegalArgEx.Throw(env, "properties is not a JSON object");
return;
}
wpi::nt::SetTopicProperties(topic, j);
wpi::nt::SetTopicProperties(topic, *j);
}
/*
@@ -856,20 +853,19 @@ Java_org_wpilib_networktables_NetworkTablesJNI_publishEx
(JNIEnv* env, jclass, jint topic, jint type, jstring typeStr,
jstring properties, jobject options)
{
wpi::util::json j;
try {
j = wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
} catch (wpi::util::json::parse_error& err) {
auto j =
wpi::util::json::parse(std::string_view{JStringRef{env, properties}});
if (!j) {
illegalArgEx.Throw(
env, fmt::format("could not parse properties JSON: {}", err.what()));
env, fmt::format("could not parse properties JSON: {}", j.error()));
return 0;
}
if (!j.is_object()) {
if (!j->is_object()) {
illegalArgEx.Throw(env, "properties is not a JSON object");
return 0;
}
return wpi::nt::PublishEx(topic, static_cast<NT_Type>(type),
JStringRef{env, typeStr}, j,
JStringRef{env, typeStr}, *j,
FromJavaPubSubOptions(env, options));
}

View File

@@ -40,7 +40,7 @@ void StorageImpl::NetworkAnnounce(LocalTopic* topic, std::string_view typeStr,
const wpi::util::json& properties,
std::optional<int> pubuid) {
DEBUG4("LS NetworkAnnounce({}, {}, {}, {})", topic->name, typeStr,
properties.dump(), pubuid.value_or(-1));
properties.to_string(), pubuid.value_or(-1));
if (pubuid.has_value()) {
return; // ack of our publish; ignore
}
@@ -751,9 +751,10 @@ void StorageImpl::AddSchema(std::string_view name, std::string_view type,
return;
}
pubHandle = AddLocalPublisher(topic, {{"retained", true}},
PubSubConfig{NT_RAW, type, {}})
->handle;
pubHandle =
AddLocalPublisher(topic, wpi::util::json::object("retained", true),
PubSubConfig{NT_RAW, type, {}})
->handle;
SetDefaultEntryValue(pubHandle, Value::MakeRaw(schema));
}
@@ -870,8 +871,8 @@ void StorageImpl::PropertiesUpdated(LocalTopic* topic,
const wpi::util::json& update,
unsigned int eventFlags, bool sendNetwork,
bool updateFlags) {
DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name, update.dump(),
eventFlags, sendNetwork, updateFlags);
DEBUG4("PropertiesUpdated({}, {}, {}, {}, {})", topic->name,
update.to_string(), eventFlags, sendNetwork, updateFlags);
topic->RefreshProperties(updateFlags);
unsigned int flags = topic->GetFlags();
if (updateFlags && (flags & NT_UNCACHED) != 0 &&

View File

@@ -137,11 +137,11 @@ bool LocalTopic::SetProperties(const wpi::util::json& update) {
if (!update.is_object()) {
return false;
}
for (auto&& change : update.items()) {
if (change.value().is_null()) {
properties.erase(change.key());
for (auto&& [key, value] : update.get_object()) {
if (value.is_null()) {
properties.erase(key);
} else {
properties[change.key()] = change.value();
properties[key] = value;
}
}
return true;
@@ -151,22 +151,25 @@ void LocalTopic::RefreshProperties(bool updateFlags) {
if (updateFlags) {
RefreshFlags();
}
m_propertiesStr = properties.dump();
m_propertiesStr = properties.to_string();
}
wpi::util::json LocalTopic::CompareProperties(const wpi::util::json props) {
wpi::util::json LocalTopic::CompareProperties(const wpi::util::json& props) {
wpi::util::json update = wpi::util::json::object();
// added/changed
for (auto&& prop : props.items()) {
auto it = properties.find(prop.key());
if (it == properties.end() || *it != prop.value()) {
update[prop.key()] = prop.value();
if (!props.is_object()) {
return update;
}
for (auto&& [key, value] : props.get_object()) {
auto it = properties.lookup(key);
if (!it || *it != value) {
update[key] = value;
}
}
// removed
for (auto&& prop : properties.items()) {
if (props.find(prop.key()) == props.end()) {
update[prop.key()] = wpi::util::json();
for (auto&& [key, value] : properties.get_object()) {
if (!props.contains(key)) {
update[key] = wpi::util::json();
}
}
return update;
@@ -187,30 +190,27 @@ void LocalTopic::ResetIfDoesNotExist() {
}
void LocalTopic::RefreshFlags() {
auto it = properties.find("persistent");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
if (auto persistent = properties.lookup("persistent")) {
if (persistent->is_bool()) {
if (persistent->get_bool()) {
m_flags |= NT_PERSISTENT;
} else {
m_flags &= ~NT_PERSISTENT;
}
}
}
it = properties.find("retained");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
if (auto retained = properties.lookup("retained")) {
if (retained->is_bool()) {
if (retained->get_bool()) {
m_flags |= NT_RETAINED;
} else {
m_flags &= ~NT_RETAINED;
}
}
}
it = properties.find("cached");
if (it != properties.end()) {
if (auto val = it->get_ptr<bool*>()) {
if (*val) {
if (auto cached = properties.lookup("cached")) {
if (cached->is_bool()) {
if (cached->get_bool()) {
m_flags &= ~NT_UNCACHED;
} else {
m_flags |= NT_UNCACHED;

View File

@@ -62,7 +62,7 @@ struct LocalTopic {
void RefreshProperties(bool updateFlags);
// returns update json
wpi::util::json CompareProperties(const wpi::util::json props);
wpi::util::json CompareProperties(const wpi::util::json& props);
TopicInfo GetTopicInfo() const {
TopicInfo info;

View File

@@ -240,7 +240,7 @@ void ClientImpl::ServerUnannounce(std::string_view name, int id) {
void ClientImpl::ServerPropertiesUpdate(std::string_view name,
const wpi::util::json& update,
bool ack) {
DEBUG4("ServerProperties({}, {}, {})", name, update.dump(), ack);
DEBUG4("ServerProperties({}, {}, {})", name, update.to_string(), ack);
assert(m_local);
m_local->ServerPropertiesUpdate(name, update, ack);
}

View File

@@ -9,7 +9,9 @@
#include <string>
#include <string_view>
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
class PubSubOptionsImpl;

View File

@@ -20,6 +20,8 @@
#include "wpi/nt/NetworkTableValue.hpp"
#include "wpi/nt/ntcore_c.h"
#include "wpi/util/DenseMap.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/raw_ostream.hpp"
namespace wpi::nt::net {

View File

@@ -24,12 +24,8 @@ using namespace wpi::nt::net;
using namespace mpack;
static bool GetNumber(wpi::util::json& val, double* num) {
if (auto v = val.get_ptr<const int64_t*>()) {
*num = *v;
} else if (auto v = val.get_ptr<const uint64_t*>()) {
*num = *v;
} else if (auto v = val.get_ptr<const double*>()) {
*num = *v;
if (val.is_number()) {
*num = val.get_number();
} else {
return false;
}
@@ -37,67 +33,64 @@ static bool GetNumber(wpi::util::json& val, double* num) {
}
static bool GetNumber(wpi::util::json& val, int64_t* num) {
if (auto v = val.get_ptr<const int64_t*>()) {
*num = *v;
} else if (auto v = val.get_ptr<const uint64_t*>()) {
*num = *v;
if (val.is_int()) {
*num = val.get_int();
} else {
return false;
}
return true;
}
static std::string* ObjGetString(wpi::util::json::object_t& obj,
std::string_view key, std::string* error) {
auto it = obj.find(key);
if (it == obj.end()) {
static std::string* ObjGetString(wpi::util::json& obj, std::string_view key,
std::string* error) {
auto val = obj.lookup(key);
if (!val) {
*error = fmt::format("no {} key", key);
return nullptr;
}
auto val = it->second.get_ptr<std::string*>();
if (!val) {
if (!val->is_string()) {
*error = fmt::format("{} must be a string", key);
return nullptr;
}
return val;
return &val->get_string();
}
static bool ObjGetNumber(wpi::util::json::object_t& obj, std::string_view key,
static bool ObjGetNumber(wpi::util::json& obj, std::string_view key,
std::string* error, int64_t* num) {
auto it = obj.find(key);
if (it == obj.end()) {
auto val = obj.lookup(key);
if (!val) {
*error = fmt::format("no {} key", key);
return false;
}
if (!GetNumber(it->second, num)) {
if (!GetNumber(*val, num)) {
*error = fmt::format("{} must be a number", key);
return false;
}
return true;
}
static bool ObjGetStringArray(wpi::util::json::object_t& obj,
std::string_view key, std::string* error,
static bool ObjGetStringArray(wpi::util::json& obj, std::string_view key,
std::string* error,
std::vector<std::string>* out) {
// prefixes
auto it = obj.find(key);
if (it == obj.end()) {
auto val = obj.lookup(key);
if (!val) {
*error = fmt::format("no {} key", key);
return false;
}
auto jarr = it->second.get_ptr<wpi::util::json::array_t*>();
if (!jarr) {
if (!val->is_array()) {
*error = fmt::format("{} must be an array", key);
return false;
}
auto& arr = val->get_array();
out->resize(0);
out->reserve(jarr->size());
for (auto&& jval : *jarr) {
auto str = jval.get_ptr<std::string*>();
if (!str) {
out->reserve(arr.size());
for (auto&& jval : arr) {
if (!jval.is_string()) {
*error = fmt::format("{}/{} must be a string", key, out->size());
return false;
}
out->emplace_back(std::move(*str));
out->emplace_back(jval.get_string());
}
return true;
}
@@ -113,43 +106,39 @@ template <typename T>
std::same_as<T, ServerMessageHandler>)
static bool WireDecodeTextImpl(std::string_view in, T& out,
wpi::util::Logger& logger) {
wpi::util::json j;
try {
j = wpi::util::json::parse(in);
} catch (wpi::util::json::parse_error& err) {
WPI_WARNING(logger, "could not decode JSON message: {}", err.what());
auto j = wpi::util::json::parse(in);
if (!j) {
WPI_WARNING(logger, "could not decode JSON message: {}", j.error());
return false;
}
if (!j.is_array()) {
if (!j->is_array()) {
WPI_WARNING(logger, "expected JSON array at top level");
return false;
}
bool rv = false;
int i = -1;
for (auto&& jmsg : j) {
for (auto&& jmsg : j->get_array()) {
++i;
std::string error;
{
auto obj = jmsg.get_ptr<wpi::util::json::object_t*>();
if (!obj) {
if (!jmsg.is_object()) {
error = "expected message to be an object";
goto err;
}
auto method = ObjGetString(*obj, "method", &error);
auto method = ObjGetString(jmsg, "method", &error);
if (!method) {
goto err;
}
auto paramsIt = obj->find("params");
if (paramsIt == obj->end()) {
auto params = jmsg.lookup("params");
if (!params) {
error = "no params key";
goto err;
}
auto params = paramsIt->second.get_ptr<wpi::util::json::object_t*>();
if (!params) {
if (!params->is_object()) {
error = "params must be an object";
goto err;
}
@@ -182,14 +171,10 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// properties; allow missing (treated as empty)
wpi::util::json* properties = nullptr;
auto propertiesIt = params->find("properties");
if (propertiesIt != params->end()) {
properties = &propertiesIt->second;
if (!properties->is_object()) {
error = "properties must be an object";
goto err;
}
auto properties = params->lookup("properties");
if (properties && !properties->is_object()) {
error = "properties must be an object";
goto err;
}
wpi::util::json propertiesEmpty;
if (!properties) {
@@ -225,12 +210,11 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// update
auto updateIt = params->find("update");
if (updateIt == params->end()) {
auto update = params->lookup("update");
if (!update) {
error = "no update key";
goto err;
}
auto update = &updateIt->second;
if (!update->is_object()) {
error = "update must be an object";
goto err;
@@ -254,20 +238,16 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
// options
PubSubOptionsImpl options;
auto optionsIt = params->find("options");
if (optionsIt != params->end()) {
auto joptions =
optionsIt->second.get_ptr<wpi::util::json::object_t*>();
if (!joptions) {
if (auto joptions = params->lookup("options")) {
if (!joptions->is_object()) {
error = "options must be an object";
goto err;
}
// periodic
auto periodicIt = joptions->find("periodic");
if (periodicIt != joptions->end()) {
if (auto periodic = joptions->lookup("periodic")) {
double val;
if (!GetNumber(periodicIt->second, &val)) {
if (!GetNumber(*periodic, &val)) {
error = "periodic value must be a number";
goto err;
}
@@ -276,36 +256,30 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// send all changes
auto sendAllIt = joptions->find("all");
if (sendAllIt != joptions->end()) {
auto sendAll = sendAllIt->second.get_ptr<bool*>();
if (!sendAll) {
if (auto sendAll = joptions->lookup("all")) {
if (!sendAll->is_bool()) {
error = "all value must be a boolean";
goto err;
}
options.sendAll = *sendAll;
options.sendAll = sendAll->get_bool();
}
// topics only
auto topicsOnlyIt = joptions->find("topicsonly");
if (topicsOnlyIt != joptions->end()) {
auto topicsOnly = topicsOnlyIt->second.get_ptr<bool*>();
if (!topicsOnly) {
if (auto topicsOnly = joptions->lookup("topicsonly")) {
if (!topicsOnly->is_bool()) {
error = "topicsonly value must be a boolean";
goto err;
}
options.topicsOnly = *topicsOnly;
options.topicsOnly = topicsOnly->get_bool();
}
// prefix match
auto prefixMatchIt = joptions->find("prefix");
if (prefixMatchIt != joptions->end()) {
auto prefixMatch = prefixMatchIt->second.get_ptr<bool*>();
if (!prefixMatch) {
if (auto prefixMatch = joptions->lookup("prefix")) {
if (!prefixMatch->is_bool()) {
error = "prefix value must be a boolean";
goto err;
}
options.prefixMatch = *prefixMatch;
options.prefixMatch = prefixMatch->get_bool();
}
}
@@ -368,10 +342,9 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
// pubuid
std::optional<int64_t> pubuid;
auto pubuidIt = params->find("pubuid");
if (pubuidIt != params->end()) {
if (auto jpubuid = params->lookup("pubuid")) {
int64_t val;
if (!GetNumber(pubuidIt->second, &val)) {
if (!GetNumber(*jpubuid, &val)) {
error = "pubuid value must be a number";
goto err;
}
@@ -387,12 +360,11 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// properties
auto propertiesIt = params->find("properties");
if (propertiesIt == params->end()) {
auto properties = params->lookup("properties");
if (!properties) {
error = "no properties key";
goto err;
}
auto properties = &propertiesIt->second;
if (!properties->is_object()) {
WPI_WARNING(logger, "{}: properties is not an object", *name);
*properties = wpi::util::json::object();
@@ -430,26 +402,23 @@ static bool WireDecodeTextImpl(std::string_view in, T& out,
}
// update
auto updateIt = params->find("update");
if (updateIt == params->end()) {
auto update = params->lookup("update");
if (!update) {
error = "no update key";
goto err;
}
auto update = &updateIt->second;
if (!update->is_object()) {
error = "update must be an object";
goto err;
}
bool ack = false;
auto ackIt = params->find("ack");
if (ackIt != params->end()) {
auto val = ackIt->second.get_ptr<bool*>();
if (!val) {
if (auto jack = params->lookup("ack")) {
if (!jack->is_bool()) {
error = "ack must be a boolean";
goto err;
}
ack = *val;
ack = jack->get_bool();
}
// complete

View File

@@ -22,43 +22,39 @@ void wpi::nt::net::WireEncodePublish(wpi::util::raw_ostream& os, int pubuid,
std::string_view name,
std::string_view typeStr,
const wpi::util::json& properties) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << PublishMsg::kMethodStr << "\",\"params\":{";
os << "\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"properties\":";
s.dump(properties, false, false, 0, 0);
os << "\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"properties\":";
properties.marshal(os);
os << ",\"pubuid\":";
s.dump_integer(pubuid);
os << ",\"type\":\"";
s.dump_escaped(typeStr, false);
os << "\"}}";
wpi::util::json::stringify_int(os, pubuid);
os << ",\"type\":";
wpi::util::json::stringify_string(os, typeStr);
os << "}}";
}
void wpi::nt::net::WireEncodeUnpublish(wpi::util::raw_ostream& os, int pubuid) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << UnpublishMsg::kMethodStr << "\",\"params\":{";
os << "\"pubuid\":";
s.dump_integer(pubuid);
wpi::util::json::stringify_int(os, pubuid);
os << "}}";
}
void wpi::nt::net::WireEncodeSetProperties(wpi::util::raw_ostream& os,
std::string_view name,
const wpi::util::json& update) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << SetPropertiesMsg::kMethodStr << "\",\"params\":{";
os << "\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"update\":";
s.dump(update, false, false, 0, 0);
os << "\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"update\":";
update.marshal(os);
os << "}}";
}
template <typename T>
static void EncodePrefixes(wpi::util::raw_ostream& os,
std::span<const T> topicNames,
wpi::util::json::serializer& s) {
std::span<const T> topicNames) {
os << '[';
bool first = true;
for (auto&& name : topicNames) {
@@ -67,9 +63,7 @@ static void EncodePrefixes(wpi::util::raw_ostream& os,
} else {
os << ',';
}
os << '"';
s.dump_escaped(name, false);
os << '"';
wpi::util::json::stringify_string(os, name);
}
os << ']';
}
@@ -78,7 +72,6 @@ template <typename T>
static void WireEncodeSubscribeImpl(wpi::util::raw_ostream& os, int subuid,
std::span<const T> topicNames,
const PubSubOptionsImpl& options) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << SubscribeMsg::kMethodStr << "\",\"params\":{";
os << "\"options\":{";
bool first = true;
@@ -105,12 +98,12 @@ static void WireEncodeSubscribeImpl(wpi::util::raw_ostream& os, int subuid,
os << ',';
}
os << "\"periodic\":";
s.dump_float(options.periodicMs / 1000.0);
wpi::util::json::stringify_float(os, options.periodicMs / 1000.0);
}
os << "},\"topics\":";
EncodePrefixes(os, topicNames, s);
EncodePrefixes(os, topicNames);
os << ",\"subuid\":";
s.dump_integer(subuid);
wpi::util::json::stringify_int(os, subuid);
os << "}}";
}
@@ -129,10 +122,9 @@ void wpi::nt::net::WireEncodeSubscribe(wpi::util::raw_ostream& os, int subuid,
void wpi::nt::net::WireEncodeUnsubscribe(wpi::util::raw_ostream& os,
int subuid) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << UnsubscribeMsg::kMethodStr << "\",\"params\":{";
os << "\"subuid\":";
s.dump_integer(subuid);
wpi::util::json::stringify_int(os, subuid);
os << "}}";
}
@@ -159,45 +151,42 @@ void wpi::nt::net::WireEncodeAnnounce(wpi::util::raw_ostream& os,
std::string_view typeStr,
const wpi::util::json& properties,
std::optional<int> pubuid) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << AnnounceMsg::kMethodStr << "\",\"params\":{";
os << "\"id\":";
s.dump_integer(id);
os << ",\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"properties\":";
s.dump(properties, false, false, 0, 0);
wpi::util::json::stringify_int(os, id);
os << ",\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"properties\":";
properties.marshal(os);
if (pubuid) {
os << ",\"pubuid\":";
s.dump_integer(*pubuid);
wpi::util::json::stringify_int(os, *pubuid);
}
os << ",\"type\":\"";
s.dump_escaped(typeStr, false);
os << "\"}}";
os << ",\"type\":";
wpi::util::json::stringify_string(os, typeStr);
os << "}}";
}
void wpi::nt::net::WireEncodeUnannounce(wpi::util::raw_ostream& os,
std::string_view name, int64_t id) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << UnannounceMsg::kMethodStr << "\",\"params\":{";
os << "\"id\":";
s.dump_integer(id);
os << ",\"name\":\"";
s.dump_escaped(name, false);
os << "\"}}";
wpi::util::json::stringify_int(os, id);
os << ",\"name\":";
wpi::util::json::stringify_string(os, name);
os << "}}";
}
void wpi::nt::net::WireEncodePropertiesUpdate(wpi::util::raw_ostream& os,
std::string_view name,
const wpi::util::json& update,
bool ack) {
wpi::util::json::serializer s{os, ' ', 0};
os << "{\"method\":\"" << PropertiesUpdateMsg::kMethodStr
<< "\",\"params\":{";
os << "\"name\":\"";
s.dump_escaped(name, false);
os << "\",\"update\":";
s.dump(update, false, false, 0, 0);
os << "\"name\":";
wpi::util::json::stringify_string(os, name);
os << ",\"update\":";
update.marshal(os);
if (ack) {
os << ",\"ack\":true";
}

View File

@@ -9,9 +9,8 @@
#include <string>
#include <string_view>
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
class raw_ostream;
} // namespace wpi::util

View File

@@ -10,6 +10,7 @@
#include <cstdlib>
#include <cstring>
#include <string_view>
#include <utility>
#include <vector>
#include "Value_internal.hpp"
@@ -313,18 +314,16 @@ void NT_GetTopicProperty(NT_Topic topic, const struct WPI_String* name,
struct WPI_String* prop) {
wpi::util::json j =
wpi::nt::GetTopicProperty(topic, wpi::util::to_string_view(name));
wpi::nt::ConvertToC(j.dump(), prop);
wpi::nt::ConvertToC(j.to_string(), prop);
}
NT_Bool NT_SetTopicProperty(NT_Topic topic, const struct WPI_String* name,
const struct WPI_String* value) {
wpi::util::json j;
try {
j = wpi::util::json::parse(wpi::util::to_string_view(value));
} catch (wpi::util::json::parse_error&) {
auto j = wpi::util::json::parse(wpi::util::to_string_view(value));
if (!j) {
return false;
}
wpi::nt::SetTopicProperty(topic, wpi::util::to_string_view(name), j);
wpi::nt::SetTopicProperty(topic, wpi::util::to_string_view(name), *j);
return true;
}
@@ -334,18 +333,16 @@ void NT_DeleteTopicProperty(NT_Topic topic, const struct WPI_String* name) {
void NT_GetTopicProperties(NT_Topic topic, struct WPI_String* property) {
wpi::util::json j = wpi::nt::GetTopicProperties(topic);
wpi::nt::ConvertToC(j.dump(), property);
wpi::nt::ConvertToC(j.to_string(), property);
}
NT_Bool NT_SetTopicProperties(NT_Topic topic,
const struct WPI_String* properties) {
wpi::util::json j;
try {
j = wpi::util::json::parse(wpi::util::to_string_view(properties));
} catch (wpi::util::json::parse_error&) {
auto j = wpi::util::json::parse(wpi::util::to_string_view(properties));
if (!j) {
return false;
}
return wpi::nt::SetTopicProperties(topic, j);
return wpi::nt::SetTopicProperties(topic, *j);
}
NT_Subscriber NT_Subscribe(NT_Topic topic, NT_Type type,
@@ -375,11 +372,11 @@ NT_Publisher NT_PublishEx(NT_Topic topic, NT_Type type,
// gracefully handle empty string
j = wpi::util::json::object();
} else {
try {
j = wpi::util::json::parse(wpi::util::to_string_view(properties));
} catch (wpi::util::json::parse_error&) {
auto ex = wpi::util::json::parse(wpi::util::to_string_view(properties));
if (!ex) {
return {};
}
j = std::move(*ex);
}
return wpi::nt::PublishEx(topic, type, wpi::util::to_string_view(typeStr), j,

View File

@@ -29,11 +29,7 @@ static std::atomic<int64_t> gNowTime;
namespace wpi::nt {
wpi::util::json TopicInfo::GetProperties() const {
try {
return wpi::util::json::parse(properties);
} catch (wpi::util::json::parse_error&) {
return wpi::util::json::object();
}
return wpi::util::json::parse(properties).value_or(wpi::util::json::object());
}
/*

View File

@@ -17,9 +17,9 @@
#include "server/Functions.hpp"
#include "server/ServerPublisher.hpp"
#include "server/ServerSubscriber.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
class Logger;
template <typename T>
class SmallVectorImpl;

View File

@@ -4,6 +4,7 @@
#include "ServerClient4Base.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@@ -38,7 +39,8 @@ void ServerClient4Base::ClientPublish(int pubuid, std::string_view name,
}
// respond with announce with pubuid to client
DEBUG4("client {}: announce {} pubuid {}", m_id, topic->name, pubuid);
DEBUG4("client {}: announce {} pubuid {} properties {}", m_id, topic->name,
pubuid, properties.to_string());
SendAnnounce(topic, pubuid);
}
@@ -68,18 +70,18 @@ void ServerClient4Base::ClientUnpublish(int pubuid) {
void ServerClient4Base::ClientSetProperties(std::string_view name,
const wpi::util::json& update) {
DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.dump());
DEBUG4("ClientSetProperties({}, {}, {})", m_id, name, update.to_string());
ServerTopic* topic = m_storage.GetTopic(name);
if (!topic || !topic->IsPublished()) {
WARN(
"server ignoring SetProperties({}) from client {} on unpublished topic "
"'{}'; publish or set a value first",
update.dump(), m_id, name);
update.to_string(), m_id, name);
return; // nothing to do
}
if (topic->special) {
WARN("server ignoring SetProperties({}) from client {} on meta topic '{}'",
update.dump(), m_id, name);
update.to_string(), m_id, name);
return; // nothing to do
}
m_storage.SetProperties(nullptr, topic, update);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <string>
#include <string_view>

View File

@@ -111,7 +111,8 @@ void ServerImpl::SendAnnounce(ServerTopic* topic, ServerClient* client) {
continue; // don't announce to requesting client again
}
DEBUG4("client {}: announce {}", aClient->GetId(), topic->name);
DEBUG4("client {}: announce {} properties {}", aClient->GetId(),
topic->name, topic->properties.to_string());
aClient->SendAnnounce(topic, std::nullopt);
}
}

View File

@@ -36,6 +36,8 @@ ServerTopic* ServerStorage::CreateTopic(ServerClient* client,
}
}
} else {
DEBUG4("creating topic '{}' with type '{}' and properties {}", name,
typeStr, properties.to_string());
// new topic
unsigned int id = m_topics.emplace_back(
std::make_unique<ServerTopic>(m_logger, name, typeStr, properties));
@@ -58,7 +60,8 @@ ServerTopic* ServerStorage::CreateTopic(ServerClient* client,
}
ServerTopic* ServerStorage::CreateMetaTopic(std::string_view name) {
return CreateTopic(nullptr, name, "msgpack", {{"retained", true}}, true);
return CreateTopic(nullptr, name, "msgpack",
wpi::util::json::object("retained", true), true);
}
void ServerStorage::DeleteTopic(ServerTopic* topic) {
@@ -90,7 +93,7 @@ void ServerStorage::DeleteTopic(ServerTopic* topic) {
void ServerStorage::SetProperties(ServerClient* client, ServerTopic* topic,
const wpi::util::json& update) {
DEBUG4("SetProperties({}, {}, {})", client ? client->GetId() : -1,
topic->name, update.dump());
topic->name, update.to_string());
bool wasPersistent = topic->persistent;
if (topic->SetProperties(update)) {
// update persistentChanged flag
@@ -110,9 +113,10 @@ void ServerStorage::SetFlags(ServerClient* client, ServerTopic* topic,
m_persistentChanged = true;
wpi::util::json update;
if (topic->persistent) {
update = {{"persistent", true}};
update = wpi::util::json::object("persistent", true);
} else {
update = {{"persistent", wpi::util::json::object()}};
update =
wpi::util::json::object("persistent", wpi::util::json::object());
}
PropertiesChanged(client, topic, update);
}
@@ -234,29 +238,22 @@ void ServerStorage::PropertiesChanged(ServerClient* client, ServerTopic* topic,
}
}
static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
wpi::util::json::serializer& s) {
static void DumpValue(wpi::util::raw_ostream& os, const Value& value) {
switch (value.type()) {
case NT_BOOLEAN:
if (value.GetBoolean()) {
os << "true";
} else {
os << "false";
}
wpi::util::json::stringify_bool(os, value.GetBoolean());
break;
case NT_DOUBLE:
s.dump_float(value.GetDouble());
wpi::util::json::stringify_double(os, value.GetDouble());
break;
case NT_FLOAT:
s.dump_float(value.GetFloat());
wpi::util::json::stringify_float(os, value.GetFloat());
break;
case NT_INTEGER:
s.dump_integer(value.GetInteger());
wpi::util::json::stringify_int(os, value.GetInteger());
break;
case NT_STRING:
os << '"';
s.dump_escaped(value.GetString(), false);
os << '"';
wpi::util::json::stringify_string(os, value.GetString());
break;
case NT_RAW:
case NT_RPC:
@@ -273,11 +270,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
if (v) {
os << "true";
} else {
os << "false";
}
wpi::util::json::stringify_bool(os, v);
}
os << ']';
break;
@@ -291,7 +284,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
s.dump_float(v);
wpi::util::json::stringify_double(os, v);
}
os << ']';
break;
@@ -305,7 +298,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
s.dump_float(v);
wpi::util::json::stringify_float(os, v);
}
os << ']';
break;
@@ -319,7 +312,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
s.dump_integer(v);
wpi::util::json::stringify_int(os, v);
}
os << ']';
break;
@@ -333,9 +326,7 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
} else {
os << ", ";
}
os << '"';
s.dump_escaped(v, false);
os << '"';
wpi::util::json::stringify_string(os, v);
}
os << ']';
break;
@@ -347,7 +338,6 @@ static void DumpValue(wpi::util::raw_ostream& os, const Value& value,
}
void ServerStorage::DumpPersistent(wpi::util::raw_ostream& os) {
wpi::util::json::serializer s{os, ' ', 16};
os << "[\n";
bool first = true;
for (const auto& topic : m_topics) {
@@ -359,31 +349,31 @@ void ServerStorage::DumpPersistent(wpi::util::raw_ostream& os) {
} else {
os << ",\n";
}
os << " {\n \"name\": \"";
s.dump_escaped(topic->name, false);
os << "\",\n \"type\": \"";
s.dump_escaped(topic->typeStr, false);
os << "\",\n \"value\": ";
DumpValue(os, topic->lastValue, s);
os << " {\n \"name\": ";
wpi::util::json::stringify_string(os, topic->name);
os << ",\n \"type\": ";
wpi::util::json::stringify_string(os, topic->typeStr);
os << ",\n \"value\": ";
DumpValue(os, topic->lastValue);
os << ",\n \"properties\": ";
s.dump(topic->properties, true, false, 2, 4);
topic->properties.marshal(os, true, 2);
os << "\n }";
}
os << "\n]\n";
}
static std::string* ObjGetString(wpi::util::json::object_t& obj,
std::string_view key, std::string* error) {
auto it = obj.find(key);
if (it == obj.end()) {
static std::string* ObjGetString(wpi::util::json& obj, std::string_view key,
std::string* error) {
auto value = obj.lookup(key);
if (!value) {
*error = fmt::format("no {} key", key);
return nullptr;
}
auto val = it->second.get_ptr<std::string*>();
if (!val) {
if (!value->is_string()) {
*error = fmt::format("{} must be a string", key);
return nullptr;
}
return val;
return &value->get_string();
}
std::string ServerStorage::LoadPersistent(std::string_view in) {
@@ -391,14 +381,12 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
return {};
}
wpi::util::json j;
try {
j = wpi::util::json::parse(in);
} catch (wpi::util::json::parse_error& err) {
return fmt::format("could not decode JSON: {}", err.what());
auto j = wpi::util::json::parse(in);
if (!j) {
return fmt::format("could not decode JSON: {}", j.error());
}
if (!j.is_array()) {
if (!j->is_array()) {
return "expected JSON array at top level";
}
@@ -407,48 +395,46 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
std::string allerrors;
int i = -1;
auto time = wpi::nt::Now();
for (auto&& jitem : j) {
for (auto&& jitem : j->get_array()) {
++i;
std::string error;
{
auto obj = jitem.get_ptr<wpi::util::json::object_t*>();
if (!obj) {
if (!jitem.is_object()) {
error = "expected item to be an object";
goto err;
}
// name
auto name = ObjGetString(*obj, "name", &error);
auto name = ObjGetString(jitem, "name", &error);
if (!name) {
goto err;
}
// type
auto typeStr = ObjGetString(*obj, "type", &error);
auto typeStr = ObjGetString(jitem, "type", &error);
if (!typeStr) {
goto err;
}
// properties
auto propsIt = obj->find("properties");
if (propsIt == obj->end()) {
auto props = jitem.lookup("properties");
if (!props) {
error = "no properties key";
goto err;
}
auto& props = propsIt->second;
if (!props.is_object()) {
if (!props->is_object()) {
error = "properties must be an object";
goto err;
}
// check to make sure persistent property is set
auto persistentIt = props.find("persistent");
if (persistentIt == props.end()) {
auto persistent = props->lookup("persistent");
if (!persistent) {
error = "no persistent property";
goto err;
}
if (auto v = persistentIt->get_ptr<bool*>()) {
if (!*v) {
if (persistent->is_bool()) {
if (!persistent->get_bool()) {
error = "persistent property is false";
goto err;
}
@@ -458,121 +444,112 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
}
// value
auto valueIt = obj->find("value");
if (valueIt == obj->end()) {
auto jvalue = jitem.lookup("value");
if (!jvalue) {
error = "no value key";
goto err;
}
Value value;
if (*typeStr == "boolean") {
if (auto v = valueIt->second.get_ptr<bool*>()) {
value = Value::MakeBoolean(*v, time);
if (jvalue->is_bool()) {
value = Value::MakeBoolean(jvalue->get_bool(), time);
} else {
error = "value type mismatch, expected boolean";
goto err;
}
} else if (*typeStr == "int") {
if (auto v = valueIt->second.get_ptr<int64_t*>()) {
value = Value::MakeInteger(*v, time);
} else if (auto v = valueIt->second.get_ptr<uint64_t*>()) {
value = Value::MakeInteger(*v, time);
if (jvalue->is_int()) {
value = Value::MakeInteger(jvalue->get_int(), time);
} else {
error = "value type mismatch, expected int";
goto err;
}
} else if (*typeStr == "float") {
if (auto v = valueIt->second.get_ptr<double*>()) {
value = Value::MakeFloat(*v, time);
if (jvalue->is_number()) {
value = Value::MakeFloat(jvalue->get_number(), time);
} else {
error = "value type mismatch, expected float";
goto err;
}
} else if (*typeStr == "double") {
if (auto v = valueIt->second.get_ptr<double*>()) {
value = Value::MakeDouble(*v, time);
if (jvalue->is_number()) {
value = Value::MakeDouble(jvalue->get_number(), time);
} else {
error = "value type mismatch, expected double";
goto err;
}
} else if (*typeStr == "string" || *typeStr == "json") {
if (auto v = valueIt->second.get_ptr<std::string*>()) {
value = Value::MakeString(*v, time);
if (jvalue->is_string()) {
value = Value::MakeString(jvalue->get_string(), time);
} else {
error = "value type mismatch, expected string";
goto err;
}
} else if (*typeStr == "boolean[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<int> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<bool*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_bool()) {
elems.push_back(jelem.get_bool());
} else {
error = "value type mismatch, expected boolean";
}
}
value = Value::MakeBooleanArray(elems, time);
} else if (*typeStr == "int[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<int64_t> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<int64_t*>()) {
elems.push_back(*v);
} else if (auto v = jelem.get_ptr<uint64_t*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_int()) {
elems.push_back(jelem.get_int());
} else {
error = "value type mismatch, expected int";
}
}
value = Value::MakeIntegerArray(elems, time);
} else if (*typeStr == "double[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<double> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<double*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_number()) {
elems.push_back(jelem.get_number());
} else {
error = "value type mismatch, expected double";
}
}
value = Value::MakeDoubleArray(elems, time);
} else if (*typeStr == "float[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<float> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<double*>()) {
elems.push_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_number()) {
elems.push_back(jelem.get_number());
} else {
error = "value type mismatch, expected float";
}
}
value = Value::MakeFloatArray(elems, time);
} else if (*typeStr == "string[]") {
auto arr = valueIt->second.get_ptr<wpi::util::json::array_t*>();
if (!arr) {
if (!jvalue->is_array()) {
error = "value type mismatch, expected array";
goto err;
}
std::vector<std::string> elems;
for (auto&& jelem : valueIt->second) {
if (auto v = jelem.get_ptr<std::string*>()) {
elems.emplace_back(*v);
for (auto&& jelem : jvalue->get_array()) {
if (jelem.is_string()) {
elems.push_back(jelem.get_string());
} else {
error = "value type mismatch, expected string";
}
@@ -580,9 +557,9 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
value = Value::MakeStringArray(std::move(elems), time);
} else {
// raw
if (auto v = valueIt->second.get_ptr<std::string*>()) {
if (jvalue->is_string()) {
std::vector<uint8_t> data;
wpi::util::Base64Decode(*v, &data);
wpi::util::Base64Decode(jvalue->get_string(), &data);
value = Value::MakeRaw(std::move(data), time);
} else {
error = "value type mismatch, expected string";
@@ -591,7 +568,7 @@ std::string ServerStorage::LoadPersistent(std::string_view in) {
}
// create persistent topic
auto topic = CreateTopic(nullptr, *name, *typeStr, props);
auto topic = CreateTopic(nullptr, *name, *typeStr, *props);
// set value
SetValue(nullptr, topic, value);

View File

@@ -12,10 +12,10 @@
#include "server/ServerTopic.hpp"
#include "wpi/util/StringMap.hpp"
#include "wpi/util/UidVector.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class Logger;
class json;
class raw_ostream;
} // namespace wpi::util

View File

@@ -13,11 +13,11 @@ bool ServerTopic::SetProperties(const wpi::util::json& update) {
return false;
}
bool updated = false;
for (auto&& elem : update.items()) {
if (elem.value().is_null()) {
properties.erase(elem.key());
for (auto&& [key, value] : update.get_object()) {
if (value.is_null()) {
properties.erase(key);
} else {
properties[elem.key()] = elem.value();
properties[key] = value;
}
updated = true;
}
@@ -32,24 +32,21 @@ void ServerTopic::RefreshProperties() {
retained = false;
cached = true;
auto persistentIt = properties.find("persistent");
if (persistentIt != properties.end()) {
if (auto val = persistentIt->get_ptr<bool*>()) {
persistent = *val;
if (auto prop = properties.lookup("persistent")) {
if (prop->is_bool()) {
persistent = prop->get_bool();
}
}
auto retainedIt = properties.find("retained");
if (retainedIt != properties.end()) {
if (auto val = retainedIt->get_ptr<bool*>()) {
retained = *val;
if (auto prop = properties.lookup("retained")) {
if (prop->is_bool()) {
retained = prop->get_bool();
}
}
auto cachedIt = properties.find("cached");
if (cachedIt != properties.end()) {
if (auto val = cachedIt->get_ptr<bool*>()) {
cached = *val;
if (auto prop = properties.lookup("cached")) {
if (prop->is_bool()) {
cached = prop->get_bool();
}
}

View File

@@ -15,10 +15,13 @@
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
#include "wpi/util/protobuf/Protobuf.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
template <wpi::util::ProtobufSerializable T>

View File

@@ -19,10 +19,13 @@
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/mutex.hpp"
#include "wpi/util/struct/Struct.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
template <typename T, typename... I>

View File

@@ -19,9 +19,12 @@
#include "wpi/nt/Topic.hpp"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/SmallVector.hpp"
#include "wpi/util/json_fwd.hpp"
#include "wpi/util/struct/Struct.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {
template <typename T, typename... I>

View File

@@ -14,7 +14,10 @@
#include "wpi/nt/NetworkTableType.hpp"
#include "wpi/nt/ntcore_c.h"
#include "wpi/nt/ntcore_cpp.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
} // namespace wpi::util
namespace wpi::nt {

View File

@@ -369,7 +369,8 @@ class UnitTopic final : public Topic {
PublisherType Publish(
const PubSubOptions& options = DEFAULT_PUB_SUB_OPTIONS) {
return UnitPublisher<T>{::wpi::nt::PublishEx(
m_handle, NT_DOUBLE, "double", {{"unit", T{}.name()}}, options)};
m_handle, NT_DOUBLE, "double",
wpi::util::json::object("unit", T{}.name()), options)};
}
/**

View File

@@ -19,9 +19,9 @@
#include "wpi/nt/NetworkTableValue.hpp"
#include "wpi/nt/ntcore_c.h"
#include "wpi/nt/ntcore_cpp_types.hpp"
#include "wpi/util/json_fwd.hpp"
namespace wpi::util {
class json;
template <typename T>
class SmallVectorImpl;
} // namespace wpi::util

View File

@@ -144,11 +144,12 @@ TEST_F(LocalStorageTest, PublishNewNoPropsNull) {
}
TEST_F(LocalStorageTest, PublishNew) {
wpi::util::json properties = {{"persistent", true}};
auto properties = wpi::util::json::object("persistent", true);
EXPECT_CALL(network, ClientPublish(_, std::string_view{"foo"},
std::string_view{"boolean"}, properties,
IsDefaultPubSubOptions()));
storage.Publish(fooTopic, NT_BOOLEAN, "boolean", {{"persistent", true}}, {});
storage.Publish(fooTopic, NT_BOOLEAN, "boolean",
wpi::util::json::object("persistent", true), {});
auto info = storage.GetTopicInfo(fooTopic);
EXPECT_EQ(info.topic, fooTopic);
@@ -645,7 +646,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkDefault) {
SetupPubSub(false, false);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated
@@ -665,7 +667,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPub) {
SetupPubSub(true, false);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated
@@ -684,7 +687,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepSub) {
SetupPubSub(false, true);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated
@@ -706,7 +710,8 @@ TEST_F(LocalStorageDuplicatesTest, FromNetworkKeepPubSub) {
SetupPubSub(true, true);
// incoming from the network are treated like a normal local publish
auto topic = storage.ServerAnnounce("foo", 0, "double", {{}}, std::nullopt);
auto topic = storage.ServerAnnounce("foo", 0, "double",
wpi::util::json::object(), std::nullopt);
storage.ServerSetValue(topic, val1);
storage.ServerSetValue(topic, val2);
// verify the timestamp was updated

View File

@@ -342,7 +342,8 @@ TEST_F(StructTest, InnerArrayNonconstexpr) {
TEST_F(StructTest, StructA) {
wpi::nt::StructTopic<ThingA> topic = inst.GetStructTopic<ThingA>("a");
wpi::nt::StructPublisher<ThingA> pub = topic.Publish();
wpi::nt::StructPublisher<ThingA> pub2 = topic.PublishEx({{}});
wpi::nt::StructPublisher<ThingA> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<ThingA> sub = topic.Subscribe({});
wpi::nt::StructEntry<ThingA> entry = topic.GetEntry({});
pub.SetDefault({});
@@ -360,7 +361,8 @@ TEST_F(StructTest, StructArrayA) {
wpi::nt::StructArrayTopic<ThingA> topic =
inst.GetStructArrayTopic<ThingA>("a");
wpi::nt::StructArrayPublisher<ThingA> pub = topic.Publish();
wpi::nt::StructArrayPublisher<ThingA> pub2 = topic.PublishEx({{}});
wpi::nt::StructArrayPublisher<ThingA> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructArraySubscriber<ThingA> sub = topic.Subscribe({});
wpi::nt::StructArrayEntry<ThingA> entry = topic.GetEntry({});
pub.SetDefault({{ThingA{}, ThingA{}}});
@@ -378,7 +380,8 @@ TEST_F(StructTest, StructFixedArrayA) {
wpi::nt::StructTopic<std::array<ThingA, 2>> topic =
inst.GetStructTopic<std::array<ThingA, 2>>("a");
wpi::nt::StructPublisher<std::array<ThingA, 2>> pub = topic.Publish();
wpi::nt::StructPublisher<std::array<ThingA, 2>> pub2 = topic.PublishEx({{}});
wpi::nt::StructPublisher<std::array<ThingA, 2>> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<std::array<ThingA, 2>> sub = topic.Subscribe({});
wpi::nt::StructEntry<std::array<ThingA, 2>> entry = topic.GetEntry({});
std::array<ThingA, 2> arr;
@@ -398,7 +401,8 @@ TEST_F(StructTest, StructB) {
wpi::nt::StructTopic<ThingB, Info1> topic =
inst.GetStructTopic<ThingB, Info1>("b", info);
wpi::nt::StructPublisher<ThingB, Info1> pub = topic.Publish();
wpi::nt::StructPublisher<ThingB, Info1> pub2 = topic.PublishEx({{}});
wpi::nt::StructPublisher<ThingB, Info1> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<ThingB, Info1> sub = topic.Subscribe({});
wpi::nt::StructEntry<ThingB, Info1> entry = topic.GetEntry({});
pub.SetDefault({});
@@ -417,7 +421,8 @@ TEST_F(StructTest, StructArrayB) {
wpi::nt::StructArrayTopic<ThingB, Info1> topic =
inst.GetStructArrayTopic<ThingB, Info1>("b", info);
wpi::nt::StructArrayPublisher<ThingB, Info1> pub = topic.Publish();
wpi::nt::StructArrayPublisher<ThingB, Info1> pub2 = topic.PublishEx({{}});
wpi::nt::StructArrayPublisher<ThingB, Info1> pub2 =
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructArraySubscriber<ThingB, Info1> sub = topic.Subscribe({});
wpi::nt::StructArrayEntry<ThingB, Info1> entry = topic.GetEntry({});
pub.SetDefault({{ThingB{}, ThingB{}}});
@@ -437,7 +442,7 @@ TEST_F(StructTest, StructFixedArrayB) {
inst.GetStructTopic<std::array<ThingB, 2>, Info1>("b", info);
wpi::nt::StructPublisher<std::array<ThingB, 2>, Info1> pub = topic.Publish();
wpi::nt::StructPublisher<std::array<ThingB, 2>, Info1> pub2 =
topic.PublishEx({{}});
topic.PublishEx(wpi::util::json::object());
wpi::nt::StructSubscriber<std::array<ThingB, 2>, Info1> sub =
topic.Subscribe({});
wpi::nt::StructEntry<std::array<ThingB, 2>, Info1> entry = topic.GetEntry({});

View File

@@ -43,33 +43,20 @@ TEST_F(WireDecodeTextClientTest, EmptyArray) {
}
TEST_F(WireDecodeTextClientTest, ErrorEmpty) {
EXPECT_CALL(
logger,
Call(_, _, _,
"could not decode JSON message: [json.exception.parse_error.101] "
"parse error at line 1, column 1: attempting to parse an empty "
"input; check that your input string or stream contains the "
"expected JSON"sv));
EXPECT_CALL(logger,
Call(_, _, _, "could not decode JSON message: absent_value"sv));
net::WireDecodeText("", handler, logger);
}
TEST_F(WireDecodeTextClientTest, ErrorBadJson1) {
EXPECT_CALL(
logger,
Call(_, _, _,
"could not decode JSON message: [json.exception.parse_error.101] "
"parse error at line 1, column 2: syntax error while parsing value "
"- unexpected end of input; expected '[', '{', or a literal"sv));
EXPECT_CALL(logger,
Call(_, _, _, "could not decode JSON message: unexpected_eof"sv));
net::WireDecodeText("[", handler, logger);
}
TEST_F(WireDecodeTextClientTest, ErrorBadJson2) {
EXPECT_CALL(
logger,
Call(_, _, _,
"could not decode JSON message: [json.exception.parse_error.101] "
"parse error at line 1, column 3: syntax error while parsing object "
"key - unexpected end of input; expected string literal"sv));
EXPECT_CALL(logger,
Call(_, _, _, "could not decode JSON message: unexpected_eof"sv));
net::WireDecodeText("[{", handler, logger);
}
@@ -129,7 +116,7 @@ TEST_F(WireDecodeTextClientTest, PublishPropsEmpty) {
}
TEST_F(WireDecodeTextClientTest, PublishProps) {
wpi::util::json props = {{"k", 6}};
auto props = wpi::util::json::object("k", 6);
EXPECT_CALL(handler, ClientPublish(5, std::string_view{"test"},
std::string_view{"double"}, props,
PubSubOptionsEq({})));

View File

@@ -29,7 +29,9 @@ class WireEncoderTextTest : public ::testing::Test {
protected:
std::string out;
wpi::util::raw_string_ostream os{out};
wpi::util::json GetJson() { return wpi::util::json::parse(os.str()); }
wpi::util::json GetJson() {
return wpi::util::json::parse(os.str()).value_or(wpi::util::json::object());
}
};
class WireEncoderBinaryTest : public ::testing::Test {
@@ -47,7 +49,8 @@ TEST_F(WireEncoderTextTest, PublishPropsEmpty) {
}
TEST_F(WireEncoderTextTest, PublishProps) {
net::WireEncodePublish(os, 5, "test", "double", {{"k", 6}});
net::WireEncodePublish(os, 5, "test", "double",
wpi::util::json::object("k", 6));
ASSERT_EQ(os.str(),
"{\"method\":\"publish\",\"params\":{"
"\"name\":\"test\",\"properties\":{\"k\":6},"
@@ -60,7 +63,7 @@ TEST_F(WireEncoderTextTest, Unpublish) {
}
TEST_F(WireEncoderTextTest, SetProperties) {
net::WireEncodeSetProperties(os, "test", {{"k", 6}});
net::WireEncodeSetProperties(os, "test", wpi::util::json::object("k", 6));
ASSERT_EQ(os.str(),
"{\"method\":\"setproperties\",\"params\":{"
"\"name\":\"test\",\"update\":{\"k\":6}}}");
@@ -122,7 +125,8 @@ TEST_F(WireEncoderTextTest, Announce) {
}
TEST_F(WireEncoderTextTest, AnnounceProperties) {
net::WireEncodeAnnounce(os, "test", 5, "double", {{"k", 6}}, std::nullopt);
net::WireEncodeAnnounce(os, "test", 5, "double",
wpi::util::json::object("k", 6), std::nullopt);
ASSERT_EQ(os.str(),
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","
"\"properties\":{\"k\":6},\"type\":\"double\"}}");
@@ -144,7 +148,8 @@ TEST_F(WireEncoderTextTest, Unannounce) {
}
TEST_F(WireEncoderTextTest, MessagePublish) {
net::ClientMessage msg{net::PublishMsg{5, "test", "double", {{"k", 6}}, {}}};
net::ClientMessage msg{net::PublishMsg{
5, "test", "double", wpi::util::json::object("k", 6), {}}};
ASSERT_TRUE(net::WireEncodeText(os, msg));
ASSERT_EQ(os.str(),
"{\"method\":\"publish\",\"params\":{"
@@ -159,7 +164,8 @@ TEST_F(WireEncoderTextTest, MessageUnpublish) {
}
TEST_F(WireEncoderTextTest, MessageSetProperties) {
net::ClientMessage msg{net::SetPropertiesMsg{"test", {{"k", 6}}}};
net::ClientMessage msg{
net::SetPropertiesMsg{"test", wpi::util::json::object("k", 6)}};
ASSERT_TRUE(net::WireEncodeText(os, msg));
ASSERT_EQ(os.str(),
"{\"method\":\"setproperties\",\"params\":{"
@@ -190,8 +196,8 @@ TEST_F(WireEncoderTextTest, MessageAnnounce) {
}
TEST_F(WireEncoderTextTest, MessageAnnounceProperties) {
net::ServerMessage msg{
net::AnnounceMsg{"test", 5, "double", std::nullopt, {{"k", 6}}}};
net::ServerMessage msg{net::AnnounceMsg{"test", 5, "double", std::nullopt,
wpi::util::json::object("k", 6)}};
ASSERT_TRUE(net::WireEncodeText(os, msg));
ASSERT_EQ(os.str(),
"{\"method\":\"announce\",\"params\":{\"id\":5,\"name\":\"test\","

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&)>;

View File

@@ -70,9 +70,6 @@ void Analyzer::UpdateFeedforwardGains() {
} catch (const AnalysisManager::FileReadingError& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());
} catch (const wpi::util::json::exception& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());
} catch (const std::exception& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());
@@ -298,9 +295,6 @@ void Analyzer::PrepareData() {
} catch (const AnalysisManager::FileReadingError& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());
} catch (const wpi::util::json::exception& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());
} catch (const std::exception& e) {
m_state = AnalyzerState::kFileError;
HandleError(e.what());

View File

@@ -3,7 +3,6 @@
// the WPILib BSD license file in the root directory of this project.
#include <filesystem>
#include <fstream>
#include <map>
#include <memory>
#include <optional>
@@ -27,7 +26,10 @@
#include "wpi/gui/wpigui.hpp"
#include "wpi/gui/wpigui_openurl.hpp"
#include "wpi/math/util/MathUtil.hpp"
#include "wpi/util/MemoryBuffer.hpp"
#include "wpi/util/fs.hpp"
#include "wpi/util/json.hpp"
#include "wpi/util/raw_ostream.hpp"
namespace gui = wpi::gui;
@@ -175,14 +177,26 @@ void CameraCalibrationSelectorButton(const char* text,
if (selector && selector->ready(0)) {
auto selectedFiles = selector->result();
if (!selectedFiles.empty()) {
auto fileBuffer = wpi::util::MemoryBuffer::GetFile(selectedFiles[0]);
if (!fileBuffer) {
goto err;
}
auto buffer = fileBuffer.value()->GetCharBuffer();
auto j = wpi::util::json::parse({buffer.data(), buffer.size()});
if (!j) {
goto err;
}
try {
cameraModel = wpi::util::json::parse(std::ifstream(selectedFiles[0]))
.get<wpical::CameraModel>();
cameraModel = j->get<wpical::CameraModel>();
} catch (...) {
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always);
ImGui::OpenPopup("Camera Calibration Loading Error");
goto err;
}
}
goto done;
err:
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always);
ImGui::OpenPopup("Camera Calibration Loading Error");
done:
selector.reset();
}
}
@@ -199,15 +213,29 @@ void FieldSelectorButton(const char* text,
auto selectedFiles = selector->result();
if (!selectedFiles.empty()) {
std::string idealLayoutPath = selectedFiles[0];
auto fileBuffer = wpi::util::MemoryBuffer::GetFile(idealLayoutPath);
if (!fileBuffer) {
gInvalidLayoutPath = idealLayoutPath;
goto err;
}
auto buffer = fileBuffer.value()->GetCharBuffer();
auto j = wpi::util::json::parse({buffer.data(), buffer.size()});
if (!j) {
gInvalidLayoutPath = idealLayoutPath;
goto err;
}
try {
layout = wpi::util::json::parse(std::ifstream(idealLayoutPath))
.get<wpi::apriltag::AprilTagFieldLayout>();
layout = j->get<wpi::apriltag::AprilTagFieldLayout>();
} catch (...) {
gInvalidLayoutPath = idealLayoutPath;
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always);
ImGui::OpenPopup("AprilTag Field Layout Loading Error");
goto err;
}
}
goto done;
err:
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always);
ImGui::OpenPopup("AprilTag Field Layout Loading Error");
done:
selector.reset();
}
if (layout.GetTags().size() > 0) {
@@ -226,11 +254,18 @@ void SaveCalibratedField(const wpi::apriltag::AprilTagFieldLayout& field,
static std::string saveDir;
ProcessDirectorySelector(saveDirSelector, saveDir);
if (!saveDir.empty()) {
std::ofstream out(saveDir + "/" + outputName + ".json");
out << wpi::util::json{field}.dump(4);
std::error_code ec;
wpi::util::raw_fd_ostream out(saveDir + "/" + outputName + ".json", ec,
fs::OF_Text);
if (!ec) {
wpi::util::json{field}.marshal(out, true, 4);
}
std::ofstream fmap(saveDir + "/" + outputName + ".fmap");
fmap << wpi::util::json{fmap::Fieldmap(field)}.dump(4);
wpi::util::raw_fd_ostream fmap(saveDir + "/" + outputName + ".fmap", ec,
fs::OF_Text);
if (!ec) {
wpi::util::json{fmap::Fieldmap(field)}.marshal(fmap, true, 4);
}
saveDir.clear();
}
@@ -286,8 +321,12 @@ void CalibrateCamera() {
std::filesystem::path myPath(cameraVideoPath);
auto outputPath = myPath.parent_path() / "cameracalibration.json";
std::ofstream output_file(outputPath);
output_file << wpi::util::json(gCameraModel).dump(4) << std::endl;
std::error_code ec;
wpi::util::raw_fd_ostream output_file(outputPath.string(), ec,
fs::OF_Text);
if (!ec) {
wpi::util::json{gCameraModel}.marshal(output_file, true, 4);
}
ImGui::CloseCurrentPopup();
calibrating = false;
} else if (!videoProcessor->IsFinished()) {
@@ -350,16 +389,30 @@ void CombineCalibrations() {
if (!selectedFiles.empty()) {
std::map<std::string, wpi::apriltag::AprilTagFieldLayout> fieldLayouts;
for (auto& path : selectedFiles) {
auto fileBuffer = wpi::util::MemoryBuffer::GetFile(path);
if (!fileBuffer) {
gInvalidLayoutPath = path;
goto err;
}
auto buffer = fileBuffer.value()->GetCharBuffer();
auto j = wpi::util::json::parse({buffer.data(), buffer.size()});
if (!j) {
gInvalidLayoutPath = path;
goto err;
}
try {
fieldLayouts.emplace(
path, wpi::util::json::parse(std::ifstream(path))
.get<wpi::apriltag::AprilTagFieldLayout>());
fieldLayouts.emplace(path,
j->get<wpi::apriltag::AprilTagFieldLayout>());
} catch (...) {
gInvalidLayoutPath = path;
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always);
ImGui::OpenPopup("AprilTag Field Layout Loading Error");
goto err;
}
}
goto good;
err:
ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Always);
ImGui::OpenPopup("AprilTag Field Layout Loading Error");
good:
calibratedFieldLayouts = fieldLayouts;
}
calibratedFieldLayoutMultiselector.reset();

View File

@@ -306,47 +306,71 @@ void to_json(wpi::util::json& json, const CameraModel& cameraModel) {
cameraModel.distortionCoefficients.data(),
cameraModel.distortionCoefficients.data() +
cameraModel.distortionCoefficients.size());
json = {{"camera_matrix", cameraMatrix},
{"distortion_coefficients", distortionCoefficients},
{"avg_reprojection_error", cameraModel.avgReprojectionError}};
json = wpi::util::json::object();
json["camera_matrix"] = cameraMatrix;
json["distortion_coefficients"] = distortionCoefficients;
json["avg_reprojection_error"] = cameraModel.avgReprojectionError;
}
void from_json(const wpi::util::json& json, CameraModel& cameraModel) {
bool isCalibdb = json.contains("camera");
Eigen::Matrix3d cameraMatrix;
std::vector<double> distortionCoeffs;
auto mat = json.at("camera_matrix");
auto distortionCoefficients = json.at("distortion_coefficients");
auto jmat = json.at("camera_matrix");
auto jdistortionCoefficients = json.at("distortion_coefficients");
if (isCalibdb) {
// OpenCV format has data key
if (mat.contains("data")) {
auto data = mat.at("data").get<std::vector<double>>();
if (auto jdata = jmat.lookup("data")) {
auto& arr = jdata->get_array();
std::vector<double> data;
data.reserve(arr.size());
for (const auto& item : arr) {
data.push_back(item.get_number());
}
cameraMatrix = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>{data.data()};
} else {
for (int i = 0; i < cameraMatrix.rows(); i++) {
auto& jcols = jmat.at(i);
for (int j = 0; j < cameraMatrix.cols(); j++) {
cameraMatrix(i, j) = mat[i][j];
cameraMatrix(i, j) = jcols.at(j).get_number();
}
}
}
// OpenCV format has data key
if (distortionCoefficients.contains("data")) {
distortionCoeffs =
distortionCoefficients.at("data").get<std::vector<double>>();
wpi::util::json::array_t* arr;
if (auto jdata = jdistortionCoefficients.lookup("data")) {
arr = &jdata->get_array();
} else {
distortionCoeffs = distortionCoefficients.get<std::vector<double>>();
arr = &jdistortionCoefficients.get_array();
}
distortionCoeffs.reserve(arr->size());
for (const auto& item : *arr) {
distortionCoeffs.push_back(item.get_number());
}
} else {
cameraMatrix = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>{
mat.get<std::vector<double>>().data()};
distortionCoeffs = distortionCoefficients.get<std::vector<double>>();
{
auto& arr = jmat.get_array();
std::vector<double> data;
data.reserve(arr.size());
for (const auto& item : arr) {
data.push_back(item.get_number());
}
cameraMatrix = Eigen::Matrix<double, 3, 3, Eigen::RowMajor>{data.data()};
}
{
auto& arr = jdistortionCoefficients.get_array();
distortionCoeffs.reserve(arr.size());
for (const auto& item : arr) {
distortionCoeffs.push_back(item.get_number());
}
}
}
// CalibDB generates JSONs with 5 values. Just zero out the remaining 3 to get
// it to 8
distortionCoeffs.resize(8, 0);
cameraModel = {cameraMatrix,
Eigen::Matrix<double, 8, 1>{distortionCoeffs.data()},
json.at("avg_reprojection_error")};
json.at("avg_reprojection_error").get_number()};
}
} // namespace wpical

View File

@@ -29,25 +29,39 @@ Fieldmap::Fieldmap(const wpi::apriltag::AprilTagFieldLayout& layout)
}
void fmap::to_json(wpi::util::json& json, const Fiducial& layout) {
json = {{"family", layout.family},
{"id", layout.id},
{"size", layout.size},
{"transform", std::vector<double>{layout.transform.data(),
layout.transform.data() +
layout.transform.size()}},
{"unique", layout.unique}};
json = wpi::util::json::object();
json["family"] = layout.family;
json["id"] = layout.id;
json["size"] = layout.size;
json["transform"] =
std::vector<double>{layout.transform.data(),
layout.transform.data() + layout.transform.size()};
json["unique"] = layout.unique;
}
void fmap::from_json(const wpi::util::json& json, Fiducial& layout) {
auto vec = json.at("transform").get<std::vector<double>>();
layout = {json.at("family"), json.at("id"), json.at("size"),
Eigen::Matrix4d{vec.data()}, json.at("unique")};
auto& arr = json.at("transform").get_array();
std::vector<double> vec;
vec.reserve(arr.size());
for (auto& elem : arr) {
vec.push_back(elem.get_number());
}
layout = {json.at("family").get_string(),
static_cast<int>(json.at("id").get_int()),
json.at("size").get_number(), Eigen::Matrix4d{vec.data()},
static_cast<int>(json.at("unique").get_int())};
}
void fmap::to_json(wpi::util::json& json, const Fieldmap& layout) {
json = {{"type", "frc"}, {"fiducials", layout.fiducials}};
json = wpi::util::json::object("type", "frc", "fiducials", layout.fiducials);
}
void fmap::from_json(const wpi::util::json& json, Fieldmap& layout) {
layout = {json.at("type"), json.at("fiducials")};
auto& arr = json.at("fiducials").get_array();
std::vector<Fiducial> fiducials;
fiducials.reserve(arr.size());
for (auto& elem : arr) {
fiducials.emplace_back(elem.get<Fiducial>());
}
layout = {json.at("type").get_string(), fiducials};
}

View File

@@ -2,7 +2,6 @@
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <fstream>
#include <optional>
#include <string>
@@ -13,7 +12,10 @@
#include "path_lookup.hpp"
#include "wpi/apriltag/AprilTagFieldLayout.hpp"
#include "wpi/apriltag/AprilTagFields.hpp"
#include "wpi/util/MemoryBuffer.hpp"
#include "wpi/util/fs.hpp"
#include "wpi/util/json.hpp"
#include "wpi/util/raw_ostream.hpp"
const std::string projectRootPath = PROJECT_ROOT_PATH;
@@ -42,8 +44,10 @@ TEST(CameraCalibrationTest, Typical) {
}
auto ret = calibrator.GetCameraModel();
EXPECT_NE(ret, std::nullopt);
std::ofstream output_file(calSavePath + "/cameracalibration.json");
output_file << wpi::util::json(ret.value()).dump(4) << std::endl;
std::error_code ec;
wpi::util::raw_fd_ostream output_file(calSavePath + "/cameracalibration.json",
ec, fs::OF_Text);
wpi::util::json(ret.value()).marshal(output_file, true, 4);
}
TEST(CameraCalibrationTest, Atypical) {
@@ -58,8 +62,10 @@ TEST(CameraCalibrationTest, Atypical) {
}
TEST(FieldCalibrationTest, Typical) {
auto model = wpi::util::json::parse(
std::ifstream(calSavePath + "/cameracalibration.json"))
auto buffer =
wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json");
auto buf = buffer.value()->GetCharBuffer();
auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()})
.get<wpical::CameraModel>();
auto ret =
wpical::calibrate(LookupPath(projectRootPath + videoLocation), model,
@@ -80,8 +86,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Camera_Model) {
}
TEST(FieldCalibrationTest, Atypical_Bad_Field_Layout) {
auto model = wpi::util::json::parse(
std::ifstream(calSavePath + "/cameracalibration.json"))
auto buffer =
wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json");
auto buf = buffer.value()->GetCharBuffer();
auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()})
.get<wpical::CameraModel>();
auto ret =
wpical::calibrate(LookupPath(projectRootPath + videoLocation), model,
@@ -90,8 +98,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Field_Layout) {
}
TEST(FieldCalibrationTest, Atypical_Bad_Input_Directory) {
auto model = wpi::util::json::parse(
std::ifstream(calSavePath + "/cameracalibration.json"))
auto buffer =
wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json");
auto buf = buffer.value()->GetCharBuffer();
auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()})
.get<wpical::CameraModel>();
auto ret =
wpical::calibrate(LookupPath(projectRootPath), model,
@@ -102,8 +112,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Input_Directory) {
}
TEST(FieldCalibrationTest, Atypical_Bad_Pinned_Tag) {
auto model = wpi::util::json::parse(
std::ifstream(calSavePath + "/cameracalibration.json"))
auto buffer =
wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json");
auto buf = buffer.value()->GetCharBuffer();
auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()})
.get<wpical::CameraModel>();
auto ret =
wpical::calibrate(LookupPath(projectRootPath + videoLocation), model,
@@ -114,8 +126,10 @@ TEST(FieldCalibrationTest, Atypical_Bad_Pinned_Tag) {
}
TEST(FieldCalibrationTest, Atypical_Bad_Pinned_Tag_Negative) {
auto model = wpi::util::json::parse(
std::ifstream(calSavePath + "/cameracalibration.json"))
auto buffer =
wpi::util::MemoryBuffer::GetFile(calSavePath + "/cameracalibration.json");
auto buf = buffer.value()->GetCharBuffer();
auto model = wpi::util::json::parse_or_throw({buf.data(), buf.size()})
.get<wpical::CameraModel>();
auto ret =
wpical::calibrate(LookupPath(projectRootPath + videoLocation), model,

View File

@@ -87,10 +87,8 @@ struct MatchDataSender {
std::shared_ptr<wpi::nt::NetworkTable> table =
wpi::nt::NetworkTableInstance::GetDefault().GetTable("FMSInfo");
MatchDataSenderEntry<wpi::nt::StringTopic> typeMetaData{
table,
".type",
kSmartDashboardType,
{{"SmartDashboard", kSmartDashboardType}}};
table, ".type", kSmartDashboardType,
wpi::util::json::object("SmartDashboard", kSmartDashboardType)};
MatchDataSenderEntry<wpi::nt::StringTopic> gameData{table, "GameData", ""};
MatchDataSenderEntry<wpi::nt::StringTopic> eventName{table, "EventName", ""};
MatchDataSenderEntry<wpi::nt::IntegerTopic> matchNumber{table, "MatchNumber",

View File

@@ -29,7 +29,7 @@ void MechanismLigament2d::UpdateEntries(
std::shared_ptr<wpi::nt::NetworkTable> table) {
m_typePub = table->GetStringTopic(".type").PublishEx(
wpi::nt::StringTopic::TYPE_STRING,
{{"SmartDashboard", kSmartDashboardType}});
wpi::util::json::object("SmartDashboard", kSmartDashboardType));
m_typePub.Set(kSmartDashboardType);
m_colorEntry = table->GetStringTopic("color").GetEntry("");

View File

@@ -85,7 +85,8 @@ void SendableBuilderImpl::ClearProperties() {
void SendableBuilderImpl::SetSmartDashboardType(std::string_view type) {
if (!m_typePublisher) {
m_typePublisher = m_table->GetStringTopic(".type").PublishEx(
wpi::nt::StringTopic::TYPE_STRING, {{"SmartDashboard", type}});
wpi::nt::StringTopic::TYPE_STRING,
wpi::util::json::object("SmartDashboard", type));
}
m_typePublisher.Set(type);
}

View File

@@ -32,7 +32,7 @@ struct Instance {
wpi::nt::StringPublisher typePublisher{
table->GetStringTopic(".type").PublishEx(
wpi::nt::StringTopic::TYPE_STRING,
{{"SmartDashboard", kSmartDashboardType}})};
wpi::util::json::object("SmartDashboard", kSmartDashboardType))};
wpi::nt::MultiSubscriber tableSubscriber{
wpi::nt::NetworkTableInstance::GetDefault(),
{{fmt::format("{}/", table->GetPath())}}};

Some files were not shown because too many files have changed in this diff Show More