#include #include #include "dao/AccountDAO.h" #include "views/MainWindow.h" #include "database/DatabaseManager.h" #include "database/dao/CommonDataDAO.h" #include "database/dao/DeviceDAO.h" #include "services/DeviceScreener.h" #include #include #include #include #include #include #include "EventHandler.h" #include "net/NetworkService.h" #include "widget/loader/ErrorWindow.h" #include "widget/loader/LoaderWindow.h" #include "widget/loader/RegistrationWindow.h" #include void startDeviceScreener(const QCoreApplication &app) { auto *thread = new QThread(); auto *deviceScreenerWorker = new DeviceScreener; // Перемещаем worker в новый поток thread. // Это значит, что все слоты worker, вызываемые через connect(), теперь будут исполняться в этом фоне-потоке. deviceScreenerWorker->moveToThread(thread); // Когда поток запустится, автоматически вызовется worker->start() внутри фонового потока. QObject::connect(thread, &QThread::started, deviceScreenerWorker, &DeviceScreener::start); // 🔁 Эти 3 строки делают автоматическую зачистку: // Когда worker закончит работу (emit finished()), мы: // - останавливаем поток (thread->quit()) // - удаляем worker (deleteLater) // - удаляем thread (deleteLater) // Это защищает от утечек памяти и оставшихся потоков после завершения. QObject::connect(deviceScreenerWorker, &DeviceScreener::finished, thread, &QThread::quit); QObject::connect(deviceScreenerWorker, &DeviceScreener::finished, deviceScreenerWorker, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); // Когда пользователь закрывает приложение (Ctrl+C, exit() и т.п.), мы вызываем worker->stop() — ты сам пишешь, что в этом методе. //Обычно он просто ставит флаг m_running = false, чтобы остановить бесконечный цикл в start() QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() { deviceScreenerWorker->stop(); }); thread->start(); } void startEventHandler(const QCoreApplication &app) { auto *thread = new QThread; auto *eventHandler = new EventHandler; eventHandler->moveToThread(thread); QObject::connect(thread, &QThread::started, eventHandler, &EventHandler::start); QObject::connect(eventHandler, &EventHandler::finished, thread, &QThread::quit); QObject::connect(eventHandler, &EventHandler::finished, eventHandler, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() { eventHandler->stop(); }); thread->start(); } void startNetworkService(const QCoreApplication &app) { auto *paymentThread = new QThread; auto *networkService = new NetworkService; networkService->moveToThread(paymentThread); QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::start); QObject::connect(networkService, &NetworkService::finished, paymentThread, &QThread::quit); QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater); QObject::connect(paymentThread, &QThread::finished, paymentThread, &QThread::deleteLater); QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() { networkService->stop(); }); // Сигнал эмитируется из фонового потока → Qt::QueuedConnection доставит его в главный поток QObject::connect(networkService, &NetworkService::tokenRefreshFailed, qApp, []() { QMessageBox *box = new QMessageBox( QMessageBox::Critical, "Ошибка авторизации", "Не удалось обновить токен авторизации.\n\n" "Проверьте подключение к интернету.\n" "Приложение продолжит попытки автоматически.", QMessageBox::Ok ); box->setAttribute(Qt::WA_DeleteOnClose); box->show(); }, Qt::QueuedConnection); paymentThread->start(); } static QFile logFile; static QMutex logMutex; void messageHandler( const QtMsgType type, const QMessageLogContext &context, const QString &msg ) { Q_UNUSED(context) // можно раскомментировать, если нужны file/line/function // Формируем строку с типом и временем const QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz"); QString level; switch (type) { case QtDebugMsg: level = "DEBUG"; break; case QtInfoMsg: level = "INFO"; break; case QtWarningMsg: level = "WARNING"; break; case QtCriticalMsg: level = "CRITICAL"; break; case QtFatalMsg: level = "FATAL"; break; } const QString line = QString("%1 [%2] [%3:%4] %5\n") .arg(timestamp, level, context.function, QString::number(context.line), msg); // Записываем в файл (потокобезопасно) { QMutexLocker locker(&logMutex); if (logFile.isOpen()) { QTextStream(&logFile) << line; logFile.flush(); } } // По желанию — дублировать в консоль const QByteArray localMsg = line.toLocal8Bit(); fprintf(stderr, "%s", localMsg.constData()); if (type == QtFatalMsg) { abort(); } } int main(int argc, char *argv[]) { QApplication app(argc, argv); // Выбрать стиль Fusion QApplication::setStyle(QStyleFactory::create("Fusion")); logFile.setFileName("application.log"); if (!logFile.open(QIODevice::Append | QIODevice::Text)) { qWarning() << "Не удалось открыть лог-файл для записи"; } // Устанавливаем свой обработчик qInstallMessageHandler(messageHandler); // Миграции БД (добавляем новые колонки если их ещё нет) DeviceDAO::migrate(); // 1. Если есть api_key — логинимся и проверяем/создаём desktop // - ошибка сети или сервера [code -1] // - неверный api_key [code 0] — показываем окно ввода // - всё ок [code 1] — запускаем приложение // 2. Если нет api_key — показываем окно для ввода const QSettings settings("config.ini", QSettings::IniFormat); const QString apiKey = settings.value("common/api_key").toString(); auto showRegistration = [&app]() { auto *registrationWindow = new RegistrationWindow(); registrationWindow->show(); QObject::connect( registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow, [registrationWindow, &app]() { auto *mainWindow = new MainWindow(); mainWindow->show(); registrationWindow->close(); startNetworkService(app); startDeviceScreener(app); startEventHandler(app); }, Qt::QueuedConnection); }; if (apiKey.isEmpty()) { showRegistration(); } else { auto *loader = new LoaderWindow(); loader->show(); QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app, showRegistration](const int result) { if (result == -1) { auto *errorWindow = new ErrorWindow( "Нет подключения к интернету или сервер не отвечает.\n\nПожалуйста, попробуйте позже." ); errorWindow->show(); } else if (result == 1) { auto *mainWindow = new MainWindow(); mainWindow->show(); startNetworkService(app); startDeviceScreener(app); startEventHandler(app); } else if (result == 0) { QSettings s("config.ini", QSettings::IniFormat); s.remove("common/api_key"); showRegistration(); } loader->close(); }, Qt::QueuedConnection); } return QApplication::exec(); }