mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-16 16:41:33 +10:00
Remove fragile JSON libraries from the firmware while retaining Meshtasticd JSON support (#10152)
This commit is contained in:
@@ -51,7 +51,7 @@ size_t RedirectablePrint::write(uint8_t c)
|
||||
size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
va_list copy;
|
||||
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
|
||||
#if ARCH_PORTDUINO
|
||||
static char printBuf[512];
|
||||
#else
|
||||
static char printBuf[160];
|
||||
|
||||
+2
-10
@@ -22,8 +22,6 @@
|
||||
#if ARCH_PORTDUINO
|
||||
#include "Throttle.h"
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
#if ENABLE_JSON_LOGGING || ARCH_PORTDUINO
|
||||
#include "serialization/MeshPacketSerializer.h"
|
||||
#endif
|
||||
|
||||
@@ -555,9 +553,7 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
} */
|
||||
|
||||
printPacket("decoded message", p);
|
||||
#if ENABLE_JSON_LOGGING
|
||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
#elif ARCH_PORTDUINO
|
||||
#if ARCH_PORTDUINO
|
||||
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerialize(p, false).c_str());
|
||||
} else if (portduino_config.JSONFilename != "") {
|
||||
@@ -852,11 +848,7 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
|
||||
|
||||
void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
||||
{
|
||||
#if ENABLE_JSON_LOGGING
|
||||
// Even ignored packets get logged in the trace
|
||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||
LOG_TRACE("%s", MeshPacketSerializer::JsonSerializeEncrypted(p).c_str());
|
||||
#elif ARCH_PORTDUINO
|
||||
#if ARCH_PORTDUINO
|
||||
// Even ignored packets get logged in the trace
|
||||
if (portduino_config.traceFilename != "" || portduino_config.logoutputlevel == level_trace) {
|
||||
p->rx_time = getValidTime(RTCQualityFromNet); // store the arrival timestamp for the phone
|
||||
|
||||
+253
-174
@@ -11,11 +11,12 @@
|
||||
#endif
|
||||
#include "SPILock.h"
|
||||
#include "power.h"
|
||||
#include "serialization/JSON.h"
|
||||
#include <FSCommon.h>
|
||||
#include <HTTPBodyParser.hpp>
|
||||
#include <HTTPMultipartBodyParser.hpp>
|
||||
#include <HTTPURLEncodedBodyParser.hpp>
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#include "esp_task_wdt.h"
|
||||
@@ -259,40 +260,95 @@ void htmlDeleteDir(const char *dirname)
|
||||
root.close();
|
||||
}
|
||||
|
||||
JSONArray htmlListDir(const char *dirname, uint8_t levels)
|
||||
// Escape a string into a JSON double-quoted literal. Matches the previous
|
||||
// SimpleJSON StringifyString behavior (0x00-0x1F and 0x7F -> \u00xx lowercase,
|
||||
// escapes " \ / \b \f \n \r \t, UTF-8 passes through unchanged).
|
||||
static std::string jsonEscape(const std::string &str)
|
||||
{
|
||||
std::string out = "\"";
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
char chr = str[i];
|
||||
if (chr == '"' || chr == '\\' || chr == '/') {
|
||||
out += '\\';
|
||||
out += chr;
|
||||
} else if (chr == '\b') {
|
||||
out += "\\b";
|
||||
} else if (chr == '\f') {
|
||||
out += "\\f";
|
||||
} else if (chr == '\n') {
|
||||
out += "\\n";
|
||||
} else if (chr == '\r') {
|
||||
out += "\\r";
|
||||
} else if (chr == '\t') {
|
||||
out += "\\t";
|
||||
} else if ((unsigned char)chr < 0x20 || chr == 0x7F) {
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "\\u%04x", (unsigned char)chr);
|
||||
out += buf;
|
||||
} else {
|
||||
out += chr;
|
||||
}
|
||||
}
|
||||
out += "\"";
|
||||
return out;
|
||||
}
|
||||
|
||||
// Format a numeric value the way the previous SimpleJSON serializer did
|
||||
// (std::stringstream with precision 15, NaN/Inf -> "null").
|
||||
static std::string jsonNum(double v)
|
||||
{
|
||||
if (std::isinf(v) || std::isnan(v))
|
||||
return "null";
|
||||
std::ostringstream ss;
|
||||
ss.precision(15);
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Build a serialized JSON array string listing files in `dirname`.
|
||||
// Subdirectories recurse as nested arrays (up to `levels` deep).
|
||||
std::string htmlListDir(const char *dirname, uint8_t levels)
|
||||
{
|
||||
File root = FSCom.open(dirname, FILE_O_READ);
|
||||
JSONArray fileList;
|
||||
std::string out = "[";
|
||||
bool first = true;
|
||||
if (!root) {
|
||||
return fileList;
|
||||
out += "]";
|
||||
return out;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
return fileList;
|
||||
out += "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
// iterate over the file list
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
std::string element;
|
||||
bool haveElement = false;
|
||||
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
|
||||
if (levels) {
|
||||
#ifdef ARCH_ESP32
|
||||
fileList.push_back(new JSONValue(htmlListDir(file.path(), levels - 1)));
|
||||
element = htmlListDir(file.path(), levels - 1);
|
||||
#else
|
||||
fileList.push_back(new JSONValue(htmlListDir(file.name(), levels - 1)));
|
||||
element = htmlListDir(file.name(), levels - 1);
|
||||
#endif
|
||||
haveElement = true;
|
||||
file.close();
|
||||
}
|
||||
} else {
|
||||
JSONObject thisFileMap;
|
||||
thisFileMap["size"] = new JSONValue((int)file.size());
|
||||
#ifdef ARCH_ESP32
|
||||
String fileName = String(file.path()).substring(1);
|
||||
thisFileMap["name"] = new JSONValue(fileName.c_str());
|
||||
#else
|
||||
String fileName = String(file.name()).substring(1);
|
||||
thisFileMap["name"] = new JSONValue(fileName.c_str());
|
||||
#endif
|
||||
String tempName = String(file.name()).substring(1);
|
||||
// Keys in the previous std::map<string,...> were emitted in
|
||||
// alphabetical order: name, nameModified, size.
|
||||
element = "{";
|
||||
element += jsonEscape("name");
|
||||
element += ":";
|
||||
element += jsonEscape(fileName.c_str());
|
||||
if (tempName.endsWith(".gz")) {
|
||||
#ifdef ARCH_ESP32
|
||||
String modifiedFile = String(file.path()).substring(1);
|
||||
@@ -300,15 +356,30 @@ JSONArray htmlListDir(const char *dirname, uint8_t levels)
|
||||
String modifiedFile = String(file.name()).substring(1);
|
||||
#endif
|
||||
modifiedFile.remove((modifiedFile.length() - 3), 3);
|
||||
thisFileMap["nameModified"] = new JSONValue(modifiedFile.c_str());
|
||||
element += ",";
|
||||
element += jsonEscape("nameModified");
|
||||
element += ":";
|
||||
element += jsonEscape(modifiedFile.c_str());
|
||||
}
|
||||
fileList.push_back(new JSONValue(thisFileMap));
|
||||
element += ",";
|
||||
element += jsonEscape("size");
|
||||
element += ":";
|
||||
element += jsonNum((int)file.size());
|
||||
element += "}";
|
||||
haveElement = true;
|
||||
}
|
||||
if (haveElement) {
|
||||
if (!first)
|
||||
out += ",";
|
||||
out += element;
|
||||
first = false;
|
||||
}
|
||||
file.close();
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
return fileList;
|
||||
out += "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
@@ -318,28 +389,25 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
res->setHeader("Access-Control-Allow-Methods", "GET");
|
||||
|
||||
concurrency::LockGuard g(spiLock);
|
||||
auto fileList = htmlListDir("/static", 10);
|
||||
std::string fileList = htmlListDir("/static", 10);
|
||||
|
||||
// create json output structure
|
||||
JSONObject filesystemObj;
|
||||
filesystemObj["total"] = new JSONValue((int)FSCom.totalBytes());
|
||||
filesystemObj["used"] = new JSONValue((int)FSCom.usedBytes());
|
||||
filesystemObj["free"] = new JSONValue(int(FSCom.totalBytes() - FSCom.usedBytes()));
|
||||
uint64_t total = FSCom.totalBytes();
|
||||
uint64_t used = FSCom.usedBytes();
|
||||
|
||||
JSONObject jsonObjInner;
|
||||
jsonObjInner["files"] = new JSONValue(fileList);
|
||||
jsonObjInner["filesystem"] = new JSONValue(filesystemObj);
|
||||
// Key order matches the previous std::map-based emission (alphabetical).
|
||||
std::string out;
|
||||
out.reserve(fileList.size() + 128);
|
||||
out += "{\"data\":{\"files\":";
|
||||
out += fileList;
|
||||
out += ",\"filesystem\":{\"free\":";
|
||||
out += jsonNum((int)(total - used));
|
||||
out += ",\"total\":";
|
||||
out += jsonNum((int)total);
|
||||
out += ",\"used\":";
|
||||
out += jsonNum((int)used);
|
||||
out += "}},\"status\":\"ok\"}";
|
||||
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["data"] = new JSONValue(jsonObjInner);
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
|
||||
delete value;
|
||||
res->print(out.c_str());
|
||||
}
|
||||
|
||||
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
@@ -354,27 +422,13 @@ void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
if (params->getQueryParameter("delete", paramValDelete)) {
|
||||
std::string pathDelete = "/" + paramValDelete;
|
||||
concurrency::LockGuard g(spiLock);
|
||||
if (FSCom.remove(pathDelete.c_str())) {
|
||||
|
||||
LOG_INFO("%s", pathDelete.c_str());
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
return;
|
||||
} else {
|
||||
|
||||
LOG_INFO("%s", pathDelete.c_str());
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["status"] = new JSONValue("Error");
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
return;
|
||||
}
|
||||
const char *status = FSCom.remove(pathDelete.c_str()) ? "ok" : "Error";
|
||||
LOG_INFO("%s", pathDelete.c_str());
|
||||
std::string out = "{\"status\":";
|
||||
out += jsonEscape(status);
|
||||
out += "}";
|
||||
res->print(out.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -615,95 +669,113 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
|
||||
res->println("<pre>");
|
||||
}
|
||||
|
||||
// Helper lambda to create JSON array and clean up memory properly
|
||||
auto createJSONArrayFromLog = [](const uint32_t *logArray, int count) -> JSONValue * {
|
||||
JSONArray tempArray;
|
||||
auto arrayFromLog = [](const uint32_t *logArray, int count) -> std::string {
|
||||
std::string s = "[";
|
||||
for (int i = 0; i < count; i++) {
|
||||
tempArray.push_back(new JSONValue((int)logArray[i]));
|
||||
if (i)
|
||||
s += ",";
|
||||
s += jsonNum((int)logArray[i]);
|
||||
}
|
||||
JSONValue *result = new JSONValue(tempArray);
|
||||
// Note: Don't delete tempArray elements here - JSONValue now owns them
|
||||
return result;
|
||||
s += "]";
|
||||
return s;
|
||||
};
|
||||
|
||||
// data->airtime->tx_log
|
||||
uint32_t *logArray;
|
||||
logArray = airTime->airtimeReport(TX_LOG);
|
||||
JSONValue *txLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
|
||||
|
||||
// data->airtime->rx_log
|
||||
std::string txLog = arrayFromLog(logArray, airTime->getPeriodsToLog());
|
||||
logArray = airTime->airtimeReport(RX_LOG);
|
||||
JSONValue *rxLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
|
||||
|
||||
// data->airtime->rx_all_log
|
||||
std::string rxLog = arrayFromLog(logArray, airTime->getPeriodsToLog());
|
||||
logArray = airTime->airtimeReport(RX_ALL_LOG);
|
||||
JSONValue *rxAllLogJsonValue = createJSONArrayFromLog(logArray, airTime->getPeriodsToLog());
|
||||
std::string rxAllLog = arrayFromLog(logArray, airTime->getPeriodsToLog());
|
||||
|
||||
// data->airtime
|
||||
JSONObject jsonObjAirtime;
|
||||
jsonObjAirtime["tx_log"] = txLogJsonValue;
|
||||
jsonObjAirtime["rx_log"] = rxLogJsonValue;
|
||||
jsonObjAirtime["rx_all_log"] = rxAllLogJsonValue;
|
||||
jsonObjAirtime["channel_utilization"] = new JSONValue(airTime->channelUtilizationPercent());
|
||||
jsonObjAirtime["utilization_tx"] = new JSONValue(airTime->utilizationTXPercent());
|
||||
jsonObjAirtime["seconds_since_boot"] = new JSONValue(int(airTime->getSecondsSinceBoot()));
|
||||
jsonObjAirtime["seconds_per_period"] = new JSONValue(int(airTime->getSecondsPerPeriod()));
|
||||
jsonObjAirtime["periods_to_log"] = new JSONValue(airTime->getPeriodsToLog());
|
||||
|
||||
// data->wifi
|
||||
JSONObject jsonObjWifi;
|
||||
jsonObjWifi["rssi"] = new JSONValue(WiFi.RSSI());
|
||||
String wifiIPString = WiFi.localIP().toString();
|
||||
std::string wifiIP = wifiIPString.c_str();
|
||||
jsonObjWifi["ip"] = new JSONValue(wifiIP.c_str());
|
||||
|
||||
// data->memory
|
||||
JSONObject jsonObjMemory;
|
||||
jsonObjMemory["heap_total"] = new JSONValue((int)memGet.getHeapSize());
|
||||
jsonObjMemory["heap_free"] = new JSONValue((int)memGet.getFreeHeap());
|
||||
jsonObjMemory["psram_total"] = new JSONValue((int)memGet.getPsramSize());
|
||||
jsonObjMemory["psram_free"] = new JSONValue((int)memGet.getFreePsram());
|
||||
spiLock->lock();
|
||||
jsonObjMemory["fs_total"] = new JSONValue((int)FSCom.totalBytes());
|
||||
jsonObjMemory["fs_used"] = new JSONValue((int)FSCom.usedBytes());
|
||||
jsonObjMemory["fs_free"] = new JSONValue(int(FSCom.totalBytes() - FSCom.usedBytes()));
|
||||
uint64_t fsTotal = FSCom.totalBytes();
|
||||
uint64_t fsUsed = FSCom.usedBytes();
|
||||
spiLock->unlock();
|
||||
|
||||
// data->power
|
||||
JSONObject jsonObjPower;
|
||||
jsonObjPower["battery_percent"] = new JSONValue(powerStatus->getBatteryChargePercent());
|
||||
jsonObjPower["battery_voltage_mv"] = new JSONValue(powerStatus->getBatteryVoltageMv());
|
||||
jsonObjPower["has_battery"] = new JSONValue(BoolToString(powerStatus->getHasBattery()));
|
||||
jsonObjPower["has_usb"] = new JSONValue(BoolToString(powerStatus->getHasUSB()));
|
||||
jsonObjPower["is_charging"] = new JSONValue(BoolToString(powerStatus->getIsCharging()));
|
||||
// Emit keys in the same alphabetical order as the previous
|
||||
// std::map-based JSON output to keep responses byte-compatible.
|
||||
std::string out;
|
||||
out.reserve(1024);
|
||||
out += "{\"data\":{";
|
||||
|
||||
// data->device
|
||||
JSONObject jsonObjDevice;
|
||||
jsonObjDevice["reboot_counter"] = new JSONValue((int)myNodeInfo.reboot_count);
|
||||
// airtime
|
||||
out += "\"airtime\":{";
|
||||
out += "\"channel_utilization\":";
|
||||
out += jsonNum(airTime->channelUtilizationPercent());
|
||||
out += ",\"periods_to_log\":";
|
||||
out += jsonNum(airTime->getPeriodsToLog());
|
||||
out += ",\"rx_all_log\":";
|
||||
out += rxAllLog;
|
||||
out += ",\"rx_log\":";
|
||||
out += rxLog;
|
||||
out += ",\"seconds_per_period\":";
|
||||
out += jsonNum((int)airTime->getSecondsPerPeriod());
|
||||
out += ",\"seconds_since_boot\":";
|
||||
out += jsonNum((int)airTime->getSecondsSinceBoot());
|
||||
out += ",\"tx_log\":";
|
||||
out += txLog;
|
||||
out += ",\"utilization_tx\":";
|
||||
out += jsonNum(airTime->utilizationTXPercent());
|
||||
out += "}";
|
||||
|
||||
// data->radio
|
||||
JSONObject jsonObjRadio;
|
||||
jsonObjRadio["frequency"] = new JSONValue(RadioLibInterface::instance->getFreq());
|
||||
jsonObjRadio["lora_channel"] = new JSONValue((int)RadioLibInterface::instance->getChannelNum() + 1);
|
||||
// device
|
||||
out += ",\"device\":{\"reboot_counter\":";
|
||||
out += jsonNum((int)myNodeInfo.reboot_count);
|
||||
out += "}";
|
||||
|
||||
// collect data to inner data object
|
||||
JSONObject jsonObjInner;
|
||||
jsonObjInner["airtime"] = new JSONValue(jsonObjAirtime);
|
||||
jsonObjInner["wifi"] = new JSONValue(jsonObjWifi);
|
||||
jsonObjInner["memory"] = new JSONValue(jsonObjMemory);
|
||||
jsonObjInner["power"] = new JSONValue(jsonObjPower);
|
||||
jsonObjInner["device"] = new JSONValue(jsonObjDevice);
|
||||
jsonObjInner["radio"] = new JSONValue(jsonObjRadio);
|
||||
// memory
|
||||
out += ",\"memory\":{";
|
||||
out += "\"fs_free\":";
|
||||
out += jsonNum((int)(fsTotal - fsUsed));
|
||||
out += ",\"fs_total\":";
|
||||
out += jsonNum((int)fsTotal);
|
||||
out += ",\"fs_used\":";
|
||||
out += jsonNum((int)fsUsed);
|
||||
out += ",\"heap_free\":";
|
||||
out += jsonNum((int)memGet.getFreeHeap());
|
||||
out += ",\"heap_total\":";
|
||||
out += jsonNum((int)memGet.getHeapSize());
|
||||
out += ",\"psram_free\":";
|
||||
out += jsonNum((int)memGet.getFreePsram());
|
||||
out += ",\"psram_total\":";
|
||||
out += jsonNum((int)memGet.getPsramSize());
|
||||
out += "}";
|
||||
|
||||
// create json output structure
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["data"] = new JSONValue(jsonObjInner);
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
// power (has_* / is_charging were serialized as the strings "true"/"false")
|
||||
out += ",\"power\":{";
|
||||
out += "\"battery_percent\":";
|
||||
out += jsonNum(powerStatus->getBatteryChargePercent());
|
||||
out += ",\"battery_voltage_mv\":";
|
||||
out += jsonNum(powerStatus->getBatteryVoltageMv());
|
||||
out += ",\"has_battery\":";
|
||||
out += jsonEscape(BoolToString(powerStatus->getHasBattery()));
|
||||
out += ",\"has_usb\":";
|
||||
out += jsonEscape(BoolToString(powerStatus->getHasUSB()));
|
||||
out += ",\"is_charging\":";
|
||||
out += jsonEscape(BoolToString(powerStatus->getIsCharging()));
|
||||
out += "}";
|
||||
|
||||
// radio
|
||||
out += ",\"radio\":{\"frequency\":";
|
||||
out += jsonNum(RadioLibInterface::instance->getFreq());
|
||||
out += ",\"lora_channel\":";
|
||||
out += jsonNum((int)RadioLibInterface::instance->getChannelNum() + 1);
|
||||
out += "}";
|
||||
|
||||
// wifi
|
||||
out += ",\"wifi\":{\"ip\":";
|
||||
out += jsonEscape(wifiIP);
|
||||
out += ",\"rssi\":";
|
||||
out += jsonNum(WiFi.RSSI());
|
||||
out += "}";
|
||||
|
||||
out += "},\"status\":\"ok\"}";
|
||||
|
||||
res->print(out.c_str());
|
||||
}
|
||||
|
||||
void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
||||
@@ -724,58 +796,66 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
||||
res->println("<pre>");
|
||||
}
|
||||
|
||||
JSONArray nodesArray;
|
||||
std::string out;
|
||||
out.reserve(2048);
|
||||
out += "{\"data\":{\"nodes\":[";
|
||||
|
||||
bool firstNode = true;
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
|
||||
while (tempNodeInfo != NULL) {
|
||||
if (nodeInfoLiteHasUser(tempNodeInfo)) {
|
||||
JSONObject node;
|
||||
|
||||
char id[16];
|
||||
snprintf(id, sizeof(id), "!%08x", tempNodeInfo->num);
|
||||
|
||||
node["id"] = new JSONValue(id);
|
||||
node["snr"] = new JSONValue(tempNodeInfo->snr);
|
||||
node["via_mqtt"] = new JSONValue(BoolToString(nodeInfoLiteViaMqtt(tempNodeInfo)));
|
||||
node["last_heard"] = new JSONValue((int)tempNodeInfo->last_heard);
|
||||
node["position"] = new JSONValue();
|
||||
|
||||
std::string position;
|
||||
if (nodeDB->hasValidPosition(tempNodeInfo)) {
|
||||
meshtastic_PositionLite posLite;
|
||||
if (nodeDB->copyNodePosition(tempNodeInfo->num, posLite)) {
|
||||
JSONObject position;
|
||||
position["latitude"] = new JSONValue((float)posLite.latitude_i * 1e-7);
|
||||
position["longitude"] = new JSONValue((float)posLite.longitude_i * 1e-7);
|
||||
position["altitude"] = new JSONValue((int)posLite.altitude);
|
||||
node["position"] = new JSONValue(position);
|
||||
position = "{\"altitude\":";
|
||||
position += jsonNum((int)posLite.altitude);
|
||||
position += ",\"latitude\":";
|
||||
position += jsonNum((float)posLite.latitude_i * 1e-7);
|
||||
position += ",\"longitude\":";
|
||||
position += jsonNum((float)posLite.longitude_i * 1e-7);
|
||||
position += "}";
|
||||
} else {
|
||||
position = "null";
|
||||
}
|
||||
} else {
|
||||
position = "null";
|
||||
}
|
||||
|
||||
node["long_name"] = new JSONValue(tempNodeInfo->long_name);
|
||||
node["short_name"] = new JSONValue(tempNodeInfo->short_name);
|
||||
// mac_address dropped from NodeInfoLite as part of the slim refactor; emit zeros.
|
||||
node["mac_address"] = new JSONValue("00:00:00:00:00:00");
|
||||
node["hw_model"] = new JSONValue(tempNodeInfo->hw_model);
|
||||
if (!firstNode)
|
||||
out += ",";
|
||||
firstNode = false;
|
||||
|
||||
nodesArray.push_back(new JSONValue(node));
|
||||
// Alphabetical key order matches previous std::map-based output.
|
||||
out += "{\"hw_model\":";
|
||||
out += jsonNum(tempNodeInfo->hw_model);
|
||||
out += ",\"id\":";
|
||||
out += jsonEscape(id);
|
||||
out += ",\"last_heard\":";
|
||||
out += jsonNum((int)tempNodeInfo->last_heard);
|
||||
out += ",\"long_name\":";
|
||||
out += jsonEscape(tempNodeInfo->long_name);
|
||||
out += ",\"mac_address\":";
|
||||
out += jsonEscape("00:00:00:00:00:00");
|
||||
out += ",\"position\":";
|
||||
out += position;
|
||||
out += ",\"short_name\":";
|
||||
out += jsonEscape(tempNodeInfo->short_name);
|
||||
out += ",\"snr\":";
|
||||
out += jsonNum(tempNodeInfo->snr);
|
||||
out += ",\"via_mqtt\":";
|
||||
out += jsonEscape(BoolToString(nodeInfoLiteViaMqtt(tempNodeInfo)));
|
||||
out += "}";
|
||||
}
|
||||
tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
|
||||
}
|
||||
|
||||
// collect data to inner data object
|
||||
JSONObject jsonObjInner;
|
||||
jsonObjInner["nodes"] = new JSONValue(nodesArray);
|
||||
|
||||
// create json output structure
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["data"] = new JSONValue(jsonObjInner);
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
out += "]},\"status\":\"ok\"}";
|
||||
res->print(out.c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -897,20 +977,28 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
|
||||
|
||||
int n = WiFi.scanNetworks();
|
||||
|
||||
// build list of network objects
|
||||
JSONArray networkObjs;
|
||||
std::string out = "{\"data\":[";
|
||||
bool firstNet = true;
|
||||
if (n > 0) {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
char ssidArray[50];
|
||||
// The previous implementation pre-escaped quotes before handing
|
||||
// the value to the JSON serializer; preserve that (byte-compatible
|
||||
// even if it double-encodes a quote) so existing clients are not
|
||||
// affected by this refactor.
|
||||
String ssidString = String(WiFi.SSID(i));
|
||||
ssidString.replace("\"", "\\\"");
|
||||
ssidString.toCharArray(ssidArray, 50);
|
||||
|
||||
if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) {
|
||||
JSONObject thisNetwork;
|
||||
thisNetwork["ssid"] = new JSONValue(ssidArray);
|
||||
thisNetwork["rssi"] = new JSONValue(int(WiFi.RSSI(i)));
|
||||
networkObjs.push_back(new JSONValue(thisNetwork));
|
||||
if (!firstNet)
|
||||
out += ",";
|
||||
firstNet = false;
|
||||
out += "{\"rssi\":";
|
||||
out += jsonNum((int)WiFi.RSSI(i));
|
||||
out += ",\"ssid\":";
|
||||
out += jsonEscape(ssidArray);
|
||||
out += "}";
|
||||
}
|
||||
// Yield some cpu cycles to IP stack.
|
||||
// This is important in case the list is large and it takes us time to return
|
||||
@@ -918,16 +1006,7 @@ void handleScanNetworks(HTTPRequest *req, HTTPResponse *res)
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
// build output structure
|
||||
JSONObject jsonObjOuter;
|
||||
jsonObjOuter["data"] = new JSONValue(networkObjs);
|
||||
jsonObjOuter["status"] = new JSONValue("ok");
|
||||
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObjOuter);
|
||||
std::string jsonString = value->Stringify();
|
||||
res->print(jsonString.c_str());
|
||||
delete value;
|
||||
out += "],\"status\":\"ok\"}";
|
||||
res->print(out.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -23,10 +23,6 @@
|
||||
#include <ETH.h>
|
||||
#endif // HAS_ETHERNET
|
||||
#include "Default.h"
|
||||
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
|
||||
#include "serialization/JSON.h"
|
||||
#include "serialization/MeshPacketSerializer.h"
|
||||
#endif
|
||||
#include <Throttle.h>
|
||||
#include <assert.h>
|
||||
#include <utility>
|
||||
@@ -147,96 +143,6 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
router->enqueueReceivedMessage(p.release());
|
||||
}
|
||||
|
||||
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
|
||||
// returns true if this is a valid JSON envelope which we accept on downlink
|
||||
inline bool isValidJsonEnvelope(JSONObject &json)
|
||||
{
|
||||
// Generate node ID from nodenum for comparison
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
// if "sender" is provided, avoid processing packets we uplinked
|
||||
return (json.find("sender") != json.end() ? (json["sender"]->AsString().compare(nodeId) != 0) : true) &&
|
||||
(json.find("hopLimit") != json.end() ? json["hopLimit"]->IsNumber() : true) && // hop limit should be a number
|
||||
(json.find("from") != json.end()) && json["from"]->IsNumber() &&
|
||||
(json["from"]->AsNumber() == nodeDB->getNodeNum()) && // only accept message if the "from" is us
|
||||
(json.find("type") != json.end()) && json["type"]->IsString() && // should specify a type
|
||||
(json.find("payload") != json.end()); // should have a payload
|
||||
}
|
||||
|
||||
inline void onReceiveJson(byte *payload, size_t length)
|
||||
{
|
||||
char payloadStr[length + 1];
|
||||
memcpy(payloadStr, payload, length);
|
||||
payloadStr[length] = 0; // null terminated string
|
||||
std::unique_ptr<JSONValue> json_value(JSON::Parse(payloadStr));
|
||||
if (json_value == nullptr) {
|
||||
LOG_ERROR("JSON received payload on MQTT but not a valid JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject json;
|
||||
json = json_value->AsObject();
|
||||
|
||||
if (!isValidJsonEnvelope(json)) {
|
||||
LOG_ERROR("JSON received payload on MQTT but not a valid envelope");
|
||||
return;
|
||||
}
|
||||
|
||||
// this is a valid envelope
|
||||
if (json["type"]->AsString().compare("sendtext") == 0 && json["payload"]->IsString()) {
|
||||
std::string jsonPayloadStr = json["payload"]->AsString();
|
||||
LOG_INFO("JSON payload %s, length %u", jsonPayloadStr.c_str(), jsonPayloadStr.length());
|
||||
|
||||
// construct protobuf data packet using TEXT_MESSAGE, send it to the mesh
|
||||
meshtastic_MeshPacket *p = router->allocForSending();
|
||||
p->decoded.portnum = meshtastic_PortNum_TEXT_MESSAGE_APP;
|
||||
if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
|
||||
(json["channel"]->AsNumber() < channels.getNumChannels()))
|
||||
p->channel = json["channel"]->AsNumber();
|
||||
if (json.find("to") != json.end() && json["to"]->IsNumber())
|
||||
p->to = json["to"]->AsNumber();
|
||||
if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
|
||||
p->hop_limit = json["hopLimit"]->AsNumber();
|
||||
if (jsonPayloadStr.length() <= sizeof(p->decoded.payload.bytes)) {
|
||||
memcpy(p->decoded.payload.bytes, jsonPayloadStr.c_str(), jsonPayloadStr.length());
|
||||
p->decoded.payload.size = jsonPayloadStr.length();
|
||||
service->sendToMesh(p, RX_SRC_LOCAL);
|
||||
} else {
|
||||
LOG_WARN("Received MQTT json payload too long, drop");
|
||||
}
|
||||
} else if (json["type"]->AsString().compare("sendposition") == 0 && json["payload"]->IsObject()) {
|
||||
// invent the "sendposition" type for a valid envelope
|
||||
JSONObject posit;
|
||||
posit = json["payload"]->AsObject(); // get nested JSON Position
|
||||
meshtastic_Position pos = meshtastic_Position_init_default;
|
||||
if (posit.find("latitude_i") != posit.end() && posit["latitude_i"]->IsNumber())
|
||||
pos.latitude_i = posit["latitude_i"]->AsNumber();
|
||||
if (posit.find("longitude_i") != posit.end() && posit["longitude_i"]->IsNumber())
|
||||
pos.longitude_i = posit["longitude_i"]->AsNumber();
|
||||
if (posit.find("altitude") != posit.end() && posit["altitude"]->IsNumber())
|
||||
pos.altitude = posit["altitude"]->AsNumber();
|
||||
if (posit.find("time") != posit.end() && posit["time"]->IsNumber())
|
||||
pos.time = posit["time"]->AsNumber();
|
||||
|
||||
// construct protobuf data packet using POSITION, send it to the mesh
|
||||
meshtastic_MeshPacket *p = router->allocForSending();
|
||||
p->decoded.portnum = meshtastic_PortNum_POSITION_APP;
|
||||
if (json.find("channel") != json.end() && json["channel"]->IsNumber() &&
|
||||
(json["channel"]->AsNumber() < channels.getNumChannels()))
|
||||
p->channel = json["channel"]->AsNumber();
|
||||
if (json.find("to") != json.end() && json["to"]->IsNumber())
|
||||
p->to = json["to"]->AsNumber();
|
||||
if (json.find("hopLimit") != json.end() && json["hopLimit"]->IsNumber())
|
||||
p->hop_limit = json["hopLimit"]->AsNumber();
|
||||
p->decoded.payload.size =
|
||||
pb_encode_to_bytes(p->decoded.payload.bytes, sizeof(p->decoded.payload.bytes), &meshtastic_Position_msg,
|
||||
&pos); // make the Data protobuf from position
|
||||
service->sendToMesh(p, RX_SRC_LOCAL);
|
||||
} else {
|
||||
LOG_DEBUG("JSON ignore downlink message with unsupported type");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Determines if the given IPAddress is a private IPv4 address, i.e. not routable on the public internet.
|
||||
bool isPrivateIpAddress(const IPAddress &ip)
|
||||
{
|
||||
@@ -386,26 +292,6 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
|
||||
return;
|
||||
}
|
||||
|
||||
// check if this is a json payload message by comparing the topic start
|
||||
if (moduleConfig.mqtt.json_enabled && (strncmp(topic, jsonTopic.c_str(), jsonTopic.length()) == 0)) {
|
||||
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
|
||||
// parse the channel name from the topic string
|
||||
// the topic has been checked above for having jsonTopic prefix, so just move past it
|
||||
char *channelName = topic + jsonTopic.length();
|
||||
// if another "/" was added, parse string up to that character
|
||||
channelName = strtok(channelName, "/") ? strtok(channelName, "/") : channelName;
|
||||
// We allow downlink JSON packets only on a channel named "mqtt"
|
||||
const meshtastic_Channel &sendChannel = channels.getByName(channelName);
|
||||
if (!(strncasecmp(channels.getGlobalId(sendChannel.index), Channels::mqttChannel, strlen(Channels::mqttChannel)) == 0 &&
|
||||
sendChannel.settings.downlink_enabled)) {
|
||||
LOG_WARN("JSON downlink received on channel not called 'mqtt' or without downlink enabled");
|
||||
return;
|
||||
}
|
||||
onReceiveJson(payload, length);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
onReceiveProto(topic, payload, length);
|
||||
}
|
||||
|
||||
@@ -433,12 +319,10 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
|
||||
|
||||
if (*moduleConfig.mqtt.root) {
|
||||
cryptTopic = moduleConfig.mqtt.root + cryptTopic;
|
||||
jsonTopic = moduleConfig.mqtt.root + jsonTopic;
|
||||
mapTopic = moduleConfig.mqtt.root + mapTopic;
|
||||
isConfiguredForDefaultRootTopic = isDefaultRootTopic(moduleConfig.mqtt.root);
|
||||
} else {
|
||||
cryptTopic = "msh" + cryptTopic;
|
||||
jsonTopic = "msh" + jsonTopic;
|
||||
mapTopic = "msh" + mapTopic;
|
||||
isConfiguredForDefaultRootTopic = true;
|
||||
}
|
||||
@@ -589,14 +473,6 @@ void MQTT::sendSubscriptions()
|
||||
std::string topic = cryptTopic + channels.getGlobalId(i) + "/+";
|
||||
LOG_INFO("Subscribe to %s", topic.c_str());
|
||||
pubSub.subscribe(topic.c_str(), 1); // FIXME, is QOS 1 right?
|
||||
#if !defined(ARCH_NRF52) || \
|
||||
defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJSON ###
|
||||
if (moduleConfig.mqtt.json_enabled == true) {
|
||||
std::string topicDecoded = jsonTopic + channels.getGlobalId(i) + "/+";
|
||||
LOG_INFO("Subscribe to %s", topicDecoded.c_str());
|
||||
pubSub.subscribe(topicDecoded.c_str(), 1); // FIXME, is QOS 1 right?
|
||||
}
|
||||
#endif // ARCH_NRF52 NRF52_USE_JSON
|
||||
}
|
||||
}
|
||||
#if !MESHTASTIC_EXCLUDE_PKI
|
||||
@@ -735,33 +611,6 @@ void MQTT::publishQueuedMessages()
|
||||
const std::unique_ptr<QueueEntry> entry(mqttQueue.dequeuePtr(0));
|
||||
LOG_INFO("publish %s, %u bytes from queue", entry->topic.c_str(), entry->envBytes.size());
|
||||
publish(entry->topic.c_str(), entry->envBytes.data(), entry->envBytes.size(), false);
|
||||
|
||||
#if !defined(ARCH_NRF52) || \
|
||||
defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
|
||||
if (!moduleConfig.mqtt.json_enabled)
|
||||
return;
|
||||
|
||||
// handle json topic
|
||||
const DecodedServiceEnvelope env(entry->envBytes.data(), entry->envBytes.size());
|
||||
if (!env.validDecode || env.packet == NULL || env.channel_id == NULL)
|
||||
return;
|
||||
|
||||
auto jsonString = MeshPacketSerializer::JsonSerialize(env.packet);
|
||||
if (jsonString.length() == 0)
|
||||
return;
|
||||
|
||||
// Generate node ID from nodenum for topic
|
||||
std::string nodeId = nodeDB->getNodeId();
|
||||
|
||||
std::string topicJson;
|
||||
if (env.packet->pki_encrypted) {
|
||||
topicJson = jsonTopic + "PKI/" + nodeId;
|
||||
} else {
|
||||
topicJson = jsonTopic + env.channel_id + "/" + nodeId;
|
||||
}
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
|
||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
#endif // ARCH_NRF52 NRF52_USE_JSON
|
||||
}
|
||||
|
||||
void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_MeshPacket &mp_decoded, ChannelIndex chIndex)
|
||||
@@ -825,21 +674,6 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp_encrypted, const meshtastic_Me
|
||||
if (moduleConfig.mqtt.proxy_to_client_enabled || this->isConnectedDirectly()) {
|
||||
LOG_DEBUG("MQTT Publish %s, %u bytes", topic.c_str(), numBytes);
|
||||
publish(topic.c_str(), bytes, numBytes, false);
|
||||
|
||||
#if !defined(ARCH_NRF52) || \
|
||||
defined(NRF52_USE_JSON) // JSON is not supported on nRF52, see issue #2804 ### Fixed by using ArduinoJson ###
|
||||
if (!moduleConfig.mqtt.json_enabled)
|
||||
return;
|
||||
// handle json topic
|
||||
auto jsonString = MeshPacketSerializer::JsonSerialize(&mp_decoded);
|
||||
if (jsonString.length() == 0)
|
||||
return;
|
||||
// Generate node ID from nodenum for JSON topic
|
||||
std::string nodeIdForJson = nodeDB->getNodeId();
|
||||
std::string topicJson = jsonTopic + channelId + "/" + nodeIdForJson;
|
||||
LOG_INFO("JSON publish message to %s, %u bytes: %s", topicJson.c_str(), jsonString.length(), jsonString.c_str());
|
||||
publish(topicJson.c_str(), jsonString.c_str(), false);
|
||||
#endif // ARCH_NRF52 NRF52_USE_JSON
|
||||
} else {
|
||||
LOG_INFO("MQTT not connected, queue packet");
|
||||
QueueEntry *entry;
|
||||
|
||||
+2
-6
@@ -6,9 +6,6 @@
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "mesh/Channels.h"
|
||||
#include "mesh/generated/meshtastic/mqtt.pb.h"
|
||||
#if !defined(ARCH_NRF52) || NRF52_USE_JSON
|
||||
#include "serialization/JSON.h"
|
||||
#endif
|
||||
#if HAS_WIFI
|
||||
#include <WiFiClient.h>
|
||||
#if __has_include(<WiFiClientSecure.h>)
|
||||
@@ -101,9 +98,8 @@ class MQTT : private concurrency::OSThread
|
||||
explicit MQTT(std::unique_ptr<MQTTClient> mqttClient);
|
||||
#endif
|
||||
|
||||
std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID
|
||||
std::string jsonTopic = "/2/json/"; // msh/2/json/CHANNELID/NODEID
|
||||
std::string mapTopic = "/2/map/"; // For protobuf-encoded MapReport messages
|
||||
std::string cryptTopic = "/2/e/"; // msh/2/e/CHANNELID/NODEID
|
||||
std::string mapTopic = "/2/map/"; // For protobuf-encoded MapReport messages
|
||||
|
||||
// For map reporting (only applies when enabled)
|
||||
const uint32_t default_map_position_precision = 14; // defaults to max. offset of ~1459m
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* File JSON.cpp part of the SimpleJSON Library - http://mjpa.in/json
|
||||
*
|
||||
* Copyright (C) 2010 Mike Anchor
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "JSON.h"
|
||||
|
||||
/**
|
||||
* Blocks off the public constructor
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
*/
|
||||
JSON::JSON() {}
|
||||
|
||||
/**
|
||||
* Parses a complete JSON encoded string
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param char* data The JSON text
|
||||
*
|
||||
* @return JSONValue* Returns a JSON Value representing the root, or NULL on error
|
||||
*/
|
||||
JSONValue *JSON::Parse(const char *data)
|
||||
{
|
||||
// Skip any preceding whitespace, end of data = no JSON = fail
|
||||
if (!SkipWhitespace(&data))
|
||||
return NULL;
|
||||
|
||||
// We need the start of a value here now...
|
||||
JSONValue *value = JSONValue::Parse(&data);
|
||||
if (value == NULL)
|
||||
return NULL;
|
||||
|
||||
// Can be white space now and should be at the end of the string then...
|
||||
if (SkipWhitespace(&data)) {
|
||||
delete value;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We're now at the end of the string
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the passed in JSONValue into a JSON encode string
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param JSONValue* value The root value
|
||||
*
|
||||
* @return std::string Returns a JSON encoded string representation of the given value
|
||||
*/
|
||||
std::string JSON::Stringify(const JSONValue *value)
|
||||
{
|
||||
if (value != NULL)
|
||||
return value->Stringify();
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param char** data Pointer to a char* that contains the JSON text
|
||||
*
|
||||
* @return bool Returns true if there is more data, or false if the end of the text was reached
|
||||
*/
|
||||
bool JSON::SkipWhitespace(const char **data)
|
||||
{
|
||||
while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n'))
|
||||
(*data)++;
|
||||
|
||||
return **data != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a JSON String as defined by the spec - "<some chars>"
|
||||
* Any escaped characters are swapped out for their unescaped values
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param char** data Pointer to a char* that contains the JSON text
|
||||
* @param std::string& str Reference to a std::string to receive the extracted string
|
||||
*
|
||||
* @return bool Returns true on success, false on failure
|
||||
*/
|
||||
bool JSON::ExtractString(const char **data, std::string &str)
|
||||
{
|
||||
str = "";
|
||||
|
||||
while (**data != 0) {
|
||||
// Save the char so we can change it if need be
|
||||
char next_char = **data;
|
||||
|
||||
// Escaping something?
|
||||
if (next_char == '\\') {
|
||||
// Move over the escape char
|
||||
(*data)++;
|
||||
|
||||
// Deal with the escaped char
|
||||
switch (**data) {
|
||||
case '"':
|
||||
next_char = '"';
|
||||
break;
|
||||
case '\\':
|
||||
next_char = '\\';
|
||||
break;
|
||||
case '/':
|
||||
next_char = '/';
|
||||
break;
|
||||
case 'b':
|
||||
next_char = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
next_char = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
next_char = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
next_char = '\r';
|
||||
break;
|
||||
case 't':
|
||||
next_char = '\t';
|
||||
break;
|
||||
case 'u': {
|
||||
// We need 5 chars (4 hex + the 'u') or its not valid
|
||||
if (!simplejson_csnlen(*data, 5))
|
||||
return false;
|
||||
|
||||
// Deal with the chars
|
||||
next_char = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// Do it first to move off the 'u' and leave us on the
|
||||
// final hex digit as we move on by one later on
|
||||
(*data)++;
|
||||
|
||||
next_char <<= 4;
|
||||
|
||||
// Parse the hex digit
|
||||
if (**data >= '0' && **data <= '9')
|
||||
next_char |= (**data - '0');
|
||||
else if (**data >= 'A' && **data <= 'F')
|
||||
next_char |= (10 + (**data - 'A'));
|
||||
else if (**data >= 'a' && **data <= 'f')
|
||||
next_char |= (10 + (**data - 'a'));
|
||||
else {
|
||||
// Invalid hex digit = invalid JSON
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// By the spec, only the above cases are allowed
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// End of the string?
|
||||
else if (next_char == '"') {
|
||||
(*data)++;
|
||||
str.shrink_to_fit(); // Remove unused capacity
|
||||
return true;
|
||||
}
|
||||
|
||||
// Disallowed char?
|
||||
else if (next_char < ' ' && next_char != '\t') {
|
||||
// SPEC Violation: Allow tabs due to real world cases
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the next char
|
||||
str += next_char;
|
||||
|
||||
// Move on
|
||||
(*data)++;
|
||||
}
|
||||
|
||||
// If we're here, the string ended incorrectly
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses some text as though it is an integer
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param char** data Pointer to a char* that contains the JSON text
|
||||
*
|
||||
* @return double Returns the double value of the number found
|
||||
*/
|
||||
double JSON::ParseInt(const char **data)
|
||||
{
|
||||
double integer = 0;
|
||||
while (**data != 0 && **data >= '0' && **data <= '9')
|
||||
integer = integer * 10 + (*(*data)++ - '0');
|
||||
|
||||
return integer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses some text as though it is a decimal
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param char** data Pointer to a char* that contains the JSON text
|
||||
*
|
||||
* @return double Returns the double value of the decimal found
|
||||
*/
|
||||
double JSON::ParseDecimal(const char **data)
|
||||
{
|
||||
double decimal = 0.0;
|
||||
double factor = 0.1;
|
||||
while (**data != 0 && **data >= '0' && **data <= '9') {
|
||||
int digit = (*(*data)++ - '0');
|
||||
decimal = decimal + digit * factor;
|
||||
factor *= 0.1;
|
||||
}
|
||||
return decimal;
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* File JSON.h part of the SimpleJSON Library - http://mjpa.in/json
|
||||
*
|
||||
* Copyright (C) 2010 Mike Anchor
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _JSON_H_
|
||||
#define _JSON_H_
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Simple function to check a string 's' has at least 'n' characters
|
||||
static inline bool simplejson_csnlen(const char *s, size_t n)
|
||||
{
|
||||
if (s == 0)
|
||||
return false;
|
||||
|
||||
const char *save = s;
|
||||
while (n-- > 0) {
|
||||
if (*(save++) == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Custom types
|
||||
class JSONValue;
|
||||
typedef std::vector<JSONValue *> JSONArray;
|
||||
typedef std::map<std::string, JSONValue *> JSONObject;
|
||||
|
||||
#include "JSONValue.h"
|
||||
|
||||
class JSON
|
||||
{
|
||||
friend class JSONValue;
|
||||
|
||||
public:
|
||||
static JSONValue *Parse(const char *data);
|
||||
static std::string Stringify(const JSONValue *value);
|
||||
|
||||
protected:
|
||||
static bool SkipWhitespace(const char **data);
|
||||
static bool ExtractString(const char **data, std::string &str);
|
||||
static double ParseInt(const char **data);
|
||||
static double ParseDecimal(const char **data);
|
||||
|
||||
private:
|
||||
JSON();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,897 +0,0 @@
|
||||
/*
|
||||
* File JSONValue.cpp part of the SimpleJSON Library - http://mjpa.in/json
|
||||
*
|
||||
* Copyright (C) 2010 Mike Anchor
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "JSONValue.h"
|
||||
|
||||
// Macros to free an array/object
|
||||
#define FREE_ARRAY(x) \
|
||||
{ \
|
||||
JSONArray::iterator iter; \
|
||||
for (iter = x.begin(); iter != x.end(); ++iter) { \
|
||||
delete *iter; \
|
||||
} \
|
||||
}
|
||||
#define FREE_OBJECT(x) \
|
||||
{ \
|
||||
JSONObject::iterator iter; \
|
||||
for (iter = x.begin(); iter != x.end(); ++iter) { \
|
||||
delete (*iter).second; \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a JSON encoded value to a JSONValue object
|
||||
*
|
||||
* @access protected
|
||||
*
|
||||
* @param char** data Pointer to a char* that contains the data
|
||||
*
|
||||
* @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error
|
||||
*/
|
||||
JSONValue *JSONValue::Parse(const char **data)
|
||||
{
|
||||
// Is it a string?
|
||||
if (**data == '"') {
|
||||
std::string str;
|
||||
if (!JSON::ExtractString(&(++(*data)), str))
|
||||
return NULL;
|
||||
else
|
||||
return new JSONValue(str);
|
||||
}
|
||||
|
||||
// Is it a boolean?
|
||||
else if ((simplejson_csnlen(*data, 4) && strncasecmp(*data, "true", 4) == 0) ||
|
||||
(simplejson_csnlen(*data, 5) && strncasecmp(*data, "false", 5) == 0)) {
|
||||
bool value = strncasecmp(*data, "true", 4) == 0;
|
||||
(*data) += value ? 4 : 5;
|
||||
return new JSONValue(value);
|
||||
}
|
||||
|
||||
// Is it a null?
|
||||
else if (simplejson_csnlen(*data, 4) && strncasecmp(*data, "null", 4) == 0) {
|
||||
(*data) += 4;
|
||||
return new JSONValue();
|
||||
}
|
||||
|
||||
// Is it a number?
|
||||
else if (**data == '-' || (**data >= '0' && **data <= '9')) {
|
||||
// Negative?
|
||||
bool neg = **data == '-';
|
||||
if (neg)
|
||||
(*data)++;
|
||||
|
||||
double number = 0.0;
|
||||
|
||||
// Parse the whole part of the number - only if it wasn't 0
|
||||
if (**data == '0')
|
||||
(*data)++;
|
||||
else if (**data >= '1' && **data <= '9')
|
||||
number = JSON::ParseInt(data);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
// Could be a decimal now...
|
||||
if (**data == '.') {
|
||||
(*data)++;
|
||||
|
||||
// Not get any digits?
|
||||
if (!(**data >= '0' && **data <= '9'))
|
||||
return NULL;
|
||||
|
||||
// Find the decimal and sort the decimal place out
|
||||
// Use ParseDecimal as ParseInt won't work with decimals less than 0.1
|
||||
// thanks to Javier Abadia for the report & fix
|
||||
double decimal = JSON::ParseDecimal(data);
|
||||
|
||||
// Save the number
|
||||
number += decimal;
|
||||
}
|
||||
|
||||
// Could be an exponent now...
|
||||
if (**data == 'E' || **data == 'e') {
|
||||
(*data)++;
|
||||
|
||||
// Check signage of expo
|
||||
bool neg_expo = false;
|
||||
if (**data == '-' || **data == '+') {
|
||||
neg_expo = **data == '-';
|
||||
(*data)++;
|
||||
}
|
||||
|
||||
// Not get any digits?
|
||||
if (!(**data >= '0' && **data <= '9'))
|
||||
return NULL;
|
||||
|
||||
// Sort the expo out
|
||||
double expo = JSON::ParseInt(data);
|
||||
for (double i = 0.0; i < expo; i++)
|
||||
number = neg_expo ? (number / 10.0) : (number * 10.0);
|
||||
}
|
||||
|
||||
// Was it neg?
|
||||
if (neg)
|
||||
number *= -1;
|
||||
|
||||
return new JSONValue(number);
|
||||
}
|
||||
|
||||
// An object?
|
||||
else if (**data == '{') {
|
||||
JSONObject object;
|
||||
|
||||
(*data)++;
|
||||
|
||||
while (**data != 0) {
|
||||
// Whitespace at the start?
|
||||
if (!JSON::SkipWhitespace(data)) {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Special case - empty object
|
||||
if (object.size() == 0 && **data == '}') {
|
||||
(*data)++;
|
||||
return new JSONValue(object);
|
||||
}
|
||||
|
||||
// We want a string now...
|
||||
std::string name;
|
||||
if (!JSON::ExtractString(&(++(*data)), name)) {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// More whitespace?
|
||||
if (!JSON::SkipWhitespace(data)) {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Need a : now
|
||||
if (*((*data)++) != ':') {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// More whitespace?
|
||||
if (!JSON::SkipWhitespace(data)) {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The value is here
|
||||
JSONValue *value = Parse(data);
|
||||
if (value == NULL) {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add the name:value
|
||||
if (object.find(name) != object.end())
|
||||
delete object[name];
|
||||
object[name] = value;
|
||||
|
||||
// More whitespace?
|
||||
if (!JSON::SkipWhitespace(data)) {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// End of object?
|
||||
if (**data == '}') {
|
||||
(*data)++;
|
||||
return new JSONValue(object);
|
||||
}
|
||||
|
||||
// Want a , now
|
||||
if (**data != ',') {
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*data)++;
|
||||
}
|
||||
|
||||
// Only here if we ran out of data
|
||||
FREE_OBJECT(object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// An array?
|
||||
else if (**data == '[') {
|
||||
JSONArray array;
|
||||
|
||||
(*data)++;
|
||||
|
||||
while (**data != 0) {
|
||||
// Whitespace at the start?
|
||||
if (!JSON::SkipWhitespace(data)) {
|
||||
FREE_ARRAY(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Special case - empty array
|
||||
if (array.size() == 0 && **data == ']') {
|
||||
(*data)++;
|
||||
return new JSONValue(array);
|
||||
}
|
||||
|
||||
// Get the value
|
||||
JSONValue *value = Parse(data);
|
||||
if (value == NULL) {
|
||||
FREE_ARRAY(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add the value
|
||||
array.push_back(value);
|
||||
|
||||
// More whitespace?
|
||||
if (!JSON::SkipWhitespace(data)) {
|
||||
FREE_ARRAY(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// End of array?
|
||||
if (**data == ']') {
|
||||
(*data)++;
|
||||
return new JSONValue(array);
|
||||
}
|
||||
|
||||
// Want a , now
|
||||
if (**data != ',') {
|
||||
FREE_ARRAY(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
(*data)++;
|
||||
}
|
||||
|
||||
// Only here if we ran out of data
|
||||
FREE_ARRAY(array);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Ran out of possibilities, it's bad!
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type NULL
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
JSONValue::JSONValue(/*NULL*/)
|
||||
{
|
||||
type = JSONType_Null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type String
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param char* m_char_value The string to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(const char *m_char_value)
|
||||
{
|
||||
type = JSONType_String;
|
||||
string_value = new std::string(std::string(m_char_value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type String
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param std::string m_string_value The string to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(const std::string &m_string_value)
|
||||
{
|
||||
type = JSONType_String;
|
||||
string_value = new std::string(m_string_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Bool
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param bool m_bool_value The bool to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(bool m_bool_value)
|
||||
{
|
||||
type = JSONType_Bool;
|
||||
bool_value = m_bool_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Number
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param double m_number_value The number to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(double m_number_value)
|
||||
{
|
||||
type = JSONType_Number;
|
||||
number_value = m_number_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Number
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param int m_integer_value The number to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(int m_integer_value)
|
||||
{
|
||||
type = JSONType_Number;
|
||||
number_value = (double)m_integer_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Number
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param unsigned int m_integer_value The number to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(unsigned int m_integer_value)
|
||||
{
|
||||
type = JSONType_Number;
|
||||
number_value = (double)m_integer_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Array
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param JSONArray m_array_value The JSONArray to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(const JSONArray &m_array_value)
|
||||
{
|
||||
type = JSONType_Array;
|
||||
array_value = new JSONArray(m_array_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic constructor for creating a JSON Value of type Object
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param JSONObject m_object_value The JSONObject to use as the value
|
||||
*/
|
||||
JSONValue::JSONValue(const JSONObject &m_object_value)
|
||||
{
|
||||
type = JSONType_Object;
|
||||
object_value = new JSONObject(m_object_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor to perform a deep copy of array / object values
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param JSONValue m_source The source JSONValue that is being copied
|
||||
*/
|
||||
JSONValue::JSONValue(const JSONValue &m_source)
|
||||
{
|
||||
type = m_source.type;
|
||||
|
||||
switch (type) {
|
||||
case JSONType_String:
|
||||
string_value = new std::string(*m_source.string_value);
|
||||
break;
|
||||
|
||||
case JSONType_Bool:
|
||||
bool_value = m_source.bool_value;
|
||||
break;
|
||||
|
||||
case JSONType_Number:
|
||||
number_value = m_source.number_value;
|
||||
break;
|
||||
|
||||
case JSONType_Array: {
|
||||
JSONArray source_array = *m_source.array_value;
|
||||
JSONArray::iterator iter;
|
||||
array_value = new JSONArray();
|
||||
for (iter = source_array.begin(); iter != source_array.end(); ++iter)
|
||||
array_value->push_back(new JSONValue(**iter));
|
||||
break;
|
||||
}
|
||||
|
||||
case JSONType_Object: {
|
||||
JSONObject source_object = *m_source.object_value;
|
||||
object_value = new JSONObject();
|
||||
JSONObject::iterator iter;
|
||||
for (iter = source_object.begin(); iter != source_object.end(); ++iter) {
|
||||
std::string name = (*iter).first;
|
||||
(*object_value)[name] = new JSONValue(*((*iter).second));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JSONType_Null:
|
||||
// Nothing to do.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The destructor for the JSON Value object
|
||||
* Handles deleting the objects in the array or the object value
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
JSONValue::~JSONValue()
|
||||
{
|
||||
if (type == JSONType_Array) {
|
||||
JSONArray::iterator iter;
|
||||
for (iter = array_value->begin(); iter != array_value->end(); ++iter)
|
||||
delete *iter;
|
||||
delete array_value;
|
||||
} else if (type == JSONType_Object) {
|
||||
JSONObject::iterator iter;
|
||||
for (iter = object_value->begin(); iter != object_value->end(); ++iter) {
|
||||
delete (*iter).second;
|
||||
}
|
||||
delete object_value;
|
||||
} else if (type == JSONType_String) {
|
||||
delete string_value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value is a NULL
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if it is a NULL value, false otherwise
|
||||
*/
|
||||
bool JSONValue::IsNull() const
|
||||
{
|
||||
return type == JSONType_Null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value is a String
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if it is a String value, false otherwise
|
||||
*/
|
||||
bool JSONValue::IsString() const
|
||||
{
|
||||
return type == JSONType_String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value is a Bool
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if it is a Bool value, false otherwise
|
||||
*/
|
||||
bool JSONValue::IsBool() const
|
||||
{
|
||||
return type == JSONType_Bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value is a Number
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if it is a Number value, false otherwise
|
||||
*/
|
||||
bool JSONValue::IsNumber() const
|
||||
{
|
||||
return type == JSONType_Number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value is an Array
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if it is an Array value, false otherwise
|
||||
*/
|
||||
bool JSONValue::IsArray() const
|
||||
{
|
||||
return type == JSONType_Array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value is an Object
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if it is an Object value, false otherwise
|
||||
*/
|
||||
bool JSONValue::IsObject() const
|
||||
{
|
||||
return type == JSONType_Object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the String value of this JSONValue
|
||||
* Use IsString() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return std::string Returns the string value
|
||||
*/
|
||||
const std::string &JSONValue::AsString() const
|
||||
{
|
||||
return (*string_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Bool value of this JSONValue
|
||||
* Use IsBool() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns the bool value
|
||||
*/
|
||||
bool JSONValue::AsBool() const
|
||||
{
|
||||
return bool_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Number value of this JSONValue
|
||||
* Use IsNumber() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return double Returns the number value
|
||||
*/
|
||||
double JSONValue::AsNumber() const
|
||||
{
|
||||
return number_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Array value of this JSONValue
|
||||
* Use IsArray() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return JSONArray Returns the array value
|
||||
*/
|
||||
const JSONArray &JSONValue::AsArray() const
|
||||
{
|
||||
return (*array_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the Object value of this JSONValue
|
||||
* Use IsObject() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return JSONObject Returns the object value
|
||||
*/
|
||||
const JSONObject &JSONValue::AsObject() const
|
||||
{
|
||||
return (*object_value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the number of children of this JSONValue.
|
||||
* This number will be 0 or the actual number of children
|
||||
* if IsArray() or IsObject().
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return The number of children.
|
||||
*/
|
||||
std::size_t JSONValue::CountChildren() const
|
||||
{
|
||||
switch (type) {
|
||||
case JSONType_Array:
|
||||
return array_value->size();
|
||||
case JSONType_Object:
|
||||
return object_value->size();
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this JSONValue has a child at the given index.
|
||||
* Use IsArray() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if the array has a value at the given index.
|
||||
*/
|
||||
bool JSONValue::HasChild(std::size_t index) const
|
||||
{
|
||||
if (type == JSONType_Array) {
|
||||
return index < array_value->size();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the child of this JSONValue at the given index.
|
||||
* Use IsArray() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return JSONValue* Returns JSONValue at the given index or NULL
|
||||
* if it doesn't exist.
|
||||
*/
|
||||
JSONValue *JSONValue::Child(std::size_t index)
|
||||
{
|
||||
if (index < array_value->size()) {
|
||||
return (*array_value)[index];
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this JSONValue has a child at the given key.
|
||||
* Use IsObject() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return bool Returns true if the object has a value at the given key.
|
||||
*/
|
||||
bool JSONValue::HasChild(const char *name) const
|
||||
{
|
||||
if (type == JSONType_Object) {
|
||||
return object_value->find(name) != object_value->end();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the child of this JSONValue at the given key.
|
||||
* Use IsObject() before using this method.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return JSONValue* Returns JSONValue for the given key in the object
|
||||
* or NULL if it doesn't exist.
|
||||
*/
|
||||
JSONValue *JSONValue::Child(const char *name)
|
||||
{
|
||||
JSONObject::const_iterator it = object_value->find(name);
|
||||
if (it != object_value->end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the keys of the JSON Object or an empty vector
|
||||
* if this value is not an object.
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @return std::vector<std::string> A vector containing the keys.
|
||||
*/
|
||||
std::vector<std::string> JSONValue::ObjectKeys() const
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
|
||||
if (type == JSONType_Object) {
|
||||
JSONObject::const_iterator iter = object_value->begin();
|
||||
while (iter != object_value->end()) {
|
||||
keys.push_back(iter->first);
|
||||
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON encoded string for the value with all necessary characters escaped
|
||||
*
|
||||
* @access public
|
||||
*
|
||||
* @param bool prettyprint Enable prettyprint
|
||||
*
|
||||
* @return std::string Returns the JSON string
|
||||
*/
|
||||
std::string JSONValue::Stringify(bool const prettyprint) const
|
||||
{
|
||||
size_t const indentDepth = prettyprint ? 1 : 0;
|
||||
return StringifyImpl(indentDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON encoded string for the value with all necessary characters escaped
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param size_t indentDepth The prettyprint indentation depth (0 : no prettyprint)
|
||||
*
|
||||
* @return std::string Returns the JSON string
|
||||
*/
|
||||
std::string JSONValue::StringifyImpl(size_t const indentDepth) const
|
||||
{
|
||||
std::string ret_string;
|
||||
size_t const indentDepth1 = indentDepth ? indentDepth + 1 : 0;
|
||||
std::string const indentStr = Indent(indentDepth);
|
||||
std::string const indentStr1 = Indent(indentDepth1);
|
||||
|
||||
switch (type) {
|
||||
case JSONType_Null:
|
||||
ret_string = "null";
|
||||
break;
|
||||
|
||||
case JSONType_String:
|
||||
ret_string = StringifyString(*string_value);
|
||||
break;
|
||||
|
||||
case JSONType_Bool:
|
||||
ret_string = bool_value ? "true" : "false";
|
||||
break;
|
||||
|
||||
case JSONType_Number: {
|
||||
if (isinf(number_value) || isnan(number_value))
|
||||
ret_string = "null";
|
||||
else {
|
||||
std::stringstream ss;
|
||||
ss.precision(15);
|
||||
ss << number_value;
|
||||
ret_string = ss.str();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JSONType_Array: {
|
||||
ret_string = indentDepth ? "[\n" + indentStr1 : "[";
|
||||
JSONArray::const_iterator iter = array_value->begin();
|
||||
while (iter != array_value->end()) {
|
||||
ret_string += (*iter)->StringifyImpl(indentDepth1);
|
||||
|
||||
// Not at the end - add a separator
|
||||
if (++iter != array_value->end())
|
||||
ret_string += ",";
|
||||
}
|
||||
ret_string += indentDepth ? "\n" + indentStr + "]" : "]";
|
||||
break;
|
||||
}
|
||||
|
||||
case JSONType_Object: {
|
||||
ret_string = indentDepth ? "{\n" + indentStr1 : "{";
|
||||
JSONObject::const_iterator iter = object_value->begin();
|
||||
while (iter != object_value->end()) {
|
||||
ret_string += StringifyString((*iter).first);
|
||||
ret_string += ":";
|
||||
ret_string += (*iter).second->StringifyImpl(indentDepth1);
|
||||
|
||||
// Not at the end - add a separator
|
||||
if (++iter != object_value->end())
|
||||
ret_string += ",";
|
||||
}
|
||||
ret_string += indentDepth ? "\n" + indentStr + "}" : "}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON encoded string with all required fields escaped
|
||||
* Works from http://www.ecma-internationl.org/publications/files/ECMA-ST/ECMA-262.pdf
|
||||
* Section 15.12.3.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param std::string str The string that needs to have the characters escaped
|
||||
*
|
||||
* @return std::string Returns the JSON string
|
||||
*/
|
||||
std::string JSONValue::StringifyString(const std::string &str)
|
||||
{
|
||||
std::string str_out = "\"";
|
||||
|
||||
std::string::const_iterator iter = str.begin();
|
||||
while (iter != str.end()) {
|
||||
char chr = *iter;
|
||||
|
||||
if (chr == '"' || chr == '\\' || chr == '/') {
|
||||
str_out += '\\';
|
||||
str_out += chr;
|
||||
} else if (chr == '\b') {
|
||||
str_out += "\\b";
|
||||
} else if (chr == '\f') {
|
||||
str_out += "\\f";
|
||||
} else if (chr == '\n') {
|
||||
str_out += "\\n";
|
||||
} else if (chr == '\r') {
|
||||
str_out += "\\r";
|
||||
} else if (chr == '\t') {
|
||||
str_out += "\\t";
|
||||
} else if (chr < 0x20 || chr == 0x7F) {
|
||||
char buf[7];
|
||||
snprintf(buf, sizeof(buf), "\\u%04x", chr);
|
||||
str_out += buf;
|
||||
} else if (chr < 0x80) {
|
||||
str_out += chr;
|
||||
} else {
|
||||
str_out += chr;
|
||||
size_t remain = str.end() - iter - 1;
|
||||
if ((chr & 0xE0) == 0xC0 && remain >= 1) {
|
||||
++iter;
|
||||
str_out += *iter;
|
||||
} else if ((chr & 0xF0) == 0xE0 && remain >= 2) {
|
||||
str_out += *(++iter);
|
||||
str_out += *(++iter);
|
||||
} else if ((chr & 0xF8) == 0xF0 && remain >= 3) {
|
||||
str_out += *(++iter);
|
||||
str_out += *(++iter);
|
||||
str_out += *(++iter);
|
||||
}
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
|
||||
str_out += "\"";
|
||||
return str_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the indentation string for the depth given
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param size_t indent The prettyprint indentation depth (0 : no indentation)
|
||||
*
|
||||
* @return std::string Returns the string
|
||||
*/
|
||||
std::string JSONValue::Indent(size_t depth)
|
||||
{
|
||||
const size_t indent_step = 2;
|
||||
depth ? --depth : 0;
|
||||
std::string indentStr(depth * indent_step, ' ');
|
||||
return indentStr;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* File JSONValue.h part of the SimpleJSON Library - http://mjpa.in/json
|
||||
*
|
||||
* Copyright (C) 2010 Mike Anchor
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _JSONVALUE_H_
|
||||
#define _JSONVALUE_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "JSON.h"
|
||||
|
||||
class JSON;
|
||||
|
||||
enum JSONType { JSONType_Null, JSONType_String, JSONType_Bool, JSONType_Number, JSONType_Array, JSONType_Object };
|
||||
|
||||
class JSONValue
|
||||
{
|
||||
friend class JSON;
|
||||
|
||||
public:
|
||||
JSONValue(/*NULL*/);
|
||||
explicit JSONValue(const char *m_char_value);
|
||||
explicit JSONValue(const std::string &m_string_value);
|
||||
explicit JSONValue(bool m_bool_value);
|
||||
explicit JSONValue(double m_number_value);
|
||||
explicit JSONValue(int m_integer_value);
|
||||
explicit JSONValue(unsigned int m_integer_value);
|
||||
explicit JSONValue(const JSONArray &m_array_value);
|
||||
explicit JSONValue(const JSONObject &m_object_value);
|
||||
explicit JSONValue(const JSONValue &m_source);
|
||||
~JSONValue();
|
||||
|
||||
bool IsNull() const;
|
||||
bool IsString() const;
|
||||
bool IsBool() const;
|
||||
bool IsNumber() const;
|
||||
bool IsArray() const;
|
||||
bool IsObject() const;
|
||||
|
||||
const std::string &AsString() const;
|
||||
bool AsBool() const;
|
||||
double AsNumber() const;
|
||||
const JSONArray &AsArray() const;
|
||||
const JSONObject &AsObject() const;
|
||||
|
||||
std::size_t CountChildren() const;
|
||||
bool HasChild(std::size_t index) const;
|
||||
JSONValue *Child(std::size_t index);
|
||||
bool HasChild(const char *name) const;
|
||||
JSONValue *Child(const char *name);
|
||||
std::vector<std::string> ObjectKeys() const;
|
||||
|
||||
std::string Stringify(bool const prettyprint = false) const;
|
||||
|
||||
protected:
|
||||
static JSONValue *Parse(const char **data);
|
||||
|
||||
private:
|
||||
static std::string StringifyString(const std::string &str);
|
||||
std::string StringifyImpl(size_t const indentDepth) const;
|
||||
static std::string Indent(size_t depth);
|
||||
|
||||
JSONType type;
|
||||
|
||||
union {
|
||||
bool bool_value;
|
||||
double number_value;
|
||||
std::string *string_value;
|
||||
JSONArray *array_value;
|
||||
JSONObject *object_value;
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,11 +1,12 @@
|
||||
#ifndef NRF52_USE_JSON
|
||||
#if ARCH_PORTDUINO
|
||||
#include "MeshPacketSerializer.h"
|
||||
#include "JSON.h"
|
||||
#include "NodeDB.h"
|
||||
#include "mesh/generated/meshtastic/mqtt.pb.h"
|
||||
#include "mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "modules/RoutingModule.h"
|
||||
#include <DebugConfiguration.h>
|
||||
#include <json/json.h>
|
||||
#include <memory>
|
||||
#include <mesh-pb-constants.h>
|
||||
#if defined(ARCH_ESP32)
|
||||
#include "../mesh/generated/meshtastic/paxcount.pb.h"
|
||||
@@ -15,41 +16,49 @@
|
||||
|
||||
static const char *errStr = "Error decoding proto for %s message!";
|
||||
|
||||
static std::string writeCompact(const Json::Value &v)
|
||||
{
|
||||
Json::StreamWriterBuilder b;
|
||||
b["indentation"] = "";
|
||||
b["emitUTF8"] = true;
|
||||
return Json::writeString(b, v);
|
||||
}
|
||||
|
||||
static bool tryParseJson(const char *s, Json::Value &out)
|
||||
{
|
||||
Json::CharReaderBuilder b;
|
||||
std::unique_ptr<Json::CharReader> reader(b.newCharReader());
|
||||
std::string errs;
|
||||
const char *end = s + strlen(s);
|
||||
return reader->parse(s, end, &out, &errs);
|
||||
}
|
||||
|
||||
std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog)
|
||||
{
|
||||
// the created jsonObj is immutable after creation, so
|
||||
// we need to do the heavy lifting before assembling it.
|
||||
std::string msgType;
|
||||
JSONObject jsonObj;
|
||||
Json::Value jsonObj(Json::objectValue);
|
||||
|
||||
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
JSONObject msgPayload;
|
||||
Json::Value msgPayload(Json::objectValue);
|
||||
switch (mp->decoded.portnum) {
|
||||
case meshtastic_PortNum_TEXT_MESSAGE_APP: {
|
||||
msgType = "text";
|
||||
// convert bytes to string
|
||||
if (shouldLog)
|
||||
LOG_DEBUG("got text message of size %u", mp->decoded.payload.size);
|
||||
|
||||
char payloadStr[(mp->decoded.payload.size) + 1];
|
||||
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
|
||||
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
|
||||
// check if this is a JSON payload
|
||||
JSONValue *json_value = JSON::Parse(payloadStr);
|
||||
if (json_value != NULL) {
|
||||
payloadStr[mp->decoded.payload.size] = 0;
|
||||
Json::Value parsed;
|
||||
if (tryParseJson(payloadStr, parsed)) {
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type json");
|
||||
|
||||
// if it is, then we can just use the json object
|
||||
jsonObj["payload"] = json_value;
|
||||
jsonObj["payload"] = parsed;
|
||||
} else {
|
||||
// if it isn't, then we need to create a json object
|
||||
// with the string as the value
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type plaintext");
|
||||
|
||||
msgPayload["text"] = new JSONValue(payloadStr);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["text"] = payloadStr;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -61,133 +70,129 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
||||
// If battery is present, encode the battery level value
|
||||
// TODO - Add a condition to send a code for a non-present value
|
||||
if (decoded->variant.device_metrics.has_battery_level) {
|
||||
msgPayload["battery_level"] = new JSONValue((int)decoded->variant.device_metrics.battery_level);
|
||||
msgPayload["battery_level"] = (int)decoded->variant.device_metrics.battery_level;
|
||||
}
|
||||
msgPayload["voltage"] = new JSONValue(decoded->variant.device_metrics.voltage);
|
||||
msgPayload["channel_utilization"] = new JSONValue(decoded->variant.device_metrics.channel_utilization);
|
||||
msgPayload["air_util_tx"] = new JSONValue(decoded->variant.device_metrics.air_util_tx);
|
||||
msgPayload["uptime_seconds"] = new JSONValue((unsigned int)decoded->variant.device_metrics.uptime_seconds);
|
||||
msgPayload["voltage"] = decoded->variant.device_metrics.voltage;
|
||||
msgPayload["channel_utilization"] = decoded->variant.device_metrics.channel_utilization;
|
||||
msgPayload["air_util_tx"] = decoded->variant.device_metrics.air_util_tx;
|
||||
msgPayload["uptime_seconds"] = (Json::UInt)decoded->variant.device_metrics.uptime_seconds;
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
|
||||
// Avoid sending 0s for sensors that could be 0
|
||||
if (decoded->variant.environment_metrics.has_temperature) {
|
||||
msgPayload["temperature"] = new JSONValue(decoded->variant.environment_metrics.temperature);
|
||||
msgPayload["temperature"] = decoded->variant.environment_metrics.temperature;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_relative_humidity) {
|
||||
msgPayload["relative_humidity"] = new JSONValue(decoded->variant.environment_metrics.relative_humidity);
|
||||
msgPayload["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_barometric_pressure) {
|
||||
msgPayload["barometric_pressure"] =
|
||||
new JSONValue(decoded->variant.environment_metrics.barometric_pressure);
|
||||
msgPayload["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_gas_resistance) {
|
||||
msgPayload["gas_resistance"] = new JSONValue(decoded->variant.environment_metrics.gas_resistance);
|
||||
msgPayload["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_voltage) {
|
||||
msgPayload["voltage"] = new JSONValue(decoded->variant.environment_metrics.voltage);
|
||||
msgPayload["voltage"] = decoded->variant.environment_metrics.voltage;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_current) {
|
||||
msgPayload["current"] = new JSONValue(decoded->variant.environment_metrics.current);
|
||||
msgPayload["current"] = decoded->variant.environment_metrics.current;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_lux) {
|
||||
msgPayload["lux"] = new JSONValue(decoded->variant.environment_metrics.lux);
|
||||
msgPayload["lux"] = decoded->variant.environment_metrics.lux;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_white_lux) {
|
||||
msgPayload["white_lux"] = new JSONValue(decoded->variant.environment_metrics.white_lux);
|
||||
msgPayload["white_lux"] = decoded->variant.environment_metrics.white_lux;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_iaq) {
|
||||
msgPayload["iaq"] = new JSONValue((uint)decoded->variant.environment_metrics.iaq);
|
||||
msgPayload["iaq"] = (Json::UInt)decoded->variant.environment_metrics.iaq;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_distance) {
|
||||
msgPayload["distance"] = new JSONValue(decoded->variant.environment_metrics.distance);
|
||||
msgPayload["distance"] = decoded->variant.environment_metrics.distance;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_speed) {
|
||||
msgPayload["wind_speed"] = new JSONValue(decoded->variant.environment_metrics.wind_speed);
|
||||
msgPayload["wind_speed"] = decoded->variant.environment_metrics.wind_speed;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_direction) {
|
||||
msgPayload["wind_direction"] = new JSONValue((uint)decoded->variant.environment_metrics.wind_direction);
|
||||
msgPayload["wind_direction"] = (Json::UInt)decoded->variant.environment_metrics.wind_direction;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_gust) {
|
||||
msgPayload["wind_gust"] = new JSONValue(decoded->variant.environment_metrics.wind_gust);
|
||||
msgPayload["wind_gust"] = decoded->variant.environment_metrics.wind_gust;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_lull) {
|
||||
msgPayload["wind_lull"] = new JSONValue(decoded->variant.environment_metrics.wind_lull);
|
||||
msgPayload["wind_lull"] = decoded->variant.environment_metrics.wind_lull;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_radiation) {
|
||||
msgPayload["radiation"] = new JSONValue(decoded->variant.environment_metrics.radiation);
|
||||
msgPayload["radiation"] = decoded->variant.environment_metrics.radiation;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_ir_lux) {
|
||||
msgPayload["ir_lux"] = new JSONValue(decoded->variant.environment_metrics.ir_lux);
|
||||
msgPayload["ir_lux"] = decoded->variant.environment_metrics.ir_lux;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_uv_lux) {
|
||||
msgPayload["uv_lux"] = new JSONValue(decoded->variant.environment_metrics.uv_lux);
|
||||
msgPayload["uv_lux"] = decoded->variant.environment_metrics.uv_lux;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_weight) {
|
||||
msgPayload["weight"] = new JSONValue(decoded->variant.environment_metrics.weight);
|
||||
msgPayload["weight"] = decoded->variant.environment_metrics.weight;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_rainfall_1h) {
|
||||
msgPayload["rainfall_1h"] = new JSONValue(decoded->variant.environment_metrics.rainfall_1h);
|
||||
msgPayload["rainfall_1h"] = decoded->variant.environment_metrics.rainfall_1h;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_rainfall_24h) {
|
||||
msgPayload["rainfall_24h"] = new JSONValue(decoded->variant.environment_metrics.rainfall_24h);
|
||||
msgPayload["rainfall_24h"] = decoded->variant.environment_metrics.rainfall_24h;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_soil_moisture) {
|
||||
msgPayload["soil_moisture"] = new JSONValue((uint)decoded->variant.environment_metrics.soil_moisture);
|
||||
msgPayload["soil_moisture"] = (Json::UInt)decoded->variant.environment_metrics.soil_moisture;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_soil_temperature) {
|
||||
msgPayload["soil_temperature"] = new JSONValue(decoded->variant.environment_metrics.soil_temperature);
|
||||
msgPayload["soil_temperature"] = decoded->variant.environment_metrics.soil_temperature;
|
||||
}
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
|
||||
if (decoded->variant.air_quality_metrics.has_pm10_standard) {
|
||||
msgPayload["pm10"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm10_standard);
|
||||
msgPayload["pm10"] = (Json::UInt)decoded->variant.air_quality_metrics.pm10_standard;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_pm25_standard) {
|
||||
msgPayload["pm25"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm25_standard);
|
||||
msgPayload["pm25"] = (Json::UInt)decoded->variant.air_quality_metrics.pm25_standard;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_pm100_standard) {
|
||||
msgPayload["pm100"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.pm100_standard);
|
||||
msgPayload["pm100"] = (Json::UInt)decoded->variant.air_quality_metrics.pm100_standard;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_co2) {
|
||||
msgPayload["co2"] = new JSONValue((unsigned int)decoded->variant.air_quality_metrics.co2);
|
||||
msgPayload["co2"] = (Json::UInt)decoded->variant.air_quality_metrics.co2;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_co2_temperature) {
|
||||
msgPayload["co2_temperature"] = new JSONValue(decoded->variant.air_quality_metrics.co2_temperature);
|
||||
msgPayload["co2_temperature"] = decoded->variant.air_quality_metrics.co2_temperature;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_co2_humidity) {
|
||||
msgPayload["co2_humidity"] = new JSONValue(decoded->variant.air_quality_metrics.co2_humidity);
|
||||
msgPayload["co2_humidity"] = decoded->variant.air_quality_metrics.co2_humidity;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_form_formaldehyde) {
|
||||
msgPayload["form_formaldehyde"] = new JSONValue(decoded->variant.air_quality_metrics.form_formaldehyde);
|
||||
msgPayload["form_formaldehyde"] = decoded->variant.air_quality_metrics.form_formaldehyde;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_form_temperature) {
|
||||
msgPayload["form_temperature"] = new JSONValue(decoded->variant.air_quality_metrics.form_temperature);
|
||||
msgPayload["form_temperature"] = decoded->variant.air_quality_metrics.form_temperature;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_form_humidity) {
|
||||
msgPayload["form_humidity"] = new JSONValue(decoded->variant.air_quality_metrics.form_humidity);
|
||||
msgPayload["form_humidity"] = decoded->variant.air_quality_metrics.form_humidity;
|
||||
}
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
|
||||
if (decoded->variant.power_metrics.has_ch1_voltage) {
|
||||
msgPayload["voltage_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_voltage);
|
||||
msgPayload["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch1_current) {
|
||||
msgPayload["current_ch1"] = new JSONValue(decoded->variant.power_metrics.ch1_current);
|
||||
msgPayload["current_ch1"] = decoded->variant.power_metrics.ch1_current;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch2_voltage) {
|
||||
msgPayload["voltage_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_voltage);
|
||||
msgPayload["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch2_current) {
|
||||
msgPayload["current_ch2"] = new JSONValue(decoded->variant.power_metrics.ch2_current);
|
||||
msgPayload["current_ch2"] = decoded->variant.power_metrics.ch2_current;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch3_voltage) {
|
||||
msgPayload["voltage_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_voltage);
|
||||
msgPayload["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch3_current) {
|
||||
msgPayload["current_ch3"] = new JSONValue(decoded->variant.power_metrics.ch3_current);
|
||||
msgPayload["current_ch3"] = decoded->variant.power_metrics.ch3_current;
|
||||
}
|
||||
}
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
@@ -200,12 +205,12 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
msgPayload["id"] = new JSONValue(decoded->id);
|
||||
msgPayload["longname"] = new JSONValue(decoded->long_name);
|
||||
msgPayload["shortname"] = new JSONValue(decoded->short_name);
|
||||
msgPayload["hardware"] = new JSONValue(decoded->hw_model);
|
||||
msgPayload["role"] = new JSONValue((int)decoded->role);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["id"] = decoded->id;
|
||||
msgPayload["longname"] = decoded->long_name;
|
||||
msgPayload["shortname"] = decoded->short_name;
|
||||
msgPayload["hardware"] = (int)decoded->hw_model;
|
||||
msgPayload["role"] = (int)decoded->role;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
@@ -219,38 +224,38 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
if ((int)decoded->time) {
|
||||
msgPayload["time"] = new JSONValue((unsigned int)decoded->time);
|
||||
msgPayload["time"] = (Json::UInt)decoded->time;
|
||||
}
|
||||
if ((int)decoded->timestamp) {
|
||||
msgPayload["timestamp"] = new JSONValue((unsigned int)decoded->timestamp);
|
||||
msgPayload["timestamp"] = (Json::UInt)decoded->timestamp;
|
||||
}
|
||||
msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
|
||||
msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
|
||||
msgPayload["latitude_i"] = (int)decoded->latitude_i;
|
||||
msgPayload["longitude_i"] = (int)decoded->longitude_i;
|
||||
if ((int)decoded->altitude) {
|
||||
msgPayload["altitude"] = new JSONValue((int)decoded->altitude);
|
||||
msgPayload["altitude"] = (int)decoded->altitude;
|
||||
}
|
||||
if ((int)decoded->ground_speed) {
|
||||
msgPayload["ground_speed"] = new JSONValue((unsigned int)decoded->ground_speed);
|
||||
msgPayload["ground_speed"] = (Json::UInt)decoded->ground_speed;
|
||||
}
|
||||
if (int(decoded->ground_track)) {
|
||||
msgPayload["ground_track"] = new JSONValue((unsigned int)decoded->ground_track);
|
||||
msgPayload["ground_track"] = (Json::UInt)decoded->ground_track;
|
||||
}
|
||||
if (int(decoded->sats_in_view)) {
|
||||
msgPayload["sats_in_view"] = new JSONValue((unsigned int)decoded->sats_in_view);
|
||||
msgPayload["sats_in_view"] = (Json::UInt)decoded->sats_in_view;
|
||||
}
|
||||
if ((int)decoded->PDOP) {
|
||||
msgPayload["PDOP"] = new JSONValue((int)decoded->PDOP);
|
||||
msgPayload["PDOP"] = (int)decoded->PDOP;
|
||||
}
|
||||
if ((int)decoded->HDOP) {
|
||||
msgPayload["HDOP"] = new JSONValue((int)decoded->HDOP);
|
||||
msgPayload["HDOP"] = (int)decoded->HDOP;
|
||||
}
|
||||
if ((int)decoded->VDOP) {
|
||||
msgPayload["VDOP"] = new JSONValue((int)decoded->VDOP);
|
||||
msgPayload["VDOP"] = (int)decoded->VDOP;
|
||||
}
|
||||
if ((int)decoded->precision_bits) {
|
||||
msgPayload["precision_bits"] = new JSONValue((int)decoded->precision_bits);
|
||||
msgPayload["precision_bits"] = (int)decoded->precision_bits;
|
||||
}
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
@@ -263,14 +268,14 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
msgPayload["id"] = new JSONValue((unsigned int)decoded->id);
|
||||
msgPayload["name"] = new JSONValue(decoded->name);
|
||||
msgPayload["description"] = new JSONValue(decoded->description);
|
||||
msgPayload["expire"] = new JSONValue((unsigned int)decoded->expire);
|
||||
msgPayload["locked_to"] = new JSONValue((unsigned int)decoded->locked_to);
|
||||
msgPayload["latitude_i"] = new JSONValue((int)decoded->latitude_i);
|
||||
msgPayload["longitude_i"] = new JSONValue((int)decoded->longitude_i);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["id"] = (Json::UInt)decoded->id;
|
||||
msgPayload["name"] = decoded->name;
|
||||
msgPayload["description"] = decoded->description;
|
||||
msgPayload["expire"] = (Json::UInt)decoded->expire;
|
||||
msgPayload["locked_to"] = (Json::UInt)decoded->locked_to;
|
||||
msgPayload["latitude_i"] = (int)decoded->latitude_i;
|
||||
msgPayload["longitude_i"] = (int)decoded->longitude_i;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
@@ -284,26 +289,26 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
|
||||
&scratch)) {
|
||||
decoded = &scratch;
|
||||
msgPayload["node_id"] = new JSONValue((unsigned int)decoded->node_id);
|
||||
msgPayload["node_broadcast_interval_secs"] = new JSONValue((unsigned int)decoded->node_broadcast_interval_secs);
|
||||
msgPayload["last_sent_by_id"] = new JSONValue((unsigned int)decoded->last_sent_by_id);
|
||||
msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count);
|
||||
JSONArray neighbors;
|
||||
msgPayload["node_id"] = (Json::UInt)decoded->node_id;
|
||||
msgPayload["node_broadcast_interval_secs"] = (Json::UInt)decoded->node_broadcast_interval_secs;
|
||||
msgPayload["last_sent_by_id"] = (Json::UInt)decoded->last_sent_by_id;
|
||||
msgPayload["neighbors_count"] = (Json::UInt)decoded->neighbors_count;
|
||||
Json::Value neighbors(Json::arrayValue);
|
||||
for (uint8_t i = 0; i < decoded->neighbors_count; i++) {
|
||||
JSONObject neighborObj;
|
||||
neighborObj["node_id"] = new JSONValue((unsigned int)decoded->neighbors[i].node_id);
|
||||
neighborObj["snr"] = new JSONValue((int)decoded->neighbors[i].snr);
|
||||
neighbors.push_back(new JSONValue(neighborObj));
|
||||
Json::Value neighborObj(Json::objectValue);
|
||||
neighborObj["node_id"] = (Json::UInt)decoded->neighbors[i].node_id;
|
||||
neighborObj["snr"] = (int)decoded->neighbors[i].snr;
|
||||
neighbors.append(neighborObj);
|
||||
}
|
||||
msgPayload["neighbors"] = new JSONValue(neighbors);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["neighbors"] = neighbors;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_TRACEROUTE_APP: {
|
||||
if (mp->decoded.request_id) { // Only report the traceroute response
|
||||
if (mp->decoded.request_id) {
|
||||
msgType = "traceroute";
|
||||
meshtastic_RouteDiscovery scratch;
|
||||
meshtastic_RouteDiscovery *decoded = NULL;
|
||||
@@ -311,13 +316,12 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg,
|
||||
&scratch)) {
|
||||
decoded = &scratch;
|
||||
JSONArray route; // Route this message took
|
||||
JSONArray routeBack; // Route this message took back
|
||||
JSONArray snrTowards; // Snr for forward route
|
||||
JSONArray snrBack; // Snr for reverse route
|
||||
Json::Value route(Json::arrayValue);
|
||||
Json::Value routeBack(Json::arrayValue);
|
||||
Json::Value snrTowards(Json::arrayValue);
|
||||
Json::Value snrBack(Json::arrayValue);
|
||||
|
||||
// Lambda function for adding a long name to the route
|
||||
auto addToRoute = [](JSONArray *route, NodeNum num) {
|
||||
auto addToRoute = [](Json::Value *r, NodeNum num) {
|
||||
char long_name[40] = "Unknown";
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = nodeInfoLiteHasUser(node);
|
||||
@@ -327,33 +331,32 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
memcpy(long_name, node->long_name, copy_len);
|
||||
long_name[copy_len] = '\0';
|
||||
}
|
||||
route->push_back(new JSONValue(long_name));
|
||||
r->append(long_name);
|
||||
};
|
||||
addToRoute(&route, mp->to); // Started at the original transmitter (destination of response)
|
||||
addToRoute(&route, mp->to);
|
||||
for (uint8_t i = 0; i < decoded->route_count; i++) {
|
||||
addToRoute(&route, decoded->route[i]);
|
||||
}
|
||||
addToRoute(&route, mp->from); // Ended at the original destination (source of response)
|
||||
addToRoute(&route, mp->from);
|
||||
|
||||
addToRoute(&routeBack, mp->from); // Started at the original destination (source of response)
|
||||
addToRoute(&routeBack, mp->from);
|
||||
for (uint8_t i = 0; i < decoded->route_back_count; i++) {
|
||||
addToRoute(&routeBack, decoded->route_back[i]);
|
||||
}
|
||||
addToRoute(&routeBack, mp->to); // Ended at the original transmitter (destination of response)
|
||||
addToRoute(&routeBack, mp->to);
|
||||
|
||||
for (uint8_t i = 0; i < decoded->snr_back_count; i++) {
|
||||
snrBack.push_back(new JSONValue((float)decoded->snr_back[i] / 4));
|
||||
snrBack.append((float)decoded->snr_back[i] / 4);
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < decoded->snr_towards_count; i++) {
|
||||
snrTowards.push_back(new JSONValue((float)decoded->snr_towards[i] / 4));
|
||||
snrTowards.append((float)decoded->snr_towards[i] / 4);
|
||||
}
|
||||
|
||||
msgPayload["route"] = new JSONValue(route);
|
||||
msgPayload["route_back"] = new JSONValue(routeBack);
|
||||
msgPayload["snr_back"] = new JSONValue(snrBack);
|
||||
msgPayload["snr_towards"] = new JSONValue(snrTowards);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["route"] = route;
|
||||
msgPayload["route_back"] = routeBack;
|
||||
msgPayload["snr_back"] = snrBack;
|
||||
msgPayload["snr_towards"] = snrTowards;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
@@ -364,9 +367,9 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
msgType = "detection";
|
||||
char payloadStr[(mp->decoded.payload.size) + 1];
|
||||
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
|
||||
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
|
||||
msgPayload["text"] = new JSONValue(payloadStr);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
payloadStr[mp->decoded.payload.size] = 0;
|
||||
msgPayload["text"] = payloadStr;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
break;
|
||||
}
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -377,10 +380,10 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Paxcount_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
msgPayload["wifi_count"] = new JSONValue((unsigned int)decoded->wifi);
|
||||
msgPayload["ble_count"] = new JSONValue((unsigned int)decoded->ble);
|
||||
msgPayload["uptime"] = new JSONValue((unsigned int)decoded->uptime);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["wifi_count"] = (Json::UInt)decoded->wifi;
|
||||
msgPayload["ble_count"] = (Json::UInt)decoded->ble;
|
||||
msgPayload["uptime"] = (Json::UInt)decoded->uptime;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, msgType.c_str());
|
||||
}
|
||||
@@ -396,20 +399,19 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
decoded = &scratch;
|
||||
if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) {
|
||||
msgType = "gpios_changed";
|
||||
msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["gpio_value"] = (Json::UInt)decoded->gpio_value;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
} else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) {
|
||||
msgType = "gpios_read_reply";
|
||||
msgPayload["gpio_value"] = new JSONValue((unsigned int)decoded->gpio_value);
|
||||
msgPayload["gpio_mask"] = new JSONValue((unsigned int)decoded->gpio_mask);
|
||||
jsonObj["payload"] = new JSONValue(msgPayload);
|
||||
msgPayload["gpio_value"] = (Json::UInt)decoded->gpio_value;
|
||||
msgPayload["gpio_mask"] = (Json::UInt)decoded->gpio_mask;
|
||||
jsonObj["payload"] = msgPayload;
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR(errStr, "RemoteHardware");
|
||||
}
|
||||
break;
|
||||
}
|
||||
// add more packet types here if needed
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -417,64 +419,56 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON");
|
||||
}
|
||||
|
||||
jsonObj["id"] = new JSONValue((unsigned int)mp->id);
|
||||
jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time);
|
||||
jsonObj["to"] = new JSONValue((unsigned int)mp->to);
|
||||
jsonObj["from"] = new JSONValue((unsigned int)mp->from);
|
||||
jsonObj["channel"] = new JSONValue((unsigned int)mp->channel);
|
||||
jsonObj["type"] = new JSONValue(msgType.c_str());
|
||||
jsonObj["sender"] = new JSONValue(nodeDB->getNodeId().c_str());
|
||||
jsonObj["id"] = (Json::UInt)mp->id;
|
||||
jsonObj["timestamp"] = (Json::UInt)mp->rx_time;
|
||||
jsonObj["to"] = (Json::UInt)mp->to;
|
||||
jsonObj["from"] = (Json::UInt)mp->from;
|
||||
jsonObj["channel"] = (Json::UInt)mp->channel;
|
||||
jsonObj["type"] = msgType;
|
||||
jsonObj["sender"] = nodeDB->getNodeId();
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
|
||||
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
|
||||
jsonObj["hops_away"] = (Json::UInt)(hopsAway);
|
||||
jsonObj["hop_start"] = (Json::UInt)(mp->hop_start);
|
||||
}
|
||||
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObj);
|
||||
std::string jsonStr = value->Stringify();
|
||||
std::string jsonStr = writeCompact(jsonObj);
|
||||
|
||||
if (shouldLog)
|
||||
LOG_INFO("serialized json message: %s", jsonStr.c_str());
|
||||
|
||||
delete value;
|
||||
return jsonStr;
|
||||
}
|
||||
|
||||
std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp)
|
||||
{
|
||||
JSONObject jsonObj;
|
||||
Json::Value jsonObj(Json::objectValue);
|
||||
|
||||
jsonObj["id"] = new JSONValue((unsigned int)mp->id);
|
||||
jsonObj["time_ms"] = new JSONValue((double)millis());
|
||||
jsonObj["timestamp"] = new JSONValue((unsigned int)mp->rx_time);
|
||||
jsonObj["to"] = new JSONValue((unsigned int)mp->to);
|
||||
jsonObj["from"] = new JSONValue((unsigned int)mp->from);
|
||||
jsonObj["channel"] = new JSONValue((unsigned int)mp->channel);
|
||||
jsonObj["want_ack"] = new JSONValue(mp->want_ack);
|
||||
jsonObj["id"] = (Json::UInt)mp->id;
|
||||
jsonObj["time_ms"] = (double)millis();
|
||||
jsonObj["timestamp"] = (Json::UInt)mp->rx_time;
|
||||
jsonObj["to"] = (Json::UInt)mp->to;
|
||||
jsonObj["from"] = (Json::UInt)mp->from;
|
||||
jsonObj["channel"] = (Json::UInt)mp->channel;
|
||||
jsonObj["want_ack"] = mp->want_ack;
|
||||
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = new JSONValue((unsigned int)(hopsAway));
|
||||
jsonObj["hop_start"] = new JSONValue((unsigned int)(mp->hop_start));
|
||||
jsonObj["hops_away"] = (Json::UInt)(hopsAway);
|
||||
jsonObj["hop_start"] = (Json::UInt)(mp->hop_start);
|
||||
}
|
||||
jsonObj["size"] = new JSONValue((unsigned int)mp->encrypted.size);
|
||||
jsonObj["size"] = (Json::UInt)mp->encrypted.size;
|
||||
auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size);
|
||||
jsonObj["bytes"] = new JSONValue(encryptedStr.c_str());
|
||||
jsonObj["bytes"] = encryptedStr;
|
||||
|
||||
// serialize and write it to the stream
|
||||
JSONValue *value = new JSONValue(jsonObj);
|
||||
std::string jsonStr = value->Stringify();
|
||||
|
||||
delete value;
|
||||
return jsonStr;
|
||||
return writeCompact(jsonObj);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,425 +0,0 @@
|
||||
#ifdef NRF52_USE_JSON
|
||||
#warning 'Using nRF52 Serializer'
|
||||
|
||||
#include "ArduinoJson.h"
|
||||
#include "MeshPacketSerializer.h"
|
||||
#include "NodeDB.h"
|
||||
#include "mesh/generated/meshtastic/mqtt.pb.h"
|
||||
#include "mesh/generated/meshtastic/remote_hardware.pb.h"
|
||||
#include "mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "modules/RoutingModule.h"
|
||||
#include <DebugConfiguration.h>
|
||||
#include <mesh-pb-constants.h>
|
||||
|
||||
StaticJsonDocument<1024> jsonObj;
|
||||
StaticJsonDocument<1024> arrayObj;
|
||||
|
||||
std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp, bool shouldLog)
|
||||
{
|
||||
// the created jsonObj is immutable after creation, so
|
||||
// we need to do the heavy lifting before assembling it.
|
||||
std::string msgType;
|
||||
jsonObj.clear();
|
||||
arrayObj.clear();
|
||||
|
||||
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
switch (mp->decoded.portnum) {
|
||||
case meshtastic_PortNum_TEXT_MESSAGE_APP: {
|
||||
msgType = "text";
|
||||
// convert bytes to string
|
||||
if (shouldLog)
|
||||
LOG_DEBUG("got text message of size %u", mp->decoded.payload.size);
|
||||
|
||||
char payloadStr[(mp->decoded.payload.size) + 1];
|
||||
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
|
||||
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
|
||||
// check if this is a JSON payload
|
||||
StaticJsonDocument<512> text_doc;
|
||||
DeserializationError error = deserializeJson(text_doc, payloadStr);
|
||||
if (error) {
|
||||
// if it isn't, then we need to create a json object
|
||||
// with the string as the value
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type plaintext");
|
||||
jsonObj["payload"]["text"] = payloadStr;
|
||||
} else {
|
||||
// if it is, then we can just use the json object
|
||||
if (shouldLog)
|
||||
LOG_INFO("text message payload is of type json");
|
||||
jsonObj["payload"] = text_doc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_TELEMETRY_APP: {
|
||||
msgType = "telemetry";
|
||||
meshtastic_Telemetry scratch;
|
||||
meshtastic_Telemetry *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
||||
// If battery is present, encode the battery level value
|
||||
// TODO - Add a condition to send a code for a non-present value
|
||||
if (decoded->variant.device_metrics.has_battery_level) {
|
||||
jsonObj["payload"]["battery_level"] = (int)decoded->variant.device_metrics.battery_level;
|
||||
}
|
||||
jsonObj["payload"]["voltage"] = decoded->variant.device_metrics.voltage;
|
||||
jsonObj["payload"]["channel_utilization"] = decoded->variant.device_metrics.channel_utilization;
|
||||
jsonObj["payload"]["air_util_tx"] = decoded->variant.device_metrics.air_util_tx;
|
||||
jsonObj["payload"]["uptime_seconds"] = (unsigned int)decoded->variant.device_metrics.uptime_seconds;
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
|
||||
if (decoded->variant.environment_metrics.has_temperature) {
|
||||
jsonObj["payload"]["temperature"] = decoded->variant.environment_metrics.temperature;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_relative_humidity) {
|
||||
jsonObj["payload"]["relative_humidity"] = decoded->variant.environment_metrics.relative_humidity;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_barometric_pressure) {
|
||||
jsonObj["payload"]["barometric_pressure"] = decoded->variant.environment_metrics.barometric_pressure;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_gas_resistance) {
|
||||
jsonObj["payload"]["gas_resistance"] = decoded->variant.environment_metrics.gas_resistance;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_voltage) {
|
||||
jsonObj["payload"]["voltage"] = decoded->variant.environment_metrics.voltage;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_current) {
|
||||
jsonObj["payload"]["current"] = decoded->variant.environment_metrics.current;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_lux) {
|
||||
jsonObj["payload"]["lux"] = decoded->variant.environment_metrics.lux;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_white_lux) {
|
||||
jsonObj["payload"]["white_lux"] = decoded->variant.environment_metrics.white_lux;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_iaq) {
|
||||
jsonObj["payload"]["iaq"] = (uint)decoded->variant.environment_metrics.iaq;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_speed) {
|
||||
jsonObj["payload"]["wind_speed"] = decoded->variant.environment_metrics.wind_speed;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_direction) {
|
||||
jsonObj["payload"]["wind_direction"] = (uint)decoded->variant.environment_metrics.wind_direction;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_gust) {
|
||||
jsonObj["payload"]["wind_gust"] = decoded->variant.environment_metrics.wind_gust;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_wind_lull) {
|
||||
jsonObj["payload"]["wind_lull"] = decoded->variant.environment_metrics.wind_lull;
|
||||
}
|
||||
if (decoded->variant.environment_metrics.has_radiation) {
|
||||
jsonObj["payload"]["radiation"] = decoded->variant.environment_metrics.radiation;
|
||||
}
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
|
||||
if (decoded->variant.air_quality_metrics.has_pm10_standard) {
|
||||
jsonObj["payload"]["pm10"] = (unsigned int)decoded->variant.air_quality_metrics.pm10_standard;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_pm25_standard) {
|
||||
jsonObj["payload"]["pm25"] = (unsigned int)decoded->variant.air_quality_metrics.pm25_standard;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_pm100_standard) {
|
||||
jsonObj["payload"]["pm100"] = (unsigned int)decoded->variant.air_quality_metrics.pm100_standard;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_co2) {
|
||||
jsonObj["payload"]["co2"] = (unsigned int)decoded->variant.air_quality_metrics.co2;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_co2_temperature) {
|
||||
jsonObj["payload"]["co2_temperature"] = decoded->variant.air_quality_metrics.co2_temperature;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_co2_humidity) {
|
||||
jsonObj["payload"]["co2_humidity"] = decoded->variant.air_quality_metrics.co2_humidity;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_form_formaldehyde) {
|
||||
jsonObj["payload"]["form_formaldehyde"] = decoded->variant.air_quality_metrics.form_formaldehyde;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_form_temperature) {
|
||||
jsonObj["payload"]["form_temperature"] = decoded->variant.air_quality_metrics.form_temperature;
|
||||
}
|
||||
if (decoded->variant.air_quality_metrics.has_form_humidity) {
|
||||
jsonObj["payload"]["form_humidity"] = decoded->variant.air_quality_metrics.form_humidity;
|
||||
}
|
||||
} else if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
|
||||
if (decoded->variant.power_metrics.has_ch1_voltage) {
|
||||
jsonObj["payload"]["voltage_ch1"] = decoded->variant.power_metrics.ch1_voltage;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch1_current) {
|
||||
jsonObj["payload"]["current_ch1"] = decoded->variant.power_metrics.ch1_current;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch2_voltage) {
|
||||
jsonObj["payload"]["voltage_ch2"] = decoded->variant.power_metrics.ch2_voltage;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch2_current) {
|
||||
jsonObj["payload"]["current_ch2"] = decoded->variant.power_metrics.ch2_current;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch3_voltage) {
|
||||
jsonObj["payload"]["voltage_ch3"] = decoded->variant.power_metrics.ch3_voltage;
|
||||
}
|
||||
if (decoded->variant.power_metrics.has_ch3_current) {
|
||||
jsonObj["payload"]["current_ch3"] = decoded->variant.power_metrics.ch3_current;
|
||||
}
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for telemetry message!");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_NODEINFO_APP: {
|
||||
msgType = "nodeinfo";
|
||||
meshtastic_User scratch;
|
||||
meshtastic_User *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
jsonObj["payload"]["id"] = decoded->id;
|
||||
jsonObj["payload"]["longname"] = decoded->long_name;
|
||||
jsonObj["payload"]["shortname"] = decoded->short_name;
|
||||
jsonObj["payload"]["hardware"] = decoded->hw_model;
|
||||
jsonObj["payload"]["role"] = (int)decoded->role;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for nodeinfo message!");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_POSITION_APP: {
|
||||
msgType = "position";
|
||||
meshtastic_Position scratch;
|
||||
meshtastic_Position *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
if ((int)decoded->time) {
|
||||
jsonObj["payload"]["time"] = (unsigned int)decoded->time;
|
||||
}
|
||||
if ((int)decoded->timestamp) {
|
||||
jsonObj["payload"]["timestamp"] = (unsigned int)decoded->timestamp;
|
||||
}
|
||||
jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i;
|
||||
jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i;
|
||||
if ((int)decoded->altitude) {
|
||||
jsonObj["payload"]["altitude"] = (int)decoded->altitude;
|
||||
}
|
||||
if ((int)decoded->ground_speed) {
|
||||
jsonObj["payload"]["ground_speed"] = (unsigned int)decoded->ground_speed;
|
||||
}
|
||||
if (int(decoded->ground_track)) {
|
||||
jsonObj["payload"]["ground_track"] = (unsigned int)decoded->ground_track;
|
||||
}
|
||||
if (int(decoded->sats_in_view)) {
|
||||
jsonObj["payload"]["sats_in_view"] = (unsigned int)decoded->sats_in_view;
|
||||
}
|
||||
if ((int)decoded->PDOP) {
|
||||
jsonObj["payload"]["PDOP"] = (int)decoded->PDOP;
|
||||
}
|
||||
if ((int)decoded->HDOP) {
|
||||
jsonObj["payload"]["HDOP"] = (int)decoded->HDOP;
|
||||
}
|
||||
if ((int)decoded->VDOP) {
|
||||
jsonObj["payload"]["VDOP"] = (int)decoded->VDOP;
|
||||
}
|
||||
if ((int)decoded->precision_bits) {
|
||||
jsonObj["payload"]["precision_bits"] = (int)decoded->precision_bits;
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for position message!");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_WAYPOINT_APP: {
|
||||
msgType = "position";
|
||||
meshtastic_Waypoint scratch;
|
||||
meshtastic_Waypoint *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
jsonObj["payload"]["id"] = (unsigned int)decoded->id;
|
||||
jsonObj["payload"]["name"] = decoded->name;
|
||||
jsonObj["payload"]["description"] = decoded->description;
|
||||
jsonObj["payload"]["expire"] = (unsigned int)decoded->expire;
|
||||
jsonObj["payload"]["locked_to"] = (unsigned int)decoded->locked_to;
|
||||
jsonObj["payload"]["latitude_i"] = (int)decoded->latitude_i;
|
||||
jsonObj["payload"]["longitude_i"] = (int)decoded->longitude_i;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for position message!");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_NEIGHBORINFO_APP: {
|
||||
msgType = "neighborinfo";
|
||||
meshtastic_NeighborInfo scratch;
|
||||
meshtastic_NeighborInfo *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
|
||||
&scratch)) {
|
||||
decoded = &scratch;
|
||||
jsonObj["payload"]["node_id"] = (unsigned int)decoded->node_id;
|
||||
jsonObj["payload"]["node_broadcast_interval_secs"] = (unsigned int)decoded->node_broadcast_interval_secs;
|
||||
jsonObj["payload"]["last_sent_by_id"] = (unsigned int)decoded->last_sent_by_id;
|
||||
jsonObj["payload"]["neighbors_count"] = decoded->neighbors_count;
|
||||
|
||||
JsonObject neighbors_obj = arrayObj.to<JsonObject>();
|
||||
JsonArray neighbors = neighbors_obj.createNestedArray("neighbors");
|
||||
JsonObject neighbors_0 = neighbors.createNestedObject();
|
||||
|
||||
for (uint8_t i = 0; i < decoded->neighbors_count; i++) {
|
||||
neighbors_0["node_id"] = (unsigned int)decoded->neighbors[i].node_id;
|
||||
neighbors_0["snr"] = (int)decoded->neighbors[i].snr;
|
||||
neighbors[i + 1] = neighbors_0;
|
||||
neighbors_0.clear();
|
||||
}
|
||||
neighbors.remove(0);
|
||||
jsonObj["payload"]["neighbors"] = neighbors;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for neighborinfo message!");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_TRACEROUTE_APP: {
|
||||
if (mp->decoded.request_id) { // Only report the traceroute response
|
||||
msgType = "traceroute";
|
||||
meshtastic_RouteDiscovery scratch;
|
||||
meshtastic_RouteDiscovery *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_RouteDiscovery_msg,
|
||||
&scratch)) {
|
||||
decoded = &scratch;
|
||||
JsonArray route = arrayObj.createNestedArray("route");
|
||||
|
||||
auto addToRoute = [](JsonArray *route, NodeNum num) {
|
||||
char long_name[40] = "Unknown";
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = nodeInfoLiteHasUser(node);
|
||||
if (name_known) {
|
||||
const size_t copy_len =
|
||||
(sizeof(node->long_name) < sizeof(long_name)) ? sizeof(node->long_name) : sizeof(long_name) - 1;
|
||||
memcpy(long_name, node->long_name, copy_len);
|
||||
long_name[copy_len] = '\0';
|
||||
}
|
||||
route->add(long_name);
|
||||
};
|
||||
|
||||
addToRoute(&route, mp->to); // route.add(mp->to);
|
||||
for (uint8_t i = 0; i < decoded->route_count; i++) {
|
||||
addToRoute(&route, decoded->route[i]); // route.add(decoded->route[i]);
|
||||
}
|
||||
addToRoute(&route,
|
||||
mp->from); // route.add(mp->from); // Ended at the original destination (source of response)
|
||||
|
||||
jsonObj["payload"]["route"] = route;
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for traceroute message!");
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("Traceroute response not reported");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_DETECTION_SENSOR_APP: {
|
||||
msgType = "detection";
|
||||
char payloadStr[(mp->decoded.payload.size) + 1];
|
||||
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
|
||||
payloadStr[mp->decoded.payload.size] = 0; // null terminated string
|
||||
jsonObj["payload"]["text"] = payloadStr;
|
||||
break;
|
||||
}
|
||||
case meshtastic_PortNum_REMOTE_HARDWARE_APP: {
|
||||
meshtastic_HardwareMessage scratch;
|
||||
meshtastic_HardwareMessage *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_HardwareMessage_msg,
|
||||
&scratch)) {
|
||||
decoded = &scratch;
|
||||
if (decoded->type == meshtastic_HardwareMessage_Type_GPIOS_CHANGED) {
|
||||
msgType = "gpios_changed";
|
||||
jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value;
|
||||
} else if (decoded->type == meshtastic_HardwareMessage_Type_READ_GPIOS_REPLY) {
|
||||
msgType = "gpios_read_reply";
|
||||
jsonObj["payload"]["gpio_value"] = (unsigned int)decoded->gpio_value;
|
||||
jsonObj["payload"]["gpio_mask"] = (unsigned int)decoded->gpio_mask;
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_ERROR("Error decoding proto for RemoteHardware message!");
|
||||
return "";
|
||||
}
|
||||
break;
|
||||
}
|
||||
// add more packet types here if needed
|
||||
default:
|
||||
LOG_WARN("Unsupported packet type %d", mp->decoded.portnum);
|
||||
return "";
|
||||
break;
|
||||
}
|
||||
} else if (shouldLog) {
|
||||
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON");
|
||||
return "";
|
||||
}
|
||||
|
||||
jsonObj["id"] = (unsigned int)mp->id;
|
||||
jsonObj["timestamp"] = (unsigned int)mp->rx_time;
|
||||
jsonObj["to"] = (unsigned int)mp->to;
|
||||
jsonObj["from"] = (unsigned int)mp->from;
|
||||
jsonObj["channel"] = (unsigned int)mp->channel;
|
||||
jsonObj["type"] = msgType.c_str();
|
||||
jsonObj["sender"] = nodeDB->getNodeId().c_str();
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = (unsigned int)(hopsAway);
|
||||
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
|
||||
}
|
||||
|
||||
// serialize and write it to the stream
|
||||
|
||||
// Serial.printf("serialized json message: \r");
|
||||
// serializeJson(jsonObj, Serial);
|
||||
// Serial.println("");
|
||||
|
||||
std::string jsonStr = "";
|
||||
serializeJson(jsonObj, jsonStr);
|
||||
|
||||
if (shouldLog)
|
||||
LOG_INFO("serialized json message: %s", jsonStr.c_str());
|
||||
|
||||
return jsonStr;
|
||||
}
|
||||
|
||||
std::string MeshPacketSerializer::JsonSerializeEncrypted(const meshtastic_MeshPacket *mp)
|
||||
{
|
||||
jsonObj.clear();
|
||||
jsonObj["id"] = (unsigned int)mp->id;
|
||||
jsonObj["time_ms"] = (double)millis();
|
||||
jsonObj["timestamp"] = (unsigned int)mp->rx_time;
|
||||
jsonObj["to"] = (unsigned int)mp->to;
|
||||
jsonObj["from"] = (unsigned int)mp->from;
|
||||
jsonObj["channel"] = (unsigned int)mp->channel;
|
||||
jsonObj["want_ack"] = mp->want_ack;
|
||||
|
||||
if (mp->rx_rssi != 0)
|
||||
jsonObj["rssi"] = (int)mp->rx_rssi;
|
||||
if (mp->rx_snr != 0)
|
||||
jsonObj["snr"] = (float)mp->rx_snr;
|
||||
const int8_t hopsAway = getHopsAway(*mp);
|
||||
if (hopsAway >= 0) {
|
||||
jsonObj["hops_away"] = (unsigned int)(hopsAway);
|
||||
jsonObj["hop_start"] = (unsigned int)(mp->hop_start);
|
||||
}
|
||||
jsonObj["size"] = (unsigned int)mp->encrypted.size;
|
||||
auto encryptedStr = bytesToHex(mp->encrypted.bytes, mp->encrypted.size);
|
||||
jsonObj["bytes"] = encryptedStr.c_str();
|
||||
|
||||
// serialize and write it to the stream
|
||||
std::string jsonStr = "";
|
||||
serializeJson(jsonObj, jsonStr);
|
||||
|
||||
return jsonStr;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user