canscope/src/j1939_db.cpp

180 lines
5.6 KiB
C++

#include "j1939_db.hpp"
#include "parsers.hpp"
#include <cmath>
#define FMT_HEADER_ONLY
#include <fmt/format.h>
void initJ1939Database(sqlite::database &db) {
db << R"(
CREATE TABLE IF NOT EXISTS pgns (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
pgn INTEGER UNIQUE,
pg_label TEXT,
pg_acronym TEXT,
pg_descr TEXT,
edp INTEGER,
dp INTEGER,
pf INTEGER,
ps INTEGER,
pg_datalen INTEGER,
pg_priority INTEGER
);
)";
db << R"(
CREATE TABLE IF NOT EXISTS spns (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
pgn INTEGER,
spn INTEGER UNIQUE,
spn_name TEXT,
spn_pos TEXT,
spn_length INTEGER,
resolution REAL,
offset REAL,
data_range TEXT,
min_value REAL,
max_value REAL,
units TEXT,
slot_id TEXT,
slot_name TEXT,
spn_type TEXT
);
)";
db << R"(
CREATE TABLE IF NOT EXISTS spn_fragments (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
spn INTEGER,
pgn INTEGER,
byte_offset INTEGER,
bit_offset INTEGER,
size INTEGER
);
)";
db << "PRAGMA journal_mode = OFF";
db << "PRAGMA synchronous = OFF";
db << "PRAGMA foreign_keys = ON;";
}
std::string buildPgnInsertSql() {
const auto &pgn_mapping = J1939MappingTables::pgn();
std::string cols, placeholders;
for (const auto &[k, v] : pgn_mapping) {
if (!cols.empty()) { cols += ", "; placeholders += ", "; }
cols += std::get<1u>(v);
placeholders += "?";
}
return fmt::format("INSERT OR REPLACE INTO pgns ({}) VALUES ({})", cols, placeholders);
}
std::string buildSpnInsertSql() {
const auto &spn_mapping = J1939MappingTables::spn();
std::string cols = "pgn", placeholders = "?";
for (const auto &[k, v] : spn_mapping) {
cols += ", ";
placeholders += ", ";
if (std::get<1u>(v) == "data_range") {
cols += "min_value, max_value";
placeholders += "?, ?";
} else {
cols += std::get<1u>(v);
placeholders += "?";
}
}
return fmt::format("INSERT OR REPLACE INTO spns ({}) VALUES ({})", cols, placeholders);
}
void insertJ1939Row(sqlite::database &db, const std::string &pgn_insert_sql, const std::string &spn_insert_sql,
std::map<std::string, std::string> &pgn_row_map, std::map<std::string, std::string> &spn_row_map) {
const auto &pgn_mapping = J1939MappingTables::pgn();
const auto &spn_mapping = J1939MappingTables::spn();
// Insert PGN
try {
auto ps = db << pgn_insert_sql;
for (const auto &[k, v] : pgn_mapping) {
ps << pgn_row_map[std::get<1u>(v).data()];
}
ps.execute();
} catch (const sqlite::sqlite_exception &e) {
if (e.get_extended_code() != SQLITE_CONSTRAINT_UNIQUE) throw;
}
// Insert SPN
struct {
bool parts_inserted_flag = false;
double min = 0.0, max = 0.0;
size_t size_bits = 0u;
} calc;
// Pre-compute size_bits
{
auto it = spn_mapping.find("SPN Length");
if (it != spn_mapping.end()) {
auto size = parsers::parseSpnSize(spn_row_map[std::get<1u>(it->second).data()]);
calc.size_bits = size.has_value() ? size.value().size_bits : 0u;
}
}
for (const auto &[k, v] : spn_row_map) {
try {
auto ps = db << spn_insert_sql;
ps << pgn_row_map["pgn"];
for (const auto &[k, v] : spn_mapping) {
if (std::get<1u>(v) == "data_range") {
auto range = parsers::parseSpnDataRange(spn_row_map[std::get<1u>(v).data()]);
if (range.has_value()) {
calc.min = range.value().min;
calc.max = range.value().max;
ps << range.value().min << range.value().max;
} else {
ps << nullptr << nullptr;
}
} else if (std::get<1u>(v) == "offset") {
auto offset = parsers::parseSpnOffset(spn_row_map[std::get<1u>(v).data()]);
ps << (offset.has_value() ? offset.value().offset : 0.0);
} else if (std::get<1u>(v) == "spn_length") {
auto size = parsers::parseSpnSize(spn_row_map[std::get<1u>(v).data()]);
calc.size_bits = size.has_value() ? size.value().size_bits : 0u;
ps << calc.size_bits;
} else if (std::get<1u>(v) == "resolution") {
double calculated = (calc.max - calc.min) / (std::pow(2.0, calc.size_bits) - 1.0);
if (std::fabs(calculated - 1.0) < 1e-9) {
ps << calculated;
} else {
auto resolution = parsers::parseSpnResolution(spn_row_map[std::get<1u>(v).data()]);
ps << (resolution.has_value() ? resolution.value().resolution : 1.0);
}
} else {
ps << spn_row_map[std::get<1u>(v).data()];
}
}
ps.execute();
// Insert SPN fragments
if (!calc.parts_inserted_flag) {
auto size = parsers::parseSpnSize(spn_row_map[std::get<1u>(spn_mapping.at("SPN Length")).data()]);
if (size.has_value()) {
auto spn_fragments = parsers::parseSpnPosition(
size.value().size_bits, spn_row_map[std::get<1u>(spn_mapping.at("SPN Position in PG")).data()]);
if (spn_fragments.has_value()) {
auto spn = std::stoll(spn_row_map["spn"]);
for (const auto &part : spn_fragments.value().spn_fragments) {
db << R"(INSERT OR REPLACE INTO spn_fragments (spn, pgn, byte_offset, bit_offset, size) VALUES (?, ?, ?, ?, ?);)"
<< spn << std::stoll(pgn_row_map["pgn"]) << part.byte_offset << part.bit_offset << part.size;
}
calc.parts_inserted_flag = true;
}
}
}
} catch (const sqlite::sqlite_exception &e) {
if (e.get_extended_code() != SQLITE_CONSTRAINT_UNIQUE) throw;
}
}
}