1
0
forked from BRT/arc
arc/services/EventHandler.cpp
trnsmkot 791eeeccd1 Refactor and enhance device and event handling logic
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.
2025-05-22 21:33:25 +07:00

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();
}