1
0
forked from BRT/arc
arc/services/DeviceScreener.h

109 lines
6.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <QObject>
#include <QNetworkAccessManager>
#include <QSet>
#include <QMap>
#include <dao/DeviceDAO.h>
#include <db/DeviceInfo.h>
class DeviceScreener final : public QObject {
Q_OBJECT
public:
explicit DeviceScreener(QObject *parent = nullptr);
~DeviceScreener() override;
static constexpr qint64 kOfflineDebounceSecs = 30;
// Как часто проверять/слать кадр истории экрана на сервер логов (с дедупом).
static constexpr qint64 kMonitoringScreenIntervalSecs = 30;
// Таймаут одной adb-команды в scanDeviceIo. На порядок выше нормального
// времени (<1с), но ограничивает зависшую команду на флапающем устройстве,
// чтобы один телефон не держал весь цикл опроса.
static constexpr int kAdbCommandTimeoutMs = 10000;
// Сколько устройств опрашивать по adb параллельно (фаза A). Ограничено, чтобы
// не насыщать USB-шину одновременными screencap'ами.
static constexpr int kMaxParallelScans = 6;
// Как часто перечитывать список установленных приложений (getListApps). Список
// меняется редко, поэтому не дёргаем adb каждый цикл.
static constexpr qint64 kAppsCheckIntervalSecs = 60;
public slots:
void start();
void stop();
signals:
void finished();
void firstPassDone();
void deviceWentOnline(const QString &deviceId);
void deviceWentOffline(const QString &deviceId);
private:
bool m_running = true;
bool m_firstScan = true;
QString m_urlAppUpdate;
QString m_token;
QString m_apiBase;
QNetworkAccessManager *m_manager = nullptr;
QMap<QString, qint64> m_lastApiUpdate; // deviceId → unix timestamp последнего PATCH
QSet<QString> m_onlineDevices; // дебаунс-онлайн (true online после dedup)
QMap<QString, qint64> m_offlineSince; // deviceId → когда пропал из ADB (для дебаунса)
QMap<QString, qint64> m_lastMonitoringScreen; // deviceId → unix ts последней проверки кадра для истории экрана
QMap<QString, QByteArray> m_lastSentScreenHash; // deviceId → sha256 последнего кадра, отправленного в историю экрана
// Кэш неизменных свойств устройства (id/name/android/размер экрана) по adbSerial.
// Эти поля не меняются за сессию, поэтому читаются по adb один раз, а не каждый
// проход цикла. Каждый цикл обновляется только battery. Инвалидируется при уходе
// устройства в OFFLINE (на случай переподключения другого аппарата по тому же serial).
QMap<QString, DeviceInfo> m_staticInfoCache;
QMap<QString, qint64> m_lastAppsCheck; // adbSerial → unix ts последнего getListApps
// Результат чистого adb-опроса одного устройства (фаза A, выполняется в пуле
// потоков). Не содержит ничего, что требует БД/членов класса.
struct ScanResult {
QString adbSerial;
DeviceInfo info; // идентичность + battery + размер экрана
bool identified = false; // false → android_id не прочитался, пропустить цикл
QByteArray image; // JPEG скриншот (может быть пустым)
QSet<QString> apps; // установленные пакеты (валидно только при appsRead)
bool appsRead = false; // true → getListApps вызывался в этом цикле
};
static QList<QPair<QString, QString> > readAdbDevices();
static QByteArray takeScreenshot(const QString &deviceId);
// Чистый adb-I/O одного устройства: потокобезопасно (не трогает БД и члены).
// cachedStatic — снимок ранее прочитанной статики (пустой id → читаем с нуля).
// readApps — перечитывать ли список установленных приложений в этом цикле.
static ScanResult scanDeviceIo(const QString &adbSerial, const DeviceInfo &cachedStatic, bool readApps);
void postDevicesData(const QList<DeviceInfo> &devices);
QJsonValue apiGet(const QString &path);
QJsonValue apiPost(const QString &path, const QJsonObject &body);
QString createDeviceOnApi(const DeviceInfo &device);
// Строит тело PATCH (с поиском bank_profile_id по БД — вызывать на главном
// потоке) и отправляет статус + опциональный скриншот fire-and-forget через
// dispatchDeviceSyncToApi, чтобы сетевой таймаут не тормозил цикл опроса.
void updateDeviceOnApi(const DeviceInfo &device, const QByteArray &image = {});
// Fire-and-forget отправка кадра истории экрана на сервер логов (monitoringBase).
// Создаёт временный NetworkService в отдельном потоке (паттерн LogArchiveSender).
static void dispatchScreenshotToMonitoring(const QString &desktopId,
const QString &deviceId,
const QString &name,
const QByteArray &jpeg);
// Fire-and-forget синхронизация устройства с основным API: PATCH статуса и
// (если image непустой) POST скриншота. Поднимает свой поток + QNetworkAccessManager,
// чтобы 30-секундный таймаут не блокировал цикл опроса. Всё необходимое
// (токен, тело запроса) передаётся по значению — БД здесь не трогается.
static void dispatchDeviceSyncToApi(const QString &apiBase,
const QString &accessToken,
const QString &apiId,
const QJsonObject &patchBody,
const QByteArray &image);
};