Files
cuframes-composer/include/cuframes_composer/cpp/mqtt_overlay.hpp
T
gx 88fa73f922 Phase 11b: подложка для text overlay + сразу-видимый placeholder
User: "у него есть полупрозрачная подложка? у него правильный z-order?"

Z-order был ок (overlays draw'аются после layout.render). Но без bg
текст плохо читался на пёстром кадре — и до прихода MQTT overlay был
visible=0 (не виден вообще).

Изменения:
  - overlay.h: cfc_overlay_text_config_t расширена bg_alpha / bg_y/u/v / bg_pad.
    bg_alpha=0 — фон отключён (default).
  - overlay.c draw_text: если bg_alpha>0, перед blit'ом текста рисуем fill
    rect (atlas_w+2*pad) × (atlas_h+2*pad) с заданным цветом и alpha.
  - overlay.c update_text: пробрасывает bg-поля при апдейте.
  - mqtt_overlay: MqttOverlayCfg + JSON loader научились читать bg_alpha,
    bg_y/u/v, bg_pad, placeholder. Default bg = чёрный 160 alpha, pad 10.
  - MqttOverlayItem::start: overlay сразу visible=1 с placeholder (default "—"),
    reposition_overlay вызывается до получения MQTT — placeholder
    позиционируется в anchor сразу.

User'у теперь видна тёмная подложка с текстом в правом-нижнем углу даже
если sensor молчит.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-04 10:19:29 +01:00

124 lines
4.3 KiB
C++

/* MqttOverlay — generic MQTT-driven text overlay (Phase 11b).
*
* Каждый overlay = одна MQTT-подписка + один persistent text overlay.
* Конфиг загружается из JSON-файла (mqtt_overlays.json):
* {
* "overlays": [
* { "id": "temp_outside",
* "topic": "zigbee2mqtt/Температура на улице",
* "json_field": "temperature", // если payload JSON; пусто — raw string
* "format": "%+.1f°C", // printf для extracted значения
* "anchor": "right-bottom", // right-top, left-bottom, ...
* "margin_x": 32, "margin_y": 24,
* "pixel_size": 32,
* "color": [255, 255, 255], "alpha": 230,
* "font_path": "/fonts/DejaVuSans-Bold.ttf"
* }, ...
* ]
* }
*
* Менеджер (MqttOverlayManager) держит vector<MqttOverlayItem>, поднимает
* MQTT-клиентов и добавляет overlays в композер. Hot-reload через
* reload_from_file() — пересоздаёт всех subscribers и overlays.
*
* Лицензия: LGPL-2.1+
*/
#ifndef CUFRAMES_COMPOSER_CPP_MQTT_OVERLAY_HPP
#define CUFRAMES_COMPOSER_CPP_MQTT_OVERLAY_HPP
#include "../overlay.h"
#include <atomic>
#include <memory>
#include <string>
#include <vector>
struct mosquitto;
struct mosquitto_message;
namespace cfc {
struct MqttOverlayCfg {
std::string id;
std::string topic;
std::string json_field; /* если payload JSON; пусто = raw string */
std::string format = "%s"; /* printf-formatted (для double — "%+.1f°C") */
std::string anchor = "right-bottom"; /* right-top, left-bottom, ... */
int margin_x = 32, margin_y = 24;
int pixel_size = 32;
int r = 255, g = 255, b = 255;
int alpha = 230;
std::string font_path = "/fonts/DejaVuSans-Bold.ttf";
/* Полупрозрачная подложка. bg_alpha=0 → отключено. */
int bg_alpha = 160;
int bg_y = 16, bg_u = 128, bg_v = 128; /* по умолчанию чёрный */
int bg_pad = 10;
/* Что показывать пока нет MQTT-данных. Пусто → overlay невидим до
* первого сообщения. По умолчанию "—" чтобы было видно что overlay
* жив, но данные ещё не пришли. */
std::string placeholder = "";
};
struct MqttBrokerCfg {
std::string host = "cctv-mosquitto";
int port = 1883;
std::string username;
std::string password;
};
class MqttOverlayItem {
public:
MqttOverlayItem(const MqttOverlayCfg& cfg, const MqttBrokerCfg& broker,
int frame_w, int frame_h);
~MqttOverlayItem();
MqttOverlayItem(const MqttOverlayItem&) = delete;
MqttOverlayItem& operator=(const MqttOverlayItem&) = delete;
bool start();
cfc_overlay_t* overlay() const { return overlay_; }
const std::string& id() const { return cfg_.id; }
private:
static void on_connect(struct mosquitto* m, void* user, int rc);
static void on_message(struct mosquitto* m, void* user,
const struct mosquitto_message* msg);
void handle_payload(const char* payload, std::size_t len);
void update_text(const std::string& text);
void reposition_overlay();
MqttOverlayCfg cfg_;
MqttBrokerCfg broker_;
int frame_w_, frame_h_;
struct mosquitto* mosq_ = nullptr;
cfc_overlay_t* overlay_ = nullptr;
std::atomic<bool> running_{false};
std::string last_text_;
};
class MqttOverlayManager {
public:
explicit MqttOverlayManager(const MqttBrokerCfg& broker) : broker_(broker) {}
~MqttOverlayManager() = default;
/* Загрузить overlays из JSON-файла. Возвращает количество созданных. */
int load_from_file(const std::string& path, int frame_w, int frame_h);
/* Pointers на overlays для регистрации в композере. */
std::vector<cfc_overlay_t*> overlay_handles() const;
void clear();
int size() const { return static_cast<int>(items_.size()); }
private:
MqttBrokerCfg broker_;
std::vector<std::unique_ptr<MqttOverlayItem>> items_;
};
} // namespace cfc
#endif /* CUFRAMES_COMPOSER_CPP_MQTT_OVERLAY_HPP */