mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
Update for jart/json.cpp change
This commit is contained in:
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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)};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({});
|
||||
|
||||
@@ -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({})));
|
||||
|
||||
@@ -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\","
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "wpi/util/json_fwd.hpp"
|
||||
namespace wpi::util {
|
||||
class json;
|
||||
} // namespace wpi::util
|
||||
|
||||
namespace wpilibws {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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&)>;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user