119 lines
7.9 KiB
C++
119 lines
7.9 KiB
C++
#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;
|
||
// Как часто снимать превью-кадр устройства (screencap). Раньше кадр снимался
|
||
// КАЖДЫЙ цикл (~1-2с на устройство) и при ~10 девайсах насыщал общий adb-сервер,
|
||
// конкурируя с uiautomator-дампами рабочих скриптов (FETCH_TRANSACTIONS) и
|
||
// приближая их к 10-минутному watchdog'у. Превью в UI и истории экрана так часто
|
||
// не нужно (отправка в мониторинг и так троттлится kMonitoringScreenIntervalSecs),
|
||
// поэтому захват ограничиваем. Online/offline-детект от кадра не зависит.
|
||
static constexpr qint64 kScreencapIntervalSecs = 4;
|
||
// Как часто перечитывать список установленных приложений (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
|
||
QMap<QString, qint64> m_lastScreencap; // adbSerial → unix ts последнего screencap (троттл превью)
|
||
|
||
// Результат чистого 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 — перечитывать ли список установленных приложений в этом цикле.
|
||
// takeShot — снимать ли превью-кадр (screencap) в этом цикле (троттл, см.
|
||
// kScreencapIntervalSecs). false → result.image пустой, превью не обновляется.
|
||
static ScanResult scanDeviceIo(const QString &adbSerial, const DeviceInfo &cachedStatic, bool readApps, bool takeShot);
|
||
|
||
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);
|
||
};
|