mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-07-02 02:51:42 +00:00
Update for jart/json.cpp change
This commit is contained in:
@@ -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\","
|
||||
|
||||
Reference in New Issue
Block a user