Files
utun2/tools/chatgui/transport/utun_node.h
T
evgeny ba1c4cf160 Add cross-platform BLE transport and Bluetooth settings
Integrate shared C transport with desktop and Android frontends, add radio probes and detailed Windows diagnostics, and fix GUI build dependencies and early configuration startup errors.
2026-10-29 10:51:19 +01:00

108 lines
7.9 KiB
C++

/* Desktop-владелец единственного встроенного ядра: читает INI, создаёт uasync и запускает core/chat в std::thread.
* [gui] utun_enabled=yes дополнительно запускает UTUN; tun_enabled управляет только созданием TUN.
* start подтверждает готовность сервисов и медиапривязок перед входом в poll.
* start/stop/настройки принадлежат GUI-потоку; настройки менять при остановленном узле.
* requestStop допускается из другого потока, stop ждёт worker и освобождает uasync вне callbacks.
* Worker уничтожает instance после подтверждённого stopping в GUI: читатели SQLite должны снять handle в его слотах.
* Идентичность, путь БД и NTP читаются через защищённую копию состояния перед стартом и после poll.
* instance — только заимствованный
* указатель для кода ядра: использовать в uasync, согласовав время жизни с stop/restart.
* Logger общий для процесса; путь файла принадлежит объекту до закрытия лога. */
#ifndef UTUN_NODE_H
#define UTUN_NODE_H
#include <QObject>
#include <QByteArray>
#include <QString>
#include <atomic>
#include <cstdint>
#include <future>
#include <condition_variable>
#include <mutex>
#include <thread>
#include "desktop_ble.h"
struct UTUN_INSTANCE;
struct sqlite3;
class UtunNode : public QObject {
Q_OBJECT
public:
// QObject принадлежит GUI; деструктор выполняет stop и закрывает собственный файл лога.
explicit UtunNode(QObject* parent = nullptr);
~UtunNode();
// Запускает сервисы по INI configPath; ждёт готовности. false — ошибка/уже запущен; см. startupError.
bool start(const QString& configPath);
// Причина последнего отказа start; читается в GUI-потоке после возврата start.
QString startupError() const { return m_startupError; }
void requestStop(); // Попросить выход из poll и разбудить его; не ждёт уничтожения ядра.
void stop(); // Идемпотентно запросить остановку, join worker и очистить опубликованное состояние.
// true от начала start до stop/отката; самостоятельный выход worker сначала сообщает stopped.
bool isRunning() const { return m_running; }
void setDebugFile(const QString& path); // Файл общего logger-а, append; пустой — файл задаёт конфиг ядра.
void setDbPath(const QString& path); // Переопределить db_path INI; создать каталог, ошибку записать в лог.
void setDebugLevel(const QString& level); // Общий уровень; пустой/неизвестный — info.
void setDebugCategories(const QString& categories); // Overrides cat=level,... поверх sys=info,bgp=info.
struct UTUN_INSTANCE* instance() const { return m_instance.load(); }
/* GUI: заимствованный handle, живой до stopping; после сигнала новые чтения запрещены.
* Слот stopping снимает handle без вызова stop/join. Worker ждёт завершения слотов перед destroy. */
struct sqlite3* databaseHandle() const;
QString databasePath() const; // Копия фактического каталога db_path ядра; пусто без instance.
// Копии данных ядра, безопасны вне uasync; до старта/после stop — пустые строки, 0/false.
QString nodeIdHex() const;
QString pubKeyHex() const;
int64_t ntpOffsetUs() const;
bool ntpSynced() const;
bool ntpEnabled() const;
DesktopBleSnapshot bluetoothSnapshot() const; // GUI: копия состояния радио и свежих обнаруженных узлов.
bool setBluetoothFlags(int flags); // GUI: 0..7; применить в uasync после пробуждения, INI сохраняет вызывающий.
ble_transport* bluetoothTransport() const; // Только uasync: заимствованный C API handle до выключения/stop.
signals:
void started(); // Синхронно в GUI после успешного start.
void stopping(); // GUI: освободить заимствованные данные; SQLite/ядро ещё живы до возврата слотов.
void stopped(); // Через Qt-очередь после освобождения ядра; событие прежнего запуска отсекается.
void error(const QString& msg); // Ошибка start; stopped при отказе запуска не посылается.
private:
void runLoop(std::promise<QString> startup);
void finalize(); // Только GUI после join: сброс моста, уничтожение uasync, очистка копии состояния.
void detachGuiReaders(); // GUI: stopping и подтверждение worker; идемпотентно в пределах запуска.
void updateSnapshot(const UTUN_INSTANCE* inst); // uasync читает ядро; NULL сбрасывает копию при finalize.
struct Snapshot {
QString nodeId, publicKey;
int64_t ntpOffsetUs = 0;
bool ntpSynced = false, ntpEnabled = false;
struct sqlite3* database = nullptr; // Заимствован до подтверждения stopping в GUI.
QString databasePath; // Каталог SQLite/media после применения GUI override.
DesktopBleSnapshot bluetooth;
} m_snapshot; // Копия для GUI; доступ под m_snapshotMutex, указателей ядра не содержит.
mutable std::mutex m_snapshotMutex;
std::atomic<UTUN_INSTANCE*> m_instance{nullptr}; // Публикуется после готовности; не продлевает жизнь ядра.
std::atomic<int> m_bluetoothFlags{0};
DesktopBle* m_bluetooth = nullptr; // Только uasync; объект живёт на стеке runLoop.
struct UASYNC* m_ua = nullptr; // Живёт до join; m_uaMutex защищает wakeup и откат неудачного старта.
std::mutex m_uaMutex;
std::thread m_thread; // Присоединяется GUI до повторного старта и уничтожения объекта.
std::atomic<bool> m_running{false}; // Узел владеет запущенным/останавливаемым worker.
std::atomic<bool> m_stop{false}; // Потокобезопасный запрос остановки.
std::atomic<bool> m_stopping{false}; // Закрывает публикацию handle перед ожиданием GUI.
std::mutex m_guiStopMutex; // Подтверждение отсоединения GUI до уничтожения SQLite.
std::condition_variable m_guiStopCondition;
bool m_guiDetached = true; // Под m_guiStopMutex; сбрасывается перед start.
uint64_t m_generation = 0; // Поколение запуска; GUI отбрасывает поздний stopped прежнего worker.
QString m_configPath;
QString m_startupError;
QString m_debugFile;
QByteArray m_debugLogPath; // Стабильный UTF-8 путь, заимствованный общим logger-ом.
QString m_dbPath;
QString m_debugLevel;
QString m_debugCategories;
};
#endif // UTUN_NODE_H