Refine error handling for "Interrupted" events, optimize screenshot throttling, and reduce redundant logs and heavy operations in ADB workflows.
This commit is contained in:
parent
0168cf7610
commit
fb9fa279a6
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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 — скриншот всегда, список приложений по троттлу.
|
||||
result.image = takeScreenshot(adbSerial);
|
||||
// Тяжёлый чистый 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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -363,22 +363,35 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
if (newStatus == EventStatus::Error) {
|
||||
// Используем скриншот из скрипта если есть, иначе снимаем новый
|
||||
QByteArray screenshot = m_screenshots.take(key);
|
||||
if (screenshot.isEmpty() && !event.deviceId.isEmpty()) {
|
||||
const DeviceInfo device = DeviceDAO::getDeviceById(event.deviceId);
|
||||
const QString adbSerial = device.adbSerial.isEmpty() ? event.deviceId : device.adbSerial;
|
||||
screenshot = AdbUtils::captureScreenshotBytes(adbSerial);
|
||||
}
|
||||
if (!screenshot.isEmpty()) {
|
||||
EventDAO::updateScreenshot(event.id, screenshot);
|
||||
}
|
||||
// Дублируем ошибку в general_logs со скриншотом
|
||||
const DeviceInfo logDevice = DeviceDAO::getDeviceById(event.deviceId);
|
||||
const QString deviceLabel = logDevice.name.isEmpty() ? event.deviceId : logDevice.name;
|
||||
const QString logMsg = QString("[%1] device=%2 bank=%3: %4")
|
||||
.arg(eventTypeToString(event.type), deviceLabel, event.bankName, result);
|
||||
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg, screenshot);
|
||||
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result, screenshot, event.id, event.transactionId, event.accountId);
|
||||
// "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;
|
||||
screenshot = AdbUtils::captureScreenshotBytes(adbSerial);
|
||||
}
|
||||
if (!screenshot.isEmpty()) {
|
||||
EventDAO::updateScreenshot(event.id, screenshot);
|
||||
}
|
||||
// Дублируем ошибку в general_logs со скриншотом
|
||||
const DeviceInfo logDevice = DeviceDAO::getDeviceById(event.deviceId);
|
||||
const QString deviceLabel = logDevice.name.isEmpty() ? event.deviceId : logDevice.name;
|
||||
const QString logMsg = QString("[%1] device=%2 bank=%3: %4")
|
||||
.arg(eventTypeToString(event.type), deviceLabel, event.bankName, result);
|
||||
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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user