184 lines
7.3 KiB
C++
184 lines
7.3 KiB
C++
#include "can_data.hpp"
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#define FMT_HEADER_ONLY
|
|
#include <fmt/format.h>
|
|
#include <fmt/ranges.h>
|
|
|
|
// For sqlite
|
|
#include "sqlite_modern_cpp.h"
|
|
|
|
std::pair<nlohmann::json, nlohmann::json> processFrame(sqlite::database &db, const std::string &iface,
|
|
const std::string &canid, const std::vector<uint8_t> &data) {
|
|
nlohmann::json verbose, brief;
|
|
|
|
db << R"(SELECT pgn, pg_label, pg_acronym, pg_descr, edp, dp, pf, ps, pg_datalen, pg_priority FROM pgns WHERE pgn = ?;)"
|
|
<< [&]() -> int32_t {
|
|
int32_t ret;
|
|
std::stringstream{} << std::hex << canid.substr(2, 4) >> ret;
|
|
return ret;
|
|
}() >> [&](int32_t pgn, const std::string &pg_label, const std::string &pg_acronym, const std::string &pg_descr,
|
|
int32_t edp, int32_t dp, int32_t pf, int32_t ps, int32_t pd_datalen, int32_t pg_priority) {
|
|
int32_t pgn_int;
|
|
std::stringstream{} << std::hex << canid.substr(2, 4) >> pgn_int;
|
|
|
|
// Make header
|
|
verbose = {
|
|
{"Interface", iface}, {"Can ID", canid}, {"PGN", canid.substr(2, 4)}, {"PGN (integer)", pgn_int},
|
|
{"Acronym", pg_acronym}, {"Label", pg_label}, {"Description", pg_descr},
|
|
};
|
|
|
|
// Get SPNs
|
|
nlohmann::json::array_t spns_array;
|
|
db << R"(SELECT spn, spn_name, spn_pos, spn_length, resolution, offset, data_range, min_value, max_value, units, slot_id, slot_name, spn_type, value_encoding FROM spns WHERE pgn = ?;)"
|
|
<< pgn_int >>
|
|
[&](int32_t spn, const std::string &spn_name, const std::string &spn_pos, int32_t spn_length, double resolution,
|
|
int32_t offset, const std::string &data_range, double min, double max, const std::string &unit,
|
|
const std::string &slot_id, const std::string &slot_name, const std::string &spn_type,
|
|
const std::string &value_encoding) {
|
|
nlohmann::json spn_json = {
|
|
{"SPN (integer)", spn},
|
|
{"SPN name", spn_name},
|
|
{"SPN position", spn_pos},
|
|
{"SPN length (bits)", spn_length},
|
|
{"Resolution", resolution},
|
|
{"Offset", offset},
|
|
{"Minimum value", min},
|
|
{"Maximum value", max},
|
|
{"Unit", unit},
|
|
{"SLOT id", slot_id},
|
|
{"SPN type", spn_type},
|
|
{"Encoding", value_encoding},
|
|
};
|
|
|
|
// Get parts
|
|
size_t result = 0u, iter = 0u, total_size_bits = 0u;
|
|
std::vector<uint8_t> ascii_bytes;
|
|
nlohmann::json::array_t parts_array;
|
|
|
|
db << "SELECT byte_offset,bit_offset,size FROM spn_fragments WHERE spn = ? AND pgn = ?" << spn << pgn >>
|
|
[&, byte_array = data](int32_t byte_offset, int32_t bit_offset, int32_t size_bits) {
|
|
if (value_encoding == "ascii") {
|
|
// ASCII SPNs are byte-aligned; collect raw bytes directly — size may exceed 64 bits.
|
|
const size_t nbytes = size_bits / UINT8_WIDTH;
|
|
|
|
for (size_t i = 0; i < nbytes && (byte_offset + i) < byte_array.size(); ++i) {
|
|
ascii_bytes.push_back(byte_array[byte_offset + i]);
|
|
}
|
|
|
|
parts_array.push_back(nlohmann::json::parse(fmt::format(
|
|
R"({{"{}":{{"byte_offset":{},"bit_offset":{},"size_bits":{},"parse_result":"ascii"}}}})",
|
|
fmt::format("Fragment#{}", iter++), byte_offset, bit_offset, size_bits)));
|
|
return;
|
|
}
|
|
|
|
result <<= size_bits;
|
|
total_size_bits += size_bits;
|
|
|
|
for (uint32_t i = 0; i < ((size_bits / UINT8_WIDTH) + (size_bits % UINT8_WIDTH ? 1 : 0)); ++i) {
|
|
const uint8_t &byte = byte_array[byte_offset + i];
|
|
result |= (((byte << (i * UINT8_WIDTH)) &
|
|
(size_bits % UINT8_WIDTH
|
|
? ~((0xff << (size_bits % UINT8_WIDTH + bit_offset + i * UINT8_WIDTH)) |
|
|
~(0xff << (bit_offset + i * UINT8_WIDTH)))
|
|
: (0xff << (size_bits % UINT8_WIDTH + bit_offset + i * UINT8_WIDTH)) |
|
|
~(0xff << (bit_offset + i * UINT8_WIDTH)))) >>
|
|
(bit_offset));
|
|
}
|
|
|
|
parts_array.push_back(nlohmann::json::parse(
|
|
fmt::format(R"({{"{}":{{"byte_offset":{},"bit_offset":{},"size_bits":{},"parse_result":"{}"}}}})",
|
|
fmt::format("Fragment#{}", iter++), byte_offset, bit_offset, size_bits,
|
|
fmt::format("{:#x}", result))));
|
|
};
|
|
|
|
spn_json["Fragments"] = parts_array;
|
|
if (value_encoding == "ascii") {
|
|
std::string text;
|
|
text.reserve(ascii_bytes.size());
|
|
|
|
for (uint8_t b : ascii_bytes) {
|
|
text += (b >= 0x20 && b < 0x7f) ? static_cast<char>(b) : '.';
|
|
}
|
|
|
|
spn_json["Value"] = text;
|
|
} else {
|
|
spn_json["Value"] = result * resolution + offset;
|
|
}
|
|
|
|
spns_array.push_back(spn_json);
|
|
};
|
|
|
|
verbose["SPNs"] = spns_array;
|
|
};
|
|
|
|
if (!verbose.is_null()) {
|
|
brief = {
|
|
{"PGN", verbose["PGN"]},
|
|
{"Label", verbose["Label"]},
|
|
{"Acronym", verbose["Acronym"]},
|
|
};
|
|
|
|
nlohmann::json::array_t spns_array;
|
|
for (const auto &spn : verbose["SPNs"]) {
|
|
const auto name = spn["SPN name"].get<std::string>();
|
|
const auto unit = spn["Unit"].get<std::string>();
|
|
const auto encoding = spn.value("Encoding", std::string{"numeric"});
|
|
|
|
if (encoding == "ascii") {
|
|
spns_array.push_back(fmt::format("{}: \"{}\"", name, spn["Value"].get<std::string>()));
|
|
} else if (encoding == "binary") {
|
|
spns_array.push_back(fmt::format("{}: {}", name, spn["Value"].get<double>() != 0.0 ? "yes" : "no"));
|
|
} else {
|
|
spns_array.push_back(fmt::format("{}: {:.6g} {}", name, spn["Value"].get<double>(), unit));
|
|
}
|
|
}
|
|
|
|
brief["SPNs"] = spns_array;
|
|
}
|
|
|
|
return {verbose, brief};
|
|
}
|
|
|
|
nlohmann::json verboseToExportJson(const nlohmann::json &verbose) {
|
|
nlohmann::json::array_t spns;
|
|
|
|
if (verbose.is_null() || !verbose.contains("SPNs"))
|
|
return spns;
|
|
|
|
std::string pgn = verbose.contains("PGN") ? verbose["PGN"].get<std::string>() : "";
|
|
|
|
for (const auto &v : verbose["SPNs"]) {
|
|
nlohmann::json spn = {
|
|
{"name", v.value("SPN name", "")}, {"offset", v.value("Offset", 0)},
|
|
{"resolution", v.value("Resolution", 0.0)}, {"max", v.value("Maximum value", 0.0)},
|
|
{"min", v.value("Minimum value", 0.0)}, {"pgn", pgn},
|
|
{"value", v.value("Value", 0.0)}, {"unit", v.value("Unit", "")},
|
|
};
|
|
|
|
nlohmann::json::array_t frags;
|
|
|
|
if (v.contains("Fragments")) {
|
|
for (const auto &[k, frag] : v["Fragments"].items()) {
|
|
auto frag_key = fmt::format("Fragment#{}", k);
|
|
|
|
if (frag.contains(frag_key)) {
|
|
frags.push_back({{
|
|
fmt::format("fragment#{}", k),
|
|
{
|
|
{"byte_pos", frag[frag_key].value("byte_offset", 0)},
|
|
{"bit_pos", frag[frag_key].value("bit_offset", 0)},
|
|
{"bit_size", frag[frag_key].value("size_bits", 0)},
|
|
},
|
|
}});
|
|
}
|
|
}
|
|
}
|
|
|
|
spn["fragments"] = std::move(frags);
|
|
spns.push_back(std::move(spn));
|
|
}
|
|
|
|
return spns;
|
|
}
|