194 lines
7.1 KiB
C++
194 lines
7.1 KiB
C++
#include "tuple.hpp"
|
|
#include <functional>
|
|
#include <boost/convert.hpp>
|
|
#include <boost/convert/stream.hpp>
|
|
#include <boost/functional/hash.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/signals2.hpp>
|
|
#include <boost/uuid/uuid.hpp>
|
|
#include <boost/uuid/uuid_generators.hpp>
|
|
#include <boost/uuid/uuid_io.hpp>
|
|
|
|
#include <condition_variable>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <ftxui/component/component.hpp>
|
|
#include <ftxui/component/screen_interactive.hpp>
|
|
#include <ftxui/dom/elements.hpp>
|
|
#include <future>
|
|
#include <nlohmann/json.hpp>
|
|
#include <ranges>
|
|
#include <unordered_map>
|
|
|
|
#define FMT_HEADER_ONLY
|
|
#include <fmt/format.h>
|
|
#include <fmt/ranges.h>
|
|
|
|
#include "signals.hpp"
|
|
#include "tagsettings.hpp"
|
|
|
|
class GraphId_ {
|
|
public:
|
|
GraphId_(const std::string &name, const std::string &uuid = generateUUID_()) : m_hash_(std::hash<std::string>()(uuid)), m_name_(name) {}
|
|
~GraphId_() = default;
|
|
|
|
bool operator==(const GraphId_ &other) const { return m_hash_ == other.hash(); }
|
|
|
|
size_t hash() const { return m_hash_; }
|
|
const std::string &name() const { return m_name_; }
|
|
|
|
private:
|
|
static std::string generateUUID_() { return boost::lexical_cast<std::string>(boost::uuids::random_generator()()); };
|
|
const size_t m_hash_;
|
|
const std::string m_name_;
|
|
};
|
|
|
|
template <> struct std::hash<GraphId_> {
|
|
std::size_t operator()(const GraphId_ &g) const noexcept { return g.hash(); }
|
|
};
|
|
|
|
ftxui::Component makeGraph(const std::string &canid, const struct spn_settings_s &settings, std::function<std::vector<uint8_t>()> data_fn, ftxui::ScreenInteractive *screen,
|
|
signals_map_t &smap) {
|
|
static const auto json_array_to_payload = [](const nlohmann::json &settings, const std::vector<std::string> &strvec_bytes) {
|
|
std::vector<uint8_t> res;
|
|
|
|
if (settings.contains("size") && settings.contains("pos") && settings.contains("le")) {
|
|
boost::cnv::cstream converter;
|
|
size_t size_setting = settings["size"].get<size_t>();
|
|
res.reserve(size_setting);
|
|
converter(std::hex)(std::skipws);
|
|
auto f = apply<int>(std::ref(converter)).value_or(-1);
|
|
|
|
if (auto [pos, size, data_size] =
|
|
std::tuple<int32_t, int32_t, size_t>{
|
|
settings["pos"].get<int32_t>(),
|
|
size_setting,
|
|
strvec_bytes.size(),
|
|
};
|
|
pos < data_size && (pos + size) < data_size) {
|
|
|
|
{
|
|
namespace rv = std::ranges::views;
|
|
const auto push = [&](const auto &i) { res.push_back(boost::lexical_cast<int32_t>(f(strvec_bytes[i]))); };
|
|
|
|
auto seq = rv::iota(pos, pos + size);
|
|
if (settings["le"].get<bool>()) {
|
|
for (const auto &i : seq) {
|
|
|
|
push(i);
|
|
}
|
|
} else {
|
|
for (const auto &i : seq | rv::reverse) {
|
|
|
|
push(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
};
|
|
|
|
static const auto create_json = [](const std::vector<uint8_t> &data, const struct spn_settings_s &settings) {
|
|
nlohmann::json array = nlohmann::json::array(), settings_json, json;
|
|
uint32_t crc = std::accumulate(data.begin(), data.end(), uint32_t{});
|
|
double spn_val = 0.0f;
|
|
|
|
for (auto &d : data) {
|
|
array.push_back(fmt::format("{:2x}", d));
|
|
}
|
|
|
|
json = {{"crc", crc}, {"data", array}};
|
|
|
|
{
|
|
std::string current_setting;
|
|
try {
|
|
|
|
tp::for_each(
|
|
std::tuple{
|
|
std::make_tuple("name", &settings.spn_name, static_cast<std::string *>(nullptr)),
|
|
std::make_tuple("le", &settings.le, static_cast<bool *>(nullptr)),
|
|
std::make_tuple("size", &settings.size, static_cast<size_t *>(nullptr)),
|
|
std::make_tuple("offset", &settings.offset, static_cast<double *>(nullptr)),
|
|
std::make_tuple("pos", &settings.pos, static_cast<size_t *>(nullptr)),
|
|
std::make_tuple("x", &settings.x_coeff, static_cast<double *>(nullptr)),
|
|
std::make_tuple("y", &settings.y_coeff, static_cast<double *>(nullptr)),
|
|
std::make_tuple("discrete", &settings.discrete, static_cast<bool *>(nullptr)),
|
|
std::make_tuple("bit_offset", &settings.bit_offset, static_cast<size_t *>(nullptr)),
|
|
std::make_tuple("bit_count", &settings.bit_count, static_cast<size_t *>(nullptr)),
|
|
std::make_tuple("uuid", &settings.uuid, static_cast<std::string *>(nullptr)),
|
|
},
|
|
|
|
[&](const auto &e) {
|
|
current_setting = std::get<0u>(e);
|
|
settings_json[std::get<0u>(e)] = boost::lexical_cast<std::remove_cvref_t<decltype(*std::get<2u>(e))>>(*std::get<1u>(e));
|
|
});
|
|
|
|
current_setting.clear();
|
|
|
|
std::vector<std::string> v = json["data"].is_array() ? json["data"].template get<std::vector<std::string>>() : std::vector<std::string>{};
|
|
std::vector<uint8_t> bytes = json_array_to_payload(settings_json, v);
|
|
|
|
json["payload_bytes"] = [&]() {
|
|
nlohmann::json::array_t array;
|
|
for (const auto &byte : bytes) {
|
|
array.push_back(fmt::format("{:2x}", byte));
|
|
}
|
|
|
|
return array;
|
|
}();
|
|
|
|
int32_t integer = 0;
|
|
|
|
for (auto iter = int32_t{0}; const auto &b : bytes) {
|
|
integer |= b << ((iter++) * UINT8_WIDTH);
|
|
}
|
|
|
|
if (settings_json["x"].get<double>() / settings_json["y"].get<double>() != NAN) {
|
|
spn_val = static_cast<double>(integer) * settings_json["x"].get<double>() / settings_json["y"].get<double>() + settings_json["offset"].get<double>();
|
|
}
|
|
|
|
json["spn_value"] = spn_val;
|
|
} catch (const boost::bad_lexical_cast &e) {
|
|
|
|
settings_json["warning"] = fmt::format("Bad '{}' setting", current_setting);
|
|
}
|
|
}
|
|
|
|
return json;
|
|
};
|
|
|
|
class Impl : public ftxui::ComponentBase {
|
|
public:
|
|
explicit Impl(const std::string &canid, const struct spn_settings_s &settings, std::function<std::vector<uint8_t>()> data_fn, ftxui::ScreenInteractive *screen, signals_map_t &smap)
|
|
: m_settings_(settings), m_data_fn_(std::move(data_fn)) {
|
|
|
|
auto renderer = ftxui::Renderer([this]() {
|
|
auto data = m_data_fn_();
|
|
auto json = create_json(data, m_settings_);
|
|
return ftxui::vbox({
|
|
ftxui::text(fmt::format("Raw data: {}", json["data"].dump())) | ftxui::color(ftxui::Color::Cyan) | ftxui::hcenter,
|
|
ftxui::text(fmt::format("CRC: {}", json["crc"].dump())) | ftxui::color(ftxui::Color::Cyan) | ftxui::hcenter,
|
|
ftxui::text(fmt::format("Payload: {}", json.contains("payload_bytes") ? json["payload_bytes"].dump() : "[]")) | ftxui::color(ftxui::Color::Cyan) |
|
|
ftxui::hcenter,
|
|
|
|
ftxui::hbox({
|
|
ftxui::text("Value: ") | ftxui::bold,
|
|
ftxui::text(fmt::format("{:.6g}", json.contains("tag_value") ? json["tag_value"].template get<double>() : 0.0f)) |
|
|
ftxui::color(ftxui::Color::IndianRed),
|
|
}) | ftxui::hcenter,
|
|
});
|
|
});
|
|
|
|
Add(renderer);
|
|
}
|
|
|
|
private:
|
|
spn_settings_s m_settings_;
|
|
std::function<std::vector<uint8_t>()> m_data_fn_;
|
|
};
|
|
|
|
return ftxui::Make<Impl>(canid, settings, std::move(data_fn), screen, smap);
|
|
}
|