Updated include paths for consistent structure under the `db` directory. Added `AccountInfoScreener` for parsing card data using Tesseract OCR and enhanced related models. Removed unused code and simplified main workflow.
165 lines
5.5 KiB
C++
165 lines
5.5 KiB
C++
#include "DeviceScreener.h"
|
||
|
||
#include <QRegularExpression>
|
||
#include <QProcess>
|
||
#include <QFile>
|
||
#include <QBuffer>
|
||
#include <QThread>
|
||
#include <QImage>
|
||
#include <dao/DeviceDAO.h>
|
||
#include <db/DeviceInfo.h>
|
||
|
||
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
||
}
|
||
|
||
DeviceScreener::~DeviceScreener() = default;
|
||
|
||
void DeviceScreener::start() {
|
||
qDebug() << "Worker started in thread:" << QThread::currentThread();
|
||
while (m_running) {
|
||
QList<QPair<QString, QString>> devices = readAdbDevices();
|
||
QStringList onlineIds;
|
||
for (const auto&[id, status] : devices) {
|
||
qDebug() << "Id:" << id << ", Status:" << status;
|
||
|
||
DeviceInfo device;
|
||
device.id = id;
|
||
|
||
if (status == "device") {
|
||
onlineIds << device.id;
|
||
device = readDeviceInfo(id);
|
||
takeScreenshot(id);
|
||
}
|
||
device.status = status;
|
||
device.updateTime = QDateTime::currentDateTime();
|
||
qDebug() << "Update device: " << DeviceDAO::upsertDevice(device);
|
||
}
|
||
qDebug() << "All devices offline: " << DeviceDAO::markDevicesOfflineExcept(onlineIds);
|
||
QThread::sleep(3);
|
||
}
|
||
emit finished();
|
||
}
|
||
|
||
QByteArray convertPngToJpeg(const QByteArray& pngData, const int quality = 90) {
|
||
QImage image;
|
||
if (!image.loadFromData(pngData, "PNG")) {
|
||
qWarning() << "Не удалось загрузить PNG из байт";
|
||
return {};
|
||
}
|
||
|
||
// if (!image.save("test.jpg", "JPG", quality)) {
|
||
// qWarning() << "Не удалось сохранить как JPG:" << "test.jpg";
|
||
// return {};
|
||
// }
|
||
|
||
QByteArray jpgData;
|
||
QBuffer buffer(&jpgData);
|
||
buffer.open(QIODevice::WriteOnly);
|
||
image.save(&buffer, "JPG", quality);
|
||
return jpgData;
|
||
}
|
||
|
||
bool DeviceScreener::takeScreenshot(const QString& deviceId) {
|
||
QProcess process;
|
||
QStringList arguments;
|
||
|
||
arguments << "-s" << deviceId << "exec-out" << "screencap" << "-p";
|
||
process.start("adb", arguments);
|
||
|
||
if (!process.waitForFinished(5000)) {
|
||
qWarning() << "ADB скриншот не завершился для" << deviceId;
|
||
return false;
|
||
}
|
||
|
||
const QByteArray imageData = process.readAllStandardOutput();
|
||
|
||
if (imageData.isEmpty()) {
|
||
qWarning() << "Скриншот пустой для " << deviceId;
|
||
return false;
|
||
}
|
||
|
||
// Обновляем изображение в БД
|
||
if (!DeviceDAO::updateImage(deviceId, convertPngToJpeg(imageData, 90))) {
|
||
qWarning() << "Не удалось записать изображение в БД для" << deviceId;
|
||
return false;
|
||
}
|
||
|
||
qDebug() << "Screenshot saved, device: " << deviceId;
|
||
return true;
|
||
}
|
||
|
||
QList<QPair<QString, QString>> DeviceScreener::readAdbDevices() {
|
||
QProcess process;
|
||
process.start("adb", QStringList() << "devices");
|
||
process.waitForFinished();
|
||
|
||
const QString output = process.readAllStandardOutput();
|
||
// qDebug() << "[adb devices output]:\n" << output;
|
||
QList<QPair<QString, QString>> devices;
|
||
const QStringList lines = output.split('\n');
|
||
for (int i = 1; i < lines.size(); ++i) {
|
||
QString line = lines.at(i).trimmed();
|
||
|
||
if (line.isEmpty()) {
|
||
continue;
|
||
}
|
||
/**
|
||
* device — устройство готово к использованию.
|
||
* offline — устройство подключено, но не может быть использовано.
|
||
* unauthorized — устройство не авторизовано для работы с ADB (нужно подтвердить запрос на подключение на самом устройстве).
|
||
* recovery — устройство в режиме восстановления.
|
||
*/
|
||
// Разделяем строку на id и type по табуляции
|
||
if (QStringList parts = line.split('\t'); parts.size() == 2) {
|
||
const QString& id = parts.at(0);
|
||
const QString& status = parts.at(1);
|
||
devices.append(qMakePair(id, status));
|
||
}
|
||
}
|
||
return devices;
|
||
}
|
||
|
||
static const QRegularExpression screenRegex(R"(Physical size: (\d+)x(\d+))");
|
||
static const QRegularExpression batteryRegex(R"(level:\s*(\d+))");
|
||
|
||
DeviceInfo DeviceScreener::readDeviceInfo(const QString& deviceId) {
|
||
DeviceInfo info;
|
||
info.id = deviceId;
|
||
info.screenWidth = 0;
|
||
info.screenHeight = 0;
|
||
info.battery = 0;
|
||
|
||
auto runAdbCommand = [&](const QStringList& args) -> QString {
|
||
QProcess process;
|
||
process.start("adb", QStringList{"-s", deviceId} + args);
|
||
process.waitForFinished();
|
||
return QString::fromUtf8(process.readAllStandardOutput()).trimmed();
|
||
};
|
||
|
||
info.name = runAdbCommand({"shell", "getprop", "ro.product.model"});
|
||
info.android = runAdbCommand({"shell", "getprop", "ro.build.version.release"});
|
||
|
||
// Размер экрана
|
||
const QString screenOutput = runAdbCommand({"shell", "wm", "size"});
|
||
QRegularExpressionMatch match = screenRegex.match(screenOutput);
|
||
if (match.hasMatch()) {
|
||
info.screenWidth = match.captured(1).toInt();
|
||
info.screenHeight = match.captured(2).toInt();
|
||
}
|
||
|
||
// Уровень батареи
|
||
const QString batteryOutput = runAdbCommand({"shell", "dumpsys", "battery"});
|
||
match = batteryRegex.match(batteryOutput);
|
||
if (match.hasMatch()) {
|
||
info.battery = match.captured(1).toInt();
|
||
}
|
||
|
||
info.updateTime = QDateTime::currentDateTime();
|
||
return info;
|
||
}
|
||
|
||
|
||
void DeviceScreener::stop() {
|
||
m_running = false;
|
||
}
|