#pragma once #include #include #include #include #include #include #include #include // ===== Depende do teu projeto ===== #include "core.hpp" #include "redis_publisher.hpp" // Se quiser habilitar nlohmann/json, remova o comentário: // #include // ======= Estruturas ======= struct SnapshotFreq { double pcl{ 0.0 }; double imu{ 0.0 }; }; struct SnapshotIMU { uint64_t timestamp = 0; double roll_deg{ 0.0 }; double pitch_deg{ 0.0 }; double yaw_deg{ 0.0 }; }; struct SnapshotStatus { double temperatura_c{ 0.0 }; uint32_t vezes_ligado{ 0 }; }; struct SnapshotDist { double frente{ 0.0 }; double tras{ 0.0 }; double esquerda{ 0.0 }; double direita{ 0.0 }; double sup{ 0.0 }; double inf{ 0.0 }; }; struct StatusSnapshot { uint64_t timestamp_ms{ 0 }; bool conectado{ true }; // placeholder por enquanto bool executando{ false }; bool sensor_ativo{ false }; std::string lidar_ip; std::string host_ip; std::string firmware_version; std::string dev_type; uint32_t handle{ 0 }; SnapshotFreq freq; SnapshotIMU imu; SnapshotStatus status; SnapshotDist dist; std::vector bboxes; static std::unique_ptr g_pub; static void start_publisher() { const std::string uri = "tcp://localhost:6379"; StatusSnapshot::g_pub = std::make_unique(uri); } // ===== Coletor: puxa dados do sistema e retorna um Snapshot pronto ===== static StatusSnapshot collect_now() { StatusSnapshot s{}; // tempo using namespace std::chrono; s.timestamp_ms = duration_cast(system_clock::now().time_since_epoch()).count(); // flags globais/atômicos do teu runtime s.conectado = Core::connected.load(std::memory_order_relaxed); s.executando = Core::running.load(std::memory_order_relaxed); s.sensor_ativo = Core::streaming.load(std::memory_order_relaxed); s.handle = Core::handle.load(std::memory_order_relaxed); s.host_ip = Core::get_host_ip(); s.lidar_ip = Core::get_lidar_ip(); s.firmware_version = Core::get_fw_version(); s.dev_type = Core::get_dev_type(); // freq (Hz). Se teus g_freq_* guardam Hz, só converte p/ double: s.freq.pcl = static_cast(Core::freq_pcl.load(std::memory_order_relaxed)); s.freq.imu = static_cast(Core::freq_imu.load(std::memory_order_relaxed)); // IMU (em graus) const auto att = Core::get_attitude(); s.imu.timestamp = att.timestamp; s.imu.roll_deg = Core::rad2deg(att.roll); s.imu.pitch_deg = Core::rad2deg(att.pitch); s.imu.yaw_deg = Core::rad2deg(att.yaw); // status (temperatura e contagem de ligadas) s.status.temperatura_c = Core::get_temperature_c(); s.status.vezes_ligado = Core::get_power_count(); // distâncias (ajusta os nomes conforme teu Distance6) const auto d = Core::get_distances(); s.dist.frente = d.front; s.dist.tras = d.back; s.dist.esquerda = d.left; s.dist.direita = d.right; s.dist.sup = d.up; s.dist.inf = d.down; // bboxes — adapta para tua fonte. Exemplo genérico: // Supondo que exista Core::get_bboxes() -> std::vector // com campos equivalentes. for (const auto& b : Core::get_bboxes()) { s.bboxes.push_back(std::move(b)); } return s; } // ===== Serialização manual (sem dependências) ===== std::string to_json_string() const { std::ostringstream os; os << std::fixed << std::setprecision(3); os << "{"; os << "\"timestamp\":" << timestamp_ms << ","; os << "\"conectado\":" << (conectado ? "true" : "false") << ","; os << "\"executando\":" << (executando ? "true" : "false") << ","; os << "\"sensor_ativo\":" << (sensor_ativo ? "true" : "false") << ","; os << "\"host_ip\":" << host_ip << ","; os << "\"lidar_ip\":" << lidar_ip << ","; os << "\"firmware_version\":" << firmware_version << ","; os << "\"dev_type\":" << dev_type << ","; os << "\"handle\":" << handle << ","; os << "\"freq\":{" << "\"pcl\":" << freq.pcl << "," << "\"imu\":" << freq.imu << "},"; os << "\"imu\":{" << "\"timestamp\":" << imu.timestamp << "," << "\"roll\":" << imu.roll_deg << "," << "\"pitch\":" << imu.pitch_deg << "," << "\"yaw\":" << imu.yaw_deg << "},"; os << "\"status\":{" << "\"temperatura\":" << status.temperatura_c << "," << "\"vezes_ligado\":" << status.vezes_ligado << "},"; os << "\"distancias\":{" << "\"frente\":" << dist.frente << "," << "\"tras\":" << dist.tras << "," << "\"esquerda\":" << dist.esquerda << "," << "\"direita\":" << dist.direita << "," << "\"sup\":" << dist.sup << "," << "\"inf\":" << dist.inf << "},"; os << "\"bboxes\":["; for (size_t i = 0; i < bboxes.size(); ++i) { const auto& b = bboxes[i]; os << "{" // limites da caixa << "\"min_x\":" << b.min_x << "," << "\"min_y\":" << b.min_y << "," << "\"min_z\":" << b.min_z << "," << "\"max_x\":" << b.max_x << "," << "\"max_y\":" << b.max_y << "," << "\"max_z\":" << b.max_z << "," // tempo e voxel info << "\"t_ms\":" << b.t_ms << "," << "\"voxels\":" << b.voxels << "," // centro e dimensões << "\"cx\":" << b.cx << "," << "\"cy\":" << b.cy << "," << "\"cz\":" << b.cz << "," << "\"w\":" << b.w << "," << "\"h\":" << b.h << "," << "\"d\":" << b.d << "," // distâncias derivadas << "\"dist_m\":" << b.dist_m << "," << "\"dist_xy_m\":" << b.dist_xy_m << "," << "\"frente_m\":" << b.frente_m // fecha objeto << "}"; if (i + 1 < bboxes.size()) os << ","; } os << "]"; os << "}"; return os.str(); } // --- 2.1 Compactador de BBoxes --- std::string bboxes_to_json_compact(std::size_t max_boxes) const { if (bboxes.empty()) return "[]"; // ordena por proximidade frontal (menor frente_m primeiro) std::vector v = bboxes; std::sort(v.begin(), v.end(), [](const BBox3D& a, const BBox3D& b) { return a.frente_m < b.frente_m; }); if (v.size() > max_boxes) v.resize(max_boxes); std::ostringstream os; os.setf(std::ios::fixed); os << std::setprecision(3); os << "["; for (std::size_t i = 0; i < v.size(); ++i) { const auto& b = v[i]; os << "{" << "\"min_x\":" << b.min_x << "," << "\"min_y\":" << b.min_y << "," << "\"min_z\":" << b.min_z << "," << "\"max_x\":" << b.max_x << "," << "\"max_y\":" << b.max_y << "," << "\"max_z\":" << b.max_z << "," << "\"t_ms\":" << b.t_ms << "," << "\"voxels\":" << b.voxels << "," << "\"cx\":" << b.cx << "," << "\"cy\":" << b.cy << "," << "\"cz\":" << b.cz << "," << "\"w\":" << b.w << "," << "\"h\":" << b.h << "," << "\"d\":" << b.d << "," << "\"dist_m\":" << b.dist_m << "," << "\"dist_xy_m\":" << b.dist_xy_m << "," << "\"frente_m\":" << b.frente_m << "}"; if (i + 1 < v.size()) os << ","; } os << "]"; return os.str(); } // --- 2.2 Publisher parcial (Hash + Stream) --- bool publish_state_partial(int ttl_sec_hash, std::size_t max_bboxes) const { if (!g_pub) { std::cerr << "[STATUS] RedisPublisher não inicializado\n"; return false; } // monta pares para o JSON (paths com '.') std::vector> J; J.reserve(32); auto addj_i = [&](std::string path, long long v) { J.emplace_back(std::move(path), std::to_string(v)); }; auto addj_b = [&](std::string path, bool v) { // booleans como true/false (sem aspas) viram bool real no JSON J.emplace_back(std::move(path), v ? "true" : "false"); }; auto addj_f = [&](std::string path, double v, int prec = 3) { std::ostringstream oss; oss.setf(std::ios::fixed); oss << std::setprecision(prec) << v; J.emplace_back(std::move(path), oss.str()); // número no JSON }; auto addj_s = [&](std::string path, const std::string& v) { // adiciona string com aspas no JSON std::ostringstream oss; oss << "\"" << v << "\""; J.emplace_back(std::move(path), oss.str()); }; // ---- mesmos campos que você já preencheu no HASH ---- addj_i("timestamp", static_cast(timestamp_ms)); addj_b("conectado", conectado); addj_b("executando", executando); addj_b("sensor_ativo", sensor_ativo); addj_s("host_ip", host_ip); addj_s("lidar_ip", lidar_ip); addj_s("firmware_version", Core::get_fw_version()); addj_s("dev_type", Core::get_dev_type()); addj_i("handle", static_cast(handle)); addj_f("freq.pcl", freq.pcl, 2); addj_f("freq.imu", freq.imu, 2); addj_f("imu.timestamp", imu.timestamp, 2); addj_f("imu.roll", imu.roll_deg, 2); addj_f("imu.pitch", imu.pitch_deg, 2); addj_f("imu.yaw", imu.yaw_deg, 2); addj_f("status.temperatura", status.temperatura_c, 2); addj_i("status.vezes_ligado", static_cast(status.vezes_ligado)); addj_f("dist.frente", dist.frente, 2); addj_f("dist.tras", dist.tras, 2); addj_f("dist.esquerda", dist.esquerda, 2); addj_f("dist.direita", dist.direita, 2); addj_f("dist.sup", dist.sup, 2); addj_f("dist.inf", dist.inf, 2); // opcional: bboxes como array JSON no snapshot (no mesmo documento) if (!bboxes.empty()) { std::string bb_json = bboxes_to_json_compact(max_bboxes); // deve ser JSON válido if (!bb_json.empty()) { // como o merge tenta json::parse(value), passar o array/objeto funciona direto J.emplace_back("bboxes", bb_json); // ex.: "[{...},{...}]" } } // grava/mescla no snapshot JSON (sem apagar outros campos já existentes) bool ok_json = g_pub->set_json_merge_paths("ctx:dados_modulo_Lvx", J, ttl_sec_hash); //std::cout << "OK_JSON: " << ok_json << "\n"; return ok_json; } bool publish_state_imu() { const auto att = Core::get_attitude(); std::vector> J; J.reserve(32); auto addj_i = [&](std::string path, long long v) { J.emplace_back(std::move(path), std::to_string(v)); }; auto addj_f = [&](std::string path, double v, int prec = 3) { std::ostringstream oss; oss.setf(std::ios::fixed); oss << std::setprecision(prec) << v; J.emplace_back(std::move(path), oss.str()); // número no JSON }; addj_i("timestamp", static_cast(att.timestamp)); addj_f("frequencia", att.frequencia, 4); addj_f("latencia", att.latencia, 4); addj_f("roll", Core::rad2deg(att.roll), 3); addj_f("pitch", Core::rad2deg(att.pitch), 3); addj_f("yaw", Core::rad2deg(att.yaw), 3); // grava/mescla no snapshot JSON (sem apagar outros campos já existentes) bool ok_json = g_pub->set_json_merge_paths("ctx:dados_modulo_Imu", J, 3); } };