Refactored `DeviceScreener`, `NetworkService`, and event-related logic for improved maintainability and flexibility. Introduced `EventHandler` for thread management and made changes to include configurable URLs via `config.ini`. Modified database schema to support device-level event linking.
131 lines
4.0 KiB
C++
131 lines
4.0 KiB
C++
#include "EventHandler.h"
|
|
|
|
#include <QMap>
|
|
#include <QThread>
|
|
#include <QTimer>
|
|
|
|
#include "dao/AccountDAO.h"
|
|
#include "dao/DeviceDAO.h"
|
|
#include "dao/EventDAO.h"
|
|
#include "rshb/CommonScript.h"
|
|
#include "rshb/PayByPhoneScript.h"
|
|
|
|
EventHandler::EventHandler(QObject *parent) : QObject(parent) {
|
|
m_checkTimer = new QTimer(this);
|
|
m_checkTimer->setInterval(1000);
|
|
connect(m_checkTimer, &QTimer::timeout, this, &EventHandler::checkAndRestart);
|
|
}
|
|
|
|
EventHandler::~EventHandler() = default;
|
|
|
|
void EventHandler::start() {
|
|
m_devices = DeviceDAO::getOnlineDeviceCodes();
|
|
|
|
clearAll();
|
|
// Запустим сразу потоки для всех ключей
|
|
for (auto &key: m_devices) {
|
|
startThreadForKey(key);
|
|
}
|
|
|
|
m_checkTimer->start();
|
|
}
|
|
|
|
void EventHandler::checkAndRestart() {
|
|
if (!m_running) {
|
|
return;
|
|
}
|
|
m_devices = DeviceDAO::getOnlineDeviceCodes();
|
|
|
|
for (const auto &key: m_devices) {
|
|
const QThread *thr = m_threads.value(key, nullptr);
|
|
if (!thr || !thr->isRunning()) {
|
|
startThreadForKey(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EventHandler::startThreadForKey(const QString &key) {
|
|
const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(key);
|
|
if (event.id == -1) {
|
|
return;
|
|
}
|
|
if (!EventDAO::updateEvent(event.id, EventStatus::InProgress)) {
|
|
qCritical() << "Cant update event: " << event.id;
|
|
return;
|
|
}
|
|
|
|
qDebug() << "Thread for key" << key << "running";
|
|
// FIXME пока это только оплата,
|
|
const AccountInfo account = AccountDAO::getAccountById(event.accountId);
|
|
const auto thr = new QThread(this);
|
|
auto *worker = new PayByPhoneScript(account, event.phone, event.bankName, event.amount);
|
|
|
|
worker->moveToThread(thr);
|
|
connect(thr, &QThread::started, worker, &PayByPhoneScript::start);
|
|
connect(worker, &PayByPhoneScript::finished, thr, &QThread::quit);
|
|
connect(worker, &PayByPhoneScript::finished, worker, &QObject::deleteLater);
|
|
connect(thr, &QThread::finished, thr, &QObject::deleteLater);
|
|
|
|
// При нормальном завершении — перезапустить новый поток
|
|
connect(worker, &PayByPhoneScript::finished, this,
|
|
[this, key]() {
|
|
// qDebug() << "Thread finished for key" << key << "- restarting.";
|
|
if (const auto tm = m_timers.take(key)) {
|
|
tm->stop();
|
|
tm->deleteLater();
|
|
}
|
|
m_threads.remove(key);
|
|
});
|
|
|
|
// Таймер на 5 минут — если не успел finish(), убить и перезапустить
|
|
const auto timer = new QTimer(this);
|
|
timer->setSingleShot(true);
|
|
connect(timer, &QTimer::timeout, this, [this, key]() {
|
|
qWarning() << "Timeout for key" << key << "- killing stuck thread.";
|
|
if (QThread *thread = m_threads.value(key); thread && thread->isRunning()) {
|
|
thread->requestInterruption(); // мягкое прерывание
|
|
if (!thread->wait(500)) {
|
|
qWarning() << "Hard terminate for key" << key;
|
|
thread->terminate();
|
|
thread->wait();
|
|
}
|
|
}
|
|
if (const auto tm = m_timers.take(key)) {
|
|
tm->deleteLater();
|
|
}
|
|
m_threads.remove(key);
|
|
});
|
|
|
|
// Сохраняем и запускаем
|
|
m_threads[key] = thr;
|
|
m_timers[key] = timer;
|
|
|
|
timer->start(5 * 60 * 1000);
|
|
thr->start();
|
|
}
|
|
|
|
void EventHandler::clearAll() {
|
|
// Останавливаем и удаляем старые
|
|
for (QThread *thread: m_threads) {
|
|
if (thread->isRunning()) {
|
|
thread->requestInterruption();
|
|
thread->terminate();
|
|
thread->wait();
|
|
}
|
|
thread->deleteLater();
|
|
}
|
|
m_threads.clear();
|
|
|
|
for (QTimer *timer: m_timers) {
|
|
timer->stop();
|
|
timer->deleteLater();
|
|
}
|
|
m_timers.clear();
|
|
}
|
|
|
|
void EventHandler::stop() {
|
|
m_running = false;
|
|
m_checkTimer->stop();
|
|
clearAll();
|
|
}
|