Integrate device data management with database access
Added SQLite database support for managing device information and common data. Implemented DAOs for handling device and common data operations, such as inserting/updating devices and tracking the last scan time. Enhanced `DeviceScreener` to read device info, update database records, and take screenshots.
This commit is contained in:
parent
144a784166
commit
1672216bdc
@ -6,7 +6,7 @@ set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
|
||||
if(WIN32)
|
||||
if (WIN32)
|
||||
# Укажи путь к Qt6
|
||||
set(CMAKE_PREFIX_PATH "C:/Qt/6.9.0/msvc2022_arm64")
|
||||
list(APPEND CMAKE_PREFIX_PATH "C:/Users/User/.vcpkg-clion/vcpkg/installed/arm64-windows")
|
||||
@ -14,14 +14,22 @@ if(WIN32)
|
||||
set(Tesseract_DIR "C:/Users/User/.vcpkg-clion/vcpkg/installed/arm64-windows/share/tesseract")
|
||||
|
||||
find_package(Tesseract CONFIG REQUIRED)
|
||||
elseif(APPLE)
|
||||
elseif (APPLE)
|
||||
# Укажи путь к Qt6
|
||||
set(CMAKE_PREFIX_PATH "/opt/homebrew/opt/qt")
|
||||
include_directories("/opt/homebrew/opt/tesseract/include")
|
||||
link_directories("/opt/homebrew/opt/tesseract/lib")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS
|
||||
Widgets
|
||||
Sql
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE DB_SOURCES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/database/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/database/*.h"
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE VIEW_SOURCES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/views/*.cpp"
|
||||
@ -37,11 +45,13 @@ add_executable(ARCDesktopProject
|
||||
main.cpp
|
||||
${VIEW_SOURCES}
|
||||
${SERVICES_SOURCES}
|
||||
${DB_SOURCES}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
if (WIN32)
|
||||
target_link_libraries(ARCDesktopProject
|
||||
Qt6::Widgets
|
||||
Qt6::Sql
|
||||
Tesseract::libtesseract
|
||||
)
|
||||
|
||||
@ -49,9 +59,10 @@ if(WIN32)
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/images" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||
|
||||
elseif(APPLE)
|
||||
elseif (APPLE)
|
||||
target_link_libraries(ARCDesktopProject
|
||||
Qt6::Widgets
|
||||
Qt6::Sql
|
||||
tesseract
|
||||
)
|
||||
endif()
|
||||
endif ()
|
||||
BIN
database/app_data.db
Normal file
BIN
database/app_data.db
Normal file
Binary file not shown.
47
database/dao/common_data_dao.cpp
Normal file
47
database/dao/common_data_dao.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "common_data_dao.h"
|
||||
#include "../database_manager.h"
|
||||
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
|
||||
|
||||
QDateTime CommonDataDAO::getLastScan() {
|
||||
QSqlQuery query(DatabaseManager::instance().database());
|
||||
query.prepare("SELECT lastScan FROM common_data WHERE id = 1");
|
||||
|
||||
if (!query.exec()) {
|
||||
qWarning() << "Ошибка при получении lastScan:" << query.lastError().text();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (query.next()) {
|
||||
const QString timestamp = query.value(0).toString();
|
||||
return QDateTime::fromString(timestamp, Qt::ISODate);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool CommonDataDAO::updateLastScan(const QDateTime& newTime) {
|
||||
QSqlQuery query(DatabaseManager::instance().database());
|
||||
query.prepare("UPDATE common_data SET lastScan = :time WHERE id = 1");
|
||||
query.bindValue(":time", newTime.toString(Qt::ISODate));
|
||||
|
||||
if (!query.exec()) {
|
||||
qWarning() << "Ошибка при обновлении lastScan:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (query.numRowsAffected() == 0) {
|
||||
QSqlQuery insertQuery(DatabaseManager::instance().database());
|
||||
insertQuery.prepare("INSERT INTO common_data (id, lastScan) VALUES (1, :time)");
|
||||
insertQuery.bindValue(":time", newTime.toString(Qt::ISODate));
|
||||
|
||||
if (!insertQuery.exec()) {
|
||||
qWarning() << "Ошибка при INSERT lastScan:" << insertQuery.lastError().text();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
11
database/dao/common_data_dao.h
Normal file
11
database/dao/common_data_dao.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QDateTime>
|
||||
|
||||
class CommonDataDAO {
|
||||
public:
|
||||
[[nodiscard]] static QDateTime getLastScan();
|
||||
|
||||
[[nodiscard]] static bool updateLastScan(const QDateTime &newTime);
|
||||
};
|
||||
75
database/dao/device_dao.cpp
Normal file
75
database/dao/device_dao.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
#include "device_dao.h"
|
||||
#include <QSqlQuery>
|
||||
#include <QSqlError>
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
#include "../database_manager.h"
|
||||
|
||||
bool DeviceDAO::upsertDevice(const DeviceInfo& device) {
|
||||
QSqlQuery query(DatabaseManager::instance().database());
|
||||
|
||||
// Проверка: существует ли запись
|
||||
query.prepare("SELECT COUNT(*) FROM devices WHERE id = :id");
|
||||
query.bindValue(":id", device.id);
|
||||
|
||||
if (!query.exec() || !query.next()) {
|
||||
qWarning() << "Ошибка при проверке существования записи:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool exists = query.value(0).toInt() > 0;
|
||||
|
||||
if (exists) {
|
||||
// Обновление
|
||||
query.prepare(R"(
|
||||
UPDATE devices SET
|
||||
status = :status,
|
||||
name = :name,
|
||||
android = :android,
|
||||
screen_width = :width,
|
||||
screen_height = :height,
|
||||
battery = :battery,
|
||||
updateTime = :updateTime
|
||||
WHERE id = :id
|
||||
)");
|
||||
} else {
|
||||
// Вставка
|
||||
query.prepare(R"(
|
||||
INSERT INTO devices (
|
||||
id, status, name, android, screen_width, screen_height, battery, updateTime
|
||||
) VALUES (
|
||||
:id, :status, :name, :android, :width, :height, :battery, :updateTime
|
||||
)
|
||||
)");
|
||||
}
|
||||
|
||||
query.bindValue(":id", device.id);
|
||||
query.bindValue(":status", device.status);
|
||||
query.bindValue(":name", device.name);
|
||||
query.bindValue(":android", device.android);
|
||||
query.bindValue(":width", device.screenWidth);
|
||||
query.bindValue(":height", device.screenHeight);
|
||||
query.bindValue(":battery", device.battery);
|
||||
query.bindValue(":updateTime", device.updateTime.toString(Qt::ISODate));
|
||||
|
||||
if (!query.exec()) {
|
||||
qWarning() << "Ошибка при вставке/обновлении устройства:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeviceDAO::updateImage(const QString& deviceId, const QByteArray& imageData) {
|
||||
QSqlQuery query(DatabaseManager::instance().database());
|
||||
query.prepare("UPDATE devices SET image = :image WHERE id = :id");
|
||||
query.bindValue(":image", imageData);
|
||||
query.bindValue(":id", deviceId);
|
||||
|
||||
if (!query.exec()) {
|
||||
qWarning() << "Ошибка при обновлении изображения:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
22
database/dao/device_dao.h
Normal file
22
database/dao/device_dao.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QSqlDatabase>
|
||||
|
||||
struct DeviceInfo {
|
||||
QString id;
|
||||
QString status;
|
||||
QString name;
|
||||
QString android;
|
||||
int screenWidth;
|
||||
int screenHeight;
|
||||
int battery;
|
||||
QDateTime updateTime;
|
||||
};
|
||||
|
||||
class DeviceDAO {
|
||||
public:
|
||||
[[nodiscard]] static bool upsertDevice(const DeviceInfo &device);
|
||||
|
||||
[[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData);
|
||||
};
|
||||
26
database/database_manager.cpp
Normal file
26
database/database_manager.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "database_manager.h"
|
||||
#include <QSqlError>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
|
||||
DatabaseManager::DatabaseManager() {
|
||||
db = QSqlDatabase::addDatabase("QSQLITE");
|
||||
db.setDatabaseName(QCoreApplication::applicationDirPath() + "/database/app_data.db");
|
||||
if (!db.open()) {
|
||||
qWarning() << "Ошибка открытия базы данных:" << db.lastError().text();
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseManager::~DatabaseManager() {
|
||||
if (db.isOpen()) {
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseManager& DatabaseManager::instance() {
|
||||
static DatabaseManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
QSqlDatabase& DatabaseManager::database() {
|
||||
return db;
|
||||
}
|
||||
16
database/database_manager.h
Normal file
16
database/database_manager.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <QSqlDatabase>
|
||||
|
||||
class DatabaseManager final : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static DatabaseManager& instance();
|
||||
|
||||
QSqlDatabase& database();
|
||||
|
||||
private:
|
||||
DatabaseManager();
|
||||
~DatabaseManager() override;
|
||||
QSqlDatabase db;
|
||||
};
|
||||
45
main.cpp
45
main.cpp
@ -2,6 +2,9 @@
|
||||
#include <QThread>
|
||||
#include "views/MyWindow.h"
|
||||
#include <tesseract/baseapi.h>
|
||||
|
||||
#include "database/database_manager.h"
|
||||
#include "database/dao/common_data_dao.h"
|
||||
#include "services/device_screener.h"
|
||||
|
||||
void setupWorkerAndThread(QCoreApplication& app) {
|
||||
@ -42,26 +45,32 @@ int main(int argc, char *argv[]) {
|
||||
// Setup worker and thread
|
||||
setupWorkerAndThread(app);
|
||||
|
||||
const QDateTime current = QDateTime::currentDateTimeUtc();
|
||||
qDebug() << "Обновление lastScan:" << CommonDataDAO::updateLastScan(current);
|
||||
|
||||
// tesseract, tesseract-lang
|
||||
QImage image("./images/img.png");
|
||||
if (image.isNull()) {
|
||||
qDebug() << "Cant load the image";
|
||||
return -1;
|
||||
}
|
||||
const QDateTime savedTime = CommonDataDAO::getLastScan();
|
||||
qDebug() << "Сохранившееся время:" << savedTime.toString();
|
||||
|
||||
image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
|
||||
tesseract::TessBaseAPI tess;
|
||||
if (tess.Init(nullptr, "rus")) {
|
||||
qDebug() << "Ошибка инициализации Tesseract с русским языком....";
|
||||
return -1;
|
||||
}
|
||||
|
||||
tess.SetImage(image.bits(), image.width(), image.height(), 1, image.bytesPerLine());
|
||||
|
||||
QString result = QString::fromUtf8(tess.GetUTF8Text());
|
||||
qDebug() << "Распознанный текст:" << result;
|
||||
//
|
||||
// // tesseract, tesseract-lang
|
||||
// QImage image("./images/img.png");
|
||||
// if (image.isNull()) {
|
||||
// qDebug() << "Cant load the image";
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// image = image.convertToFormat(QImage::Format_Grayscale8);
|
||||
//
|
||||
// tesseract::TessBaseAPI tess;
|
||||
// if (tess.Init(nullptr, "rus")) {
|
||||
// qDebug() << "Ошибка инициализации Tesseract с русским языком....";
|
||||
// return -1;
|
||||
// }
|
||||
//
|
||||
// tess.SetImage(image.bits(), image.width(), image.height(), 1, image.bytesPerLine());
|
||||
//
|
||||
// QString result = QString::fromUtf8(tess.GetUTF8Text());
|
||||
// qDebug() << "Распознанный текст:" << result;
|
||||
|
||||
|
||||
MyWindow window;
|
||||
|
||||
@ -1,20 +1,140 @@
|
||||
#include "device_screener.h"
|
||||
#include <QDebug>
|
||||
#include <QThread>
|
||||
|
||||
DeviceScreener::DeviceScreener(QObject* parent) : QObject(parent) {}
|
||||
#include <QRegularExpression>
|
||||
#include <QProcess>
|
||||
#include <QFile>
|
||||
#include <QThread>
|
||||
#include <../../database/dao/device_dao.h>
|
||||
|
||||
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
DeviceScreener::~DeviceScreener() = default;
|
||||
|
||||
void DeviceScreener::start() {
|
||||
qDebug() << "Worker started in thread:" << QThread::currentThread();
|
||||
while (m_running) {
|
||||
qDebug() << "Running in background...";
|
||||
QThread::sleep(2);
|
||||
QList<QPair<QString, QString>> devices = readAdbDevices();
|
||||
for (const auto&[id, status] : devices) {
|
||||
qDebug() << "Id:" << id << ", Status:" << status;
|
||||
|
||||
DeviceInfo device;
|
||||
device.id = id;
|
||||
device.status = status;
|
||||
device.updateTime = QDateTime::currentDateTime();
|
||||
|
||||
if (status == "device") {
|
||||
device = readDeviceInfo(id);
|
||||
takeScreenshot(id);
|
||||
}
|
||||
|
||||
qDebug() << "Update device: " << DeviceDAO::upsertDevice(device);
|
||||
}
|
||||
QThread::sleep(5);
|
||||
}
|
||||
emit finished();
|
||||
}
|
||||
|
||||
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, imageData)) {
|
||||
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;
|
||||
}
|
||||
@ -1,19 +1,29 @@
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <../../database/dao/device_dao.h>
|
||||
|
||||
class DeviceScreener final : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DeviceScreener(QObject* parent = nullptr);
|
||||
explicit DeviceScreener(QObject *parent = nullptr);
|
||||
|
||||
~DeviceScreener() override;
|
||||
|
||||
public slots:
|
||||
public slots:
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
signals:
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
bool m_running = true;
|
||||
|
||||
static QList<QPair<QString, QString> > readAdbDevices();
|
||||
|
||||
static bool takeScreenshot(const QString &deviceId);
|
||||
|
||||
static DeviceInfo readDeviceInfo(const QString &deviceId);
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user