1
0
forked from BRT/arc

Refine error handling for "Interrupted" events, optimize screenshot throttling, and reduce redundant logs and heavy operations in ADB workflows.

This commit is contained in:
slava 2026-06-02 18:25:25 +05:00
parent 0168cf7610
commit fb9fa279a6
4 changed files with 71 additions and 23 deletions

View File

@ -223,9 +223,15 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
m_finished = true;
if (m_folder.isEmpty()) return;
if (errorMessage.isEmpty()) {
// "Interrupted" трактуем как НЕ-ошибку для дампа: это следствие внешнего
// requestInterruption() (watchdog-таймаут или clearAll при остановке/выходе),
// а не самостоятельный сбой. Причину уже репортит инициатор прерывания
// (watchdog шлёт "Script timeout (N min)"), поэтому тяжёлый ZIP-дамп по
// Interrupted не шлём — иначе на один таймаут копится дубль log+dump.
if (errorMessage.isEmpty() || errorMessage == "Interrupted") {
QDir(m_folder).removeRecursively();
qDebug() << "[TaskDumpRecorder] success — removed" << m_folder;
qDebug() << "[TaskDumpRecorder]" << (errorMessage.isEmpty() ? "success" : "interrupted")
<< "— removed" << m_folder;
return;
}

View File

@ -355,10 +355,19 @@ void DeviceScreener::start() {
appsDue.insert(serial);
}
}
// С каких устройств пора снять превью-кадр (троттл screencap, см.
// kScreencapIntervalSecs) — чтобы не грузить adb каждый цикл.
QSet<QString> shotDue;
for (const QString &serial : deviceSerials) {
if (nowSec - m_lastScreencap.value(serial, 0) >= kScreencapIntervalSecs) {
shotDue.insert(serial);
}
}
const QList<ScanResult> scans = QtConcurrent::blockingMapped<QList<ScanResult> >(
&scanPool, deviceSerials,
[&cacheSnapshot, &appsDue](const QString &serial) {
return scanDeviceIo(serial, cacheSnapshot.value(serial), appsDue.contains(serial));
[&cacheSnapshot, &appsDue, &shotDue](const QString &serial) {
return scanDeviceIo(serial, cacheSnapshot.value(serial),
appsDue.contains(serial), shotDue.contains(serial));
});
// --- Фаза B: всё, что трогает БД/члены класса — последовательно в этом потоке.
@ -398,6 +407,12 @@ void DeviceScreener::start() {
liveSet.insert(device.id);
// Отмечаем попытку захвата превью (даже если кадр пустой) — чтобы не
// дёргать screencap каждый цикл на устройстве, где он подвисает.
if (shotDue.contains(scan.adbSerial)) {
m_lastScreencap[scan.adbSerial] = nowSec;
}
device.image = scan.image;
if (!device.image.isEmpty()) {
if (!DeviceDAO::updateImage(device.id, device.image)) {
@ -624,7 +639,7 @@ static const QRegularExpression batteryRegex(R"(level:\s*(\d+))");
DeviceScreener::ScanResult DeviceScreener::scanDeviceIo(const QString &adbSerial,
const DeviceInfo &cachedStatic,
bool readApps) {
bool readApps, bool takeShot) {
ScanResult result;
result.adbSerial = adbSerial;
@ -692,8 +707,12 @@ DeviceScreener::ScanResult DeviceScreener::scanDeviceIo(const QString &adbSerial
info.updateTime = QDateTime::currentDateTime();
result.info = info;
// Тяжёлый чистый adb-I/O — скриншот всегда, список приложений по троттлу.
// Тяжёлый чистый adb-I/O — превью-кадр и список приложений по троттлу.
// takeShot=false → кадр не снимаем (result.image пустой), фаза B просто не
// обновит превью в этом цикле; следующий захват — через kScreencapIntervalSecs.
if (takeShot) {
result.image = takeScreenshot(adbSerial);
}
if (readApps) {
result.apps = AdbUtils::getListApps(adbSerial);
result.appsRead = true;

View File

@ -24,6 +24,13 @@ public:
// Сколько устройств опрашивать по 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;
@ -57,6 +64,7 @@ private:
// устройства в 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, выполняется в пуле
// потоков). Не содержит ничего, что требует БД/членов класса.
@ -76,7 +84,9 @@ private:
// Чистый adb-I/O одного устройства: потокобезопасно (не трогает БД и члены).
// cachedStatic — снимок ранее прочитанной статики (пустой id → читаем с нуля).
// readApps — перечитывать ли список установленных приложений в этом цикле.
static ScanResult scanDeviceIo(const QString &adbSerial, const DeviceInfo &cachedStatic, bool 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);

View File

@ -363,6 +363,16 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
if (newStatus == EventStatus::Error) {
// Используем скриншот из скрипта если есть, иначе снимаем новый
QByteArray screenshot = m_screenshots.take(key);
// "Interrupted" — не самостоятельная ошибка, а следствие внешнего
// requestInterruption(): его выставляет либо watchdog (он уже шлёт
// "Script timeout (N min)" в startThreadForTask), либо clearAll() при
// остановке/выходе (намеренная остановка). Реальную причину репортит
// инициатор прерывания, поэтому здесь Interrupted на сервер НЕ
// дублируем — иначе на один таймаут выходит три записи в мониторинге
// (timeout-log + interrupted-log + interrupted-dump). Логику declined
// для CREATE_TRANSACTION ниже это не затрагивает.
if (result != "Interrupted") {
if (screenshot.isEmpty() && !event.deviceId.isEmpty()) {
const DeviceInfo device = DeviceDAO::getDeviceById(event.deviceId);
const QString adbSerial = device.adbSerial.isEmpty() ? event.deviceId : device.adbSerial;
@ -379,6 +389,9 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg, screenshot);
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result, screenshot, event.id, event.transactionId, event.accountId);
} else {
qDebug() << "[EventHandler] Interrupted — server error report skipped (cause already reported by interrupter), event id:" << event.id;
}
if (event.type == EventType::CreateTransaction) {
const MaterialInfo declAcc = MaterialDAO::getAccountById(event.accountId);