/
MaximU
/
application
Обзор
Документация
Войти
/
MaximU
/
application
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
application.cpp
844 строки
25 KB
Максим
Упорядочены #include-ы.
15 июл 2026, 22:13
15 июл 2026, 22:13
ee4913b
Код
Авторство
О чём код?
#include "application.h" #include <QMap> #include <QMessageBox> #include <QSettings> #include "applicationspecifics.h" #include "exchangeinfo.h" #include "profilerinfo.h" #include "replaywidget.h" LockingQueue<std::shared_ptr<char[]>> gInputPool; //!< Пул массивов входной информации. Используется для сокращения операций выделения памяти под массивы входной информации. LockingQueue<std::shared_ptr<char[]>> gPendingQueue; //!< Очередь заполненных массивов входной информации. LockingQueue<std::shared_ptr<char[]>> gInputQueue; //!< Очередь массивов входной информации, ожидающих обработки. LockingQueue<std::shared_ptr<char[]>> gOutputPool; //!< Пул массивов выходной информации. Используется для сокращения операций выделения памяти под массивы выходной информации. LockingQueue<std::shared_ptr<char[]>> gOutputQueue; //!< Очередь массивов выходной информации, ожидающих отправки. QList<ReplayWidget *> gReplayWidgetsList; //!< Список виджетов записи/воспроизведения. /*! * \brief Возвращает буфер массива входной информации для заполнения. * \return Буфер массива входной информации. */ std::shared_ptr<char[]> acquireInputBuffer() { std::shared_ptr<char[]> buffer; if (!gInputPool.isEmpty()) { buffer = gInputPool.dequeue(); } else { buffer = std::shared_ptr<char[]>(new char[InputSizeMax]); } return buffer; } /*! * \brief Помещает обработанный массив входной информации в пул буферов. * \param buffer Массив входной информации. */ void releaseInputBuffer(std::shared_ptr<char[]> buffer) { gInputPool.enqueue(buffer); } /*! * \brief Возвращает массив входной информации для обработки. * \return Массив входной информации для обработки. */ std::shared_ptr<char[]> acquireInputData() { return gInputQueue.dequeue(); } /*! * \brief Помещает заполненный буфер массива входной информации в очередь на ожидание обработки. * \param data Буфер массива входной информации */ void releaseInputData(std::shared_ptr<char[]> data) { gPendingQueue.enqueue(data); } /*! * \brief Достает из пула массивов выходной информации буфер, добавляет его в очередь массивов выходной информации, ожидающих отправки и возвращает для заполнения. * \param data Массив выходной информации. */ std::shared_ptr<char[]> acquireOutputBuffer() { std::shared_ptr<char[]> buffer; if (!gOutputPool.isEmpty()) { buffer = gOutputPool.dequeue(); } else { buffer = std::shared_ptr<char[]>(new char[OutputSizeMax]); } return buffer; } /*! * \brief Помещает обработанный массив выходной информации в пул буферов. * \param data Массив выходной информации. */ void releaseOutputBuffer(std::shared_ptr<char[]> data) { gOutputPool.enqueue(data); } /*! * \brief Помещает заполненный массив выходной информации в очередь на ожидание отправки. * \param data Массива выходной информации */ void releaseOutputData(std::shared_ptr<char[]> data) { gOutputQueue.enqueue(data); } /*! * \brief Конструктор. * \param name Название. * \param maxCount Максимальное количество хранимых порций информации. * \param altType Тип массва выходной информации, устанавливаемый для исторической информации. */ Application::History::History(const QString &name, int maxCount, int altType) : LockingQueue<std::shared_ptr<char[]>>(), mName(name), mMaxCount(maxCount), mAltType(altType) { } /*! * \brief Деструктор. */ Application::History::~History() { } /*! * \brief Возвращает название. * \return Название. */ QString Application::History::name() const { return mName; } /*! * \brief Возвращает максимальное количество хранимых массивов информации. * \return Максимальное количество хранимых массивов информации. */ int Application::History::maxCount() const { return mMaxCount; } /*! * \brief Возвращает тип массва выходной информации, устанавливаемый для исторической информации. * \return Тип массва выходной информации, устанавливаемый для исторической информации. */ int Application::History::altType() const { return mAltType; } /*! * \brief Помещает новый массив информации в историю, удаляя старый при достижении максимального количества хранимых массивов информации. * \param data Новые данные. * \return Избыточный буфер. */ std::shared_ptr<char[]> Application::History::push(std::shared_ptr<char[]> data) { std::shared_ptr<char[]> buffer = count() == mMaxCount ? dequeue() : nullptr; enqueue(data); return buffer; } /*! * \brief Конструктор. * \param id Идентификатор приложения. * \param argc * \param argv */ Application::Application(const QString &id, int &argc, char **argv) : QtSingleApplication(id, argc, argv) { setApplicationName(id); setApplicationVersion(QString(APP_VERSION)); } /*! * \brief Деструктор. */ Application::~Application() { } /*! * \brief Инициализирует приложение после проверки на открытые экземпляры приложения. */ void Application::initApplication() { createData(); createConnections(); readSettings(); // Чтение настроек должно выполняться после создания объектов (replays, threads, connections), до открытия соединений. } /*! * \brief Удаляет данные приложения. */ void Application::exitApplication() { writeSettings(); deleteConnections(); deleteData(); gInputPool.clear(); gPendingQueue.clear(); gInputQueue.clear(); gOutputPool.clear(); gOutputQueue.clear(); //// Удаление исторических данных. for (auto history : mHistoryTypeMap.values()) { delete history; } } /*! * \brief Возвращает путь к файлу настроек приложения. * \return Путь к файлу настроек приложения. */ QString Application::applicationCfgPath() { return QString("%1/%2").arg(applicationDirPath()).arg("application.cfg"); } /*! * \brief Восстанавливает настройки приложения. */ void Application::readSettings() { QSettings settings(applicationCfgPath(), QSettings::IniFormat); ///// Клиентские соединения. settings.beginGroup("ClientConnections"); for (ConnectionClient *client : mConnectionClients) { settings.beginGroup(client->label()); client->setAddress(settings.value("address", client->address()).toString()); client->setPort(settings.value("port", client->port()).toInt()); settings.endGroup(); } settings.endGroup(); //// Серверные соединения. settings.beginGroup("ServerConnections"); for (ConnectionServer *server : mConnectionServers) { settings.beginGroup(server->label()); server->setPort(settings.value("port", server->port()).toInt()); settings.endGroup(); } settings.endGroup(); //// readSettingsSpecific(settings); } /*! * \brief Сохраняет настройки приложения. */ void Application::writeSettings() { QSettings settings(applicationCfgPath(), QSettings::IniFormat); //// Клиентские соединения. settings.beginGroup("ClientConnections"); for (ConnectionClient *client : mConnectionClients) { settings.beginGroup(client->label()); settings.setValue("address", client->address()); settings.setValue("port", client->port()); settings.endGroup(); } settings.endGroup(); //// Серверные соединения. settings.beginGroup("ServerConnections"); for (ConnectionServer *server : mConnectionServers) { settings.beginGroup(server->label()); settings.setValue("port", server->port()); settings.endGroup(); } settings.endGroup(); //// writeSettingsSpecific(settings); } /*! * \brief Обновляет пользовательский интерфейс. */ void Application::updateUi() { emit ui(); } /*! * \brief Выводит строку в окно отображения истории операций и сохраняет её в файл истории. * \param string Отображаемая строка. * \param time Признак добавления времени. */ void Application::logString(const QString &string, bool time) { emit log(string, time); } /*! * \brief Создает данные программы. */ void Application::createData() { try { createDataSpecific(); } catch (...) { deleteDataSpecific(); QMessageBox::information(nullptr, applicationName(), tr("Ошибка при создании данных программы!\nВыполнение программы будет прервано.")); throw; } } /*! * \brief Удаляет накопленные данные. */ void Application::clearData() { clearDataSpecific(); } /*! * \brief Удаляет данные программы. */ void Application::deleteData() { deleteDataSpecific(); } /*! * \brief Создает сетевые и прочие соединения. */ void Application::createConnections() { createConnectionsSpecific(); } /*! * \brief Открывает соединения. */ void Application::startConnections() { //// Клиентские соединения. for (ConnectionClient *client : mConnectionClients) { if (client->property("autoConnection").toBool()) { client->setReconnect(true); client->open(); } } //// Серверные соединения. for (ConnectionServer *server : mConnectionServers) { if (server->property("autoConnection").toBool()) { server->open(); } } } /*! * \brief Закрывает соединения. */ void Application::deleteConnections() { //// Клиентские соединения. for (ConnectionClient *client : mConnectionClients) { client->blockSignals(true); } processEvents(); for (ConnectionClient *client : mConnectionClients) { delete client; } mConnectionClients.clear(); //// Серверные соединения. for (ConnectionServer *server : mConnectionServers) { delete server; } mConnectionServers.clear(); } /*! * \brief Включает сохранение исторических данных для указанного типа массива выходной информации. * \param type Тип массива выходной информации * \param name Название. * \param maxCount Максимальное количество сохраняемых массивов выходной информации. * \param newType Тип массва выходной информации, устанавливаемый для исторической информации. */ void Application::enableHistory(int type, const QString &name, int maxCount, int newType) { mHistoryTypeMap.insert(type, new History(name, maxCount, newType)); } /*! * \brief Возвращает список экземпляров информации истории выдачи массивов выходной информации. * \return Список экземпляров информации истории выдачи массивов выходной информации. */ QList<Application::History *> Application::histories() const { return mHistoryTypeMap.values(); } /*! * \brief Добавляет клиентское соединение. * \param client Добавляемое соединение. * \param types Типы массивов, выдаваемых соединением. * \param autoConnection Признак автоподключения при старте программы. */ void Application::addConnectionClient(ConnectionClient *client, const QList<int> types, bool autoConnection) { if (client->id() == Connection::IdNone) { int id = (mConnectionClients.size() + 1) << 0; client->setId(id); } mConnectionClients.append(client); connect(client, SIGNAL(connected(ConnectionClient *)), this, SLOT(slot_connection_connected(ConnectionClient *))); connect(client, SIGNAL(connecting(ConnectionClient *)), this, SLOT(slot_connection_connecting(ConnectionClient *))); connect(client, SIGNAL(disconnected(ConnectionClient *)), this, SLOT(slot_connection_disconnected(ConnectionClient *))); connect(client, SIGNAL(beginReadPortion(Connection *)), this, SLOT(slot_connection_beginReadPortion(Connection *))); connect(client, SIGNAL(endReadPortion(Connection *)), this, SLOT(slot_connection_endReadPortion(Connection *))); connect(client, SIGNAL(beginWritePortion(Connection *)), this, SLOT(slot_connection_beginWritePortion(Connection *))); connect(client, SIGNAL(endWritePortion(Connection *)), this, SLOT(slot_connection_endWritePortion(Connection *))); client->setProperty("autoConnection", autoConnection); if (!types.isEmpty()) { for (int type : types) { auto &list = mTypesMap[type]; list.append(client->id()); } auto &list = mTypesMapRev[client->id()]; list.append(types); } } /*! * \brief Добавляет серверное соединение. * \param server Добавляемое соединение. * \param types Типы массивов, выдаваемых соединением. * \param autoConnection Признак автоподключения при старте программы. */ void Application::addConnectionServer(ConnectionServer *server, const QList<int> types, bool autoConnection) { if (server->id() == Connection::IdNone) { int id = (mConnectionServers.size() + 1) << 16; server->setId(id); } mConnectionServers.append(server); connect(server, SIGNAL(connectionConnected(ConnectionClient *)), this, SLOT(slot_connection_connected(ConnectionClient *))); connect(server, SIGNAL(connectionDisconnected(ConnectionClient *)), this, SLOT(slot_connection_disconnected(ConnectionClient *))); connect(server, SIGNAL(beginReadPortion(Connection *)), this, SLOT(slot_connection_beginReadPortion(Connection *))); connect(server, SIGNAL(endReadPortion(Connection *)), this, SLOT(slot_connection_endReadPortion(Connection *))); connect(server, SIGNAL(beginWritePortion(Connection *)), this, SLOT(slot_connection_beginWritePortion(Connection *))); connect(server, SIGNAL(endWritePortion(Connection *)), this, SLOT(slot_connection_endWritePortion(Connection *))); server->setProperty("autoConnection", autoConnection); if (!types.isEmpty()) { for (int type : types) { auto &list = mTypesMap[type]; list.append(server->id()); } auto &list = mTypesMapRev[server->id()]; list.append(types); } } /*! * \brief Возвращает соединение по идентификатору. * \param id Идентификатор. * \return Соединение по идентификатору. */ Connection * Application::connectionById(int id) { int serverId = id & 0xFFFF0000; //!< Идентификатор серверного соединения. int clientId = id & 0x0000FFFF; //!< Идентификатор клиентского соединения. if (serverId != 0) { for (ConnectionServer *server : mConnectionServers) { if (server->id() == serverId) { if (clientId != 0) { for (ConnectionClient *client : server->connections()) { if (client->id() == clientId) { return client; } } return nullptr; } else { return server; } } } return nullptr; } if (serverId == 0 && clientId != 0) { for (ConnectionClient *client : mConnectionClients) { if (client->id() == clientId) { return client; } } return nullptr; } return nullptr; } /*! * \brief Возвращает указатель на соединение по метке. * \param label Метка. * \return Соединение по метке. */ Connection * Application::connectionByLabel(const QString &label) { for (ConnectionServer *server : mConnectionServers) { if (server->label() == label) { return server; } } for (ConnectionClient *client : mConnectionClients) { if (client->label() == label) { return client; } } return nullptr; } /*! * \brief Возвращает список клиентских соединений. * \return Список клиентских соединений. */ const QList<ConnectionClient *> & Application::connectionClients() { return mConnectionClients; } /*! * \brief Возвращает список серверных соединений. * \return Список серверных соединений. */ const QList<ConnectionServer *> & Application::connectionServers() { return mConnectionServers; } /*! * \brief Вызывается при очередной попытке подключении соединения. * \param connection Соединение. */ void Application::slot_connection_connecting(ConnectionClient *connection) { emit updateConnectionsTable(Connection::EventUpdate, connection->id()); } /*! * \brief Вызывается при подключении соединения к серверу. * \param connection Соединение. */ void Application::slot_connection_connected(ConnectionClient *connection) { emit updateConnectionsTable(Connection::EventConnect, connection->id()); connectionConnectedSpecific(connection); //// Выдача исторической информации. if (mTypesMapRev.contains(connection->id())) { for (int type : mTypesMapRev[connection->id()]) { if (mHistoryTypeMap.contains(type)) { for (auto data : *mHistoryTypeMap[type]) { connection->write(data.get(), reinterpret_cast<ExchangeInfo::Header *>(data.get())->length); } } } } } /*! * \brief Вызывается при отключения соединения от сервера. * \param connection Соединение. */ void Application::slot_connection_disconnected(ConnectionClient *connection) { emit updateConnectionsTable(Connection::EventDisconnect, connection->id()); connectionDisconnectedSpecific(connection); } /*! * \brief Вызывается в начале приема порции входной информации. * \param connection Соединение. */ void Application::slot_connection_beginReadPortion(Connection *connection) { emit updateConnectionsTable(Connection::EventUpdate, connection->id()); } /*! * \brief Вызывается по окончании приема порции входной информации.n * \param connection Соединение. */ void Application::slot_connection_endReadPortion(Connection *connection) { emit updateConnectionsTable(Connection::EventUpdate, connection->id()); while (!gPendingQueue.isEmpty()) { input(gPendingQueue.dequeue()); } } /*! * \brief Вызывается в начале записи порции входной информацииn * \param connection Соединение. */ void Application::slot_connection_beginWritePortion(Connection *connection) { emit updateConnectionsTable(Connection::EventUpdate, connection->id()); } /*! * \brief Вызывается по окончании записи порции входной информации. * \param connection Соединение. */ void Application::slot_connection_endWritePortion(Connection *connection) { emit updateConnectionsTable(Connection::EventUpdate, connection->id()); } /*! * \brief Вызывается по окончании чтения порции входной информации. */ void Application::slot_replay_endReadPortion() { while (!gPendingQueue.isEmpty()) { input(gPendingQueue.dequeue()); } } /*! * \brief Вызывается после завершения обработки массива входной информации.e * \param results Список кодов результата завершения цикла обработки информации. */ void Application::slot_workerThread_iterationDone(const QSet<int> &results) { if (!results.isEmpty()) { //// Пользовательская обработка выходной информации. iterationDoneSpecific(results); //// gOutputQueue.lock(); for (auto data: gOutputQueue) { //// Запись выходной информации. record(data); //// Выдача выходной информации. int type = reinterpret_cast<ExchangeInfo::Header *>(data.get())->type; int length = reinterpret_cast<ExchangeInfo::Header *>(data.get())->length; for (int id : mTypesMap[type]) { connectionById(id)->write(data.get(), length); } //// Сохранение исторической информации. if (mHistoryTypeMap.contains(type)) { History *history = mHistoryTypeMap[type]; reinterpret_cast<ExchangeInfo *>(data.get())->header.type = history->altType(); auto buffer = history->push(data); if (buffer != nullptr) { releaseOutputBuffer(buffer); } } } gOutputQueue.clear(); gOutputQueue.unlock(); } emit iterationDone(results); // Сигнал главному окну приложения. } /*! * \brief Вызывается при возникновении в потоке обработки исключения. * \param message Сообщение о контексте исключения. */ void Application::slot_workerThread_exception(const QString &message) { emit log(message, true); } /*! * \brief Вызывается при поступлении массива входной информации. * \param data Массив входной информации. */ void Application::input(std::shared_ptr<char[]> data) { emit updateProfilerTable(); //// Запись. record(data); //// Обработка. bool handled = handleSpecific(data); if (!handled) { //// Помещает полученный массив входной информации в очередь на обработку в WorkerThread. gInputQueue.enqueue(data); } else { //// Помещает обработанный массив входной информации в пул буферов. releaseInputBuffer(data); } } /*! * \brief Записвыает массив в файл. * \param data Массив входной информации. */ void Application::record(std::shared_ptr<char[]> data) { int type = reinterpret_cast<ExchangeInfo *>(data.get())->header.type; for (auto replay : gReplayWidgetsList) { if (replay->types().contains(type)) { ExchangeInfo *exchange = reinterpret_cast<ExchangeInfo *>(data.get()); replay->recordOnce(data.get(), exchange->header.length, exchange->header.time, exchange->header.offsetUTC); } } }