/
breton75
/
modus2
Обзор
Документация
Войти
/
breton75
/
modus2
Код
Запросы
0
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
utils/mdserver/src/main.cpp
809 строк
25 KB
s.sviridov
Update sv_can.h and main.cpp
04 мар 2026, 19:52
04 мар 2026, 19:52
fae3b6a
Код
Авторство
О чём код?
#include <QCoreApplication> #include <QList> #include <QLibrary> #include "stdio.h" #include <QTextCodec> #include <QFile> #include <QtNetwork/QHostAddress> #include <QProcess> #include <QDir> #include <QPair> #include <QFileInfo> #include <iostream> #include <signal.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <QJsonDocument> #include <QJsonObject> #include <QJsonValue> //#include "../../../framework/modus.h" //#include "../../../framework/dbus/sv_dbus.h" #include "../../../framework/global/configuration.h" #include "../../../framework/global/defs.h" #include "../../../framework/global/entities.h" #include "../../../framework/global/event.h" #include "../../../../../svlib/Auxiliary/1.0/auxiliary.h" //#include "../../../../libs/control/2.1/src/sv_control_server.h" #include "control_server.h" modus::Configuration CONFIGURATION; modus::Entities ENTITIES; modus::ControlServer* CONTROL = nullptr; modus::MasterLogger* MASTERLOGGER = nullptr; //modus::DBus dbus; modus::Token me = modus::Token(modus::eMain, QString("main")); const OptionStructList AppOptions = { {{OPTION_DEBUG}, "Режим отладки","", "", ""}, {{OPTION_CONFIG_FILE}, "Файл конфигурации сервера","config.json", "", ""}, {{OPTION_LOG_LEVEL}, "Уровень вывода сообщений при запуске сервера","info", "", ""}, {{OPTION_CASE}, "При запуске, добавляет в конфигурацию проекта параметр 'case'", " ", "", ""}, }; bool initConfig(modus::AppConfig &appcfg); bool readModules(const modus::AppConfig& appcfg); bool readSignals(const modus::AppConfig& appcfg); bool initModules(); void initControl(); void closeModules(); void deleteSignals(); void signal_handler(int sig); SvResult execute(const QString& command) { QProcess p; p.start(command, QIODevice::ReadOnly); if(!p.waitForStarted()) return SvResult(SvResult::Error, p.errorString()); if(!p.waitForFinished()) return SvResult(SvResult::Error, p.errorString()); SvResult result = SvResult(SvResult::OK, QString(p.readAll())); p.close(); return result; } QList<pid_t> get_pids_by_name(const QString& name, pid_t exclude = 0) { QList<pid_t> result = QList<pid_t>(); SvResult e = execute(QString("ps -C %1 -o pid=").arg(name)); if(e.type != SvResult::OK) { qCritical() << e.text; return result; } QString pids = e.text; bool ok; for(QString pid: pids.split('\n')) { if(pid.trimmed().isEmpty()) continue; pid_t upid = pid.toInt(&ok); if(ok && upid != exclude) result.append(upid); } return result; } struct AppConfig { bool start = false; bool stop = false; bool restart = false; bool status = false; bool twin = false; bool debug = false; QString config_file_name = QString(); QString start_log_level = QString(); QString case_value = QString(); QString paths = QString(); }; int server_operate(const QStringList& args, modus::AppConfig& cfg) { try { //! разбираем операцию SvCommandLineParser op_parser; op_parser.addPositionalArgument(APP_OPERATION, "Команда управления сервером start|stop|status|restart", "operation (start|stop|status|restart)"); op_parser.addOptionStructList(AppOptions); op_parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); op_parser.addHelpOption(); op_parser.addVersionOption(); if (!op_parser.parse(args)) throw SvException(SvException::SomeError, QString("Ошибка разбора командной строки:\n\%1\n\n%2").arg(op_parser.errorText()).arg(op_parser.helpText()), -1); if (op_parser.isSetVersionOption()) throw SvException(SvException::NoError, QString("Сервер сбора и обработки данных Modus v.%1\n").arg(APP_VERSION), 1); if (op_parser.isSetHelpOption()) throw SvException(SvException::NoError, op_parser.helpText(), 2); if(op_parser.positionalArguments().count()) { QString op = op_parser.positionalArguments().at(0); cfg.start = op == QString(OPERATION_START); cfg.stop = op == QString(OPERATION_STOP); cfg.restart = op == QString(OPERATION_RESTART); cfg.status = op == QString(OPERATION_STATUS); cfg.twin = op == QString(OPERATION_TWIN); } else throw SvException("Не указана операция.\n\n" + op_parser.helpText(), -1); cfg.debug = op_parser.isSet(OPTION_DEBUG); cfg.config_file_name = op_parser.isSet(OPTION_CONFIG_FILE) ? op_parser.value(OPTION_CONFIG_FILE) : "config.json"; cfg.start_log_level = op_parser.isSet(OPTION_LOG_LEVEL) ? op_parser.value(OPTION_LOG_LEVEL) : "info"; cfg.case_value = op_parser.isSet(OPTION_CASE) ? op_parser.value(OPTION_CASE) : ""; // свой pid pid_t mypid = getpid(); QList<pid_t> pids = get_pids_by_name(qApp->applicationName(), mypid); // qDebug() << "mypid" << mypid << "pids" << pids; if(cfg.start) { if(pids.count() > 0) { std::cout << "Сервер уже запущен!\n"; for(pid_t pid: pids) std::cout << QString("%1\n").arg(pid).toStdString(); return 3; } return 0; } if(cfg.twin) { // сюда попадаем после операции restart if(pids.count() > 1) { std::cout << "Сервер уже запущен!\n"; for(pid_t pid: pids) std::cout << QString("%1\n").arg(pid).toStdString(); return 4; } return 0; } else if(cfg.stop) { if(pids.count() == 0) throw SvException("Сервер не запущен", 1); for(pid_t pid: pids) { SvResult r = execute(QString("kill -9 %1").arg(pids.at(0))); if(r.type == SvResult::OK && r.text.isEmpty()) std::cout << QString("Сервер остановлен [%1]\n").arg(pid).toStdString(); else throw SvException(r.text, -1); } return 1; } else if(cfg.status) { if(pids.count() == 0) throw SvException("Сервер не запущен", 1); for(pid_t pid: pids) { SvResult rs = execute(QString("ps -p %1 -o pid,start=\"Started\",etime=\"RunningTime\",sz=\"Memory\",cmd=\"Cmd\"").arg(pid)); if(rs.type == SvResult::OK) std::cout << QString("Сервер запущен\n%1\n").arg(rs.text).toStdString(); else throw SvException(rs.text, -1); } return 1; } else if(cfg.restart) { // останавливаем сервер(а) if(pids.count() == 0) std::cout << "Сервер не запущен, но сейчас запустим\n"; else { for(pid_t pid: pids) { SvResult r = execute(QString("kill -SIGINT %1").arg(pids.at(0))); if(r.type == SvResult::OK && r.text.isEmpty()) std::cout << QString("Сервер остановлен [%1]\n").arg(pid).toStdString(); else throw SvException(r.text, -1); } } // запускаем новый экземпляр if(!QProcess::startDetached(QString("%1 twin").arg(qApp->applicationFilePath()))) throw SvException("Не удалось запустить сервер", -1); // после запуска ждем немного чтоб ОС прочухалась QThread::currentThread()->msleep(2000); QList<pid_t> newpids = get_pids_by_name(qApp->applicationName(), mypid); if(newpids.count() == 0) throw SvException("Не удалось перезапустить сервер", -1); throw SvException(QString("Сервер перезапущен [%1]").arg(newpids.at(0)), 1); } else throw SvException("Неизвестная операция", -2); } catch(SvException &e) { std::cout << e.error.toStdString() << "\n"; return e.code; } } static void getMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg) { MASTERLOGGER->handleMessage(type, context, msg); } int main(int argc, char *argv[]) { // запрос версии для монитора if((argc > 1) && (QString(argv[1]).trimmed() == "-v")) { std::cout << QString(APP_VERSION).toStdString().c_str() << std::endl; return 0; } QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")); MASTERLOGGER = new modus::MasterLogger; qInstallMessageHandler(getMessage); QCoreApplication a(argc, argv); // SvCore a(argc, argv); modus::AppConfig cfg; // в случае если дана команда start, то возвращается 0. в других случаях не 0 int result = server_operate(a.arguments(), cfg); if(result) return result; try { // загружаем конфигурацию. она нужна для инициализации мастер-логгера if(!CONFIGURATION.load(cfg.config_file_name)) throw SvException(CONFIGURATION.lastError(), -2); if(!MASTERLOGGER->init(cfg, &ENTITIES, &CONFIGURATION)) //int(modus::event::stringToLevel(cfg.start_log_level)))) throw SvException(MASTERLOGGER->lastError(), -3); /* инициализируем dbus ПОСЛЕ запуска потомка // dbus.init("/"); // dbus.setDebugMode(cfg.debug); // dbus.setToken(me); // этот логгер и токен работает только в загрузчике. все модули и сигналы создают свои логгеры // задаем параметры логирования по-умолчанию, чтобы видеть ошибки // sv::log::Options log_options; // log_options.enable = true; // log_options.level = sv::log::stringToLevel(cfg.start_log_level); // sv::log::llDebug; // dbus.setOptions(log_options); QDir::setCurrent(qApp->applicationDirPath()); /// перехватываем момент закрытия программы, чтобы корректно завершить signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); // int result = 0; */ // инициализируем конфигурацию if(!initConfig(cfg)) throw SvException(-10); // читаем модули и сигналы. СИГНАЛЫ В ПОСЛЕДНЮЮ ОЧЕРЕДЬ! if(!readModules(cfg)) throw SvException(-20); if(!readSignals(cfg)) throw SvException(-40); // инициализируем сервер управления initControl(); QString err_s = QString{}; if(!MASTERLOGGER->setEventLog(CONFIGURATION.json(), err_s)) qCritical() << QString("Ошибка при инициализации системы регистрации событий: %1").arg(err_s); // инициализируем модули, начинаем работу if(!initModules()) throw SvException(-50); // CONTROL->setLogger(); } catch(SvException& e) { result = e.code; if(result) { printf("Ошибка при запуске сервера: '%s'. Код ошибки %d\n", e.error.toStdString().c_str(), result); return result; } } if(result == 0) { setsid(); // отцепляемся от родителя if(!cfg.debug) { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } cfg.start_date_time = QDateTime::currentDateTime(); return a.exec(); } else { printf("Ошибка при запуске сервера. Код ошибки %d\n", result); return result; } } void signal_handler(int sig) { Q_UNUSED(sig); qDebug() << "closing"; closeModules(); deleteSignals(); _Exit(0); } bool initConfig(modus::AppConfig& appcfg) { try { // if(!CONFIGURATION.load(appcfg.config_file_name)) // throw SvException(CONFIGURATION.lastError()); //! формируем пути для поиска внешних файлов appcfg.paths = QDir::current().absolutePath().append(";"); appcfg.paths.push_front(QString("%1;").arg(QFileInfo(appcfg.config_file_name).absolutePath())); if(CONFIGURATION.json().contains(P_PATHS) && CONFIGURATION.json().value(P_PATHS).isString()) appcfg.paths.push_front(QString("%1;").arg(CONFIGURATION.json().value(P_PATHS).toString())); // выводим информация о конфигурации qCritical() << modus::event::Message(me, llinf, mtscc, QString(50, '-')); qCritical() << modus::event::Message(me, llinf, mtscc, QString("Сервер сбора и обработки данных Modus v.%1").arg(APP_VERSION)); if(CONFIGURATION.json().contains("project")) { QJsonObject jobj = CONFIGURATION.json().value("project").toObject(); qCritical() << modus::event::Message(me, llinf, mtinf, jobj.value("name").toString()); qCritical() << modus::event::Message(me, llinf, mtinf, QString("Версия конфигурации %1").arg(jobj.value("version").toString())); qCritical() << modus::event::Message(me, llinf, mtinf, QString(jobj.value("description").toString())); } CONFIGURATION.json().insert(OPTION_CASE, QJsonValue(appcfg.case_value)); return true; } catch(SvException& e) { qCritical() << modus::event::Message(me, llerr, mterr, QString("Ошибка: %1").arg(e.error)); return false; } } bool readModules(const modus::AppConfig& appcfg) { // qCritical() << modus::event::Message(me, llinf, mtinf, QString("Читаем данные модулей: ")); try { int counter = 0; if(!CONFIGURATION.json().contains(P_MODULES)) throw SvException(QString("Неверный файл конфигурации. Отсутствует раздел '%1'.").arg(P_MODULES)); QList<modus::module::Config> module_configs = QList<modus::module::Config>(); modus::module::get_modules_from_json(appcfg, &module_configs); // QJsonArray module_list = CONFIGURATION.json().value(P_MODULES).toArray(); // for(QJsonValue v: module_list) { for(modus::module::Config config: module_configs) { if(!config.enable) continue; qCritical() << modus::event::Message(me, lldbg, mtinf, QString("%1: параметры прочитаны").arg(config.name)); if(ENTITIES.Modules()->contains(config.token)) throw SvException(QString("Модуль %1. Повторяющийся идентификатор в файле %2").arg(config.name).arg(config.file_reference)); // создаем объект модуля modus::ModuleAdaptor* newobj = new modus::ModuleAdaptor(); //! для загрузки библиотек, указываем пути поиска файлов config.search_paths = appcfg.paths; qCritical() << modus::event::Message(me, lldbg2, mtinf, QString("%1: создан").arg(config.name)); if(newobj->init(config)) { qCritical() << modus::event::Message(me, lldbg2, mtinf, QString("%1: проинициализирован").arg(config.name)); if(!ENTITIES.addModule(newobj)) throw SvException(ENTITIES.lastError()); qCritical() << modus::event::Message(me, lldbg2, mtscc, QString("%1: успешно добавлен").arg(newobj->config()->name)); counter++; } else throw SvException(QString("Не удалось добавить модуль %1: %2").arg(config.name).arg(newobj->lastError())); } qCritical() << modus::event::Message(me, llinf, mtscc, QString("OK [прочитано %1 модулей]").arg(counter)); return true; } catch(SvException& e) { qCritical() << modus::event::Message(me, llerr, mterr, QString("Ошибка: %1").arg(e.error)); return false; } } bool readSignals(const modus::AppConfig& appcfg) { auto bind = [=](modus::Signal* signal, modus::signal::Binding binding) -> void { if(!binding.valid) return; try { switch (binding.token.entity) { case modus::eModule: { if(!binding.token.name.isEmpty() && ENTITIES.Modules()->contains(binding.token)) { if(!ENTITIES.Modules()->item(binding.token.uid)->bindSignal(signal, binding)) throw SvException(ENTITIES.Modules()->item(binding.token)->lastError()); } break; } default: throw SvException(QString("Неверно определена привязка для сигнала %1\n").arg(signal->config()->name)); } } catch(SvException& e) { qCritical() << modus::event::Message(me, lldbg, mterr, QString("Ошибка привязки сигнала %1: %2\nФайл: %3") .arg(signal->config()->name).arg(e.error).arg(signal->config()->file) ); throw e; } }; qCritical() << modus::event::Message(me, llinf, mtinf, QString("Читаем данные сигналов:")); try { int counter = 0; if(!CONFIGURATION.json().contains(P_SIGNALS)) throw SvException(QString("Неверный файл конфигурации. Отсутствует раздел '%1'.").arg(P_SIGNALS)); /// парсим список сигналов. в списке сигналов могут содержаться ссылки на другие файлы. /// парсер разбирает сигналы из всех файлов и добавляет их в signal_list QList<modus::signal::Config> signal_configs = QList<modus::signal::Config>(); modus::signal::get_signals_from_json(appcfg, &signal_configs); for(modus::signal::Config config: signal_configs) { if(!config.enable) continue; // qCritical() << modus::event::Message(me, lldbg, mtdbg, QString(" %1: параметры прочитаны").arg(config.name)); if(ENTITIES.Signals()->contains(config.token)) throw SvException(QString("Сигнал %1. Повторяющийся идентификатор в файле %2!").arg(config.name).arg(config.file)); //! создаем объект modus::Signal* newsignal = new modus::Signal(config); if(newsignal) { // qCritical() << modus::event::Message(me, lldbg2, mtinf, QString("%1: создан").arg(config.name)); if(!ENTITIES.addSignal(newsignal)) throw SvException(ENTITIES.lastError()); // qCritical() << modus::event::Message(me, lldbg2, mtscc, QString("%1: успешно добавлен").arg(newsignal->config()->name)); //! алгоритм привязок: //! 1. вначале привязываем сигнал к модулю-хозяину. данный модуль работает с сигналом в режиме ReadWrite, //! то есть может изменять все поля и значения данного сигнала. //! привязка к хозяину опеределяется параметром конфигурации master. //! если параметр master отстутствует в конфигурации, то данный сигнал может быть изменен только //! через web control //! 2. затем привязываем сигнал к модулям-читателям. модули читатели работают с сигналом в режиме ReadOnly, //! то есть не могут изменять никие поля сигнала. //! привязки к модулям читателям определяются параметром bindings. если параметр bindings отсутствует //! в конфигурации, то переходим сразу к шагу 3. //! 3. привязываем сигнал ко всем обработчикам. проходим по списку обработчиков и вызываем bindSignal. //! если для очередного обработчика, сигнал был привязан на 2-ом этапе, то пропускаем //! эти функции привязывают сигналы, для которых явно определены привязки //! привязываем сигнал к модулю-хозяину bind(newsignal, newsignal->config()->master); //! привязываем сигнал к модулям-читателям for(modus::signal::Binding binding: newsignal->config()->readers) { bind(newsignal, binding); } // qCritical() << modus::event::Message(me, lldbg2, mtinf, QString("%1: привязан").arg(config.name)); counter++; } } // CONFIGURATION.json().insert(P_SIGNALS, signal_array); qCritical() << modus::event::Message(me, llinf, mtscc, QString("OK [Прочитано %1]\n").arg(counter) ); return true; } catch(SvException& e) { qCritical() << modus::event::Message(me, llerr, mterr, QString("Ошибка: %1\n").arg(e.error)); return false; } } bool initModules() { qCritical() << modus::event::Message(me, llinf, mtinf, "Инициализируем модули:"); try { foreach(modus::ModuleAdaptor* obj, ENTITIES.Modules()->items()) { if(!obj->start()) throw SvException(QString("%1: %2").arg(obj->config()->name).arg(obj->lastError())); qCritical() << modus::event::Message(me, lldbg, mtdbg, QString(" %1: OK").arg(obj->config()->name)); } qCritical() << modus::event::Message(me, llinf, mtscc, QString("OK")); return true; } catch(SvException& e) { qCritical() << modus::event::Message(me, llerr, mterr, QString("Ошибка: %1").arg(e.error)); return false; } } void initControl() { qCritical() << modus::event::Message(me, llinf, mtinf, "Инициализируем сервер управления:"); try { modus::ControlServer::Params params; if(!CONFIGURATION.json().contains(P_CONTROL)) { qCritical() << modus::event::Message(me, llinf, mtinf, QString("Раздел '%1' в конфигурации отсутствует. Будут использованы параметры по умолчанию.").arg(P_CONTROL)); } else { if(!CONFIGURATION.json().value(P_CONTROL).isObject()) throw sv::SvException(QString("Неверный файл конфигурации. Параметры сервера управления заданы неверно.")); QJsonObject control = CONFIGURATION.json().value(P_CONTROL).toObject(); params = modus::ControlServer::Params::fromJsonObject(control); } CONTROL = new modus::ControlServer(); if(!CONTROL->configure(params, &CONFIGURATION, &ENTITIES, MASTERLOGGER)) throw sv::SvException(CONTROL->lastError()); CONTROL->start(); /* before << QString("Раздел '%1' отсутствует. Доступ к управлению сервером невозможен.").arg(P_CONTROL) ); } else { if(!CONFIGURATION.json().value(P_CONTROL).isObject()) throw SvException(QString("Неверный файл конфигурации. Параметры сервера управления заданы неверно.")); QJsonObject control = CONFIGURATION.json().value(P_CONTROL).toObject(); modus::SvControlServer::Params params = modus::SvControlServer::Params::fromJsonObject(control); CONTROL = new modus::SvControlServer(); if(!CONTROL->configure(params, &CONFIGURATION, &ENTITIES)) throw SvException(CONTROL->lastError()); CONTROL->start(); */ qCritical() << modus::event::Message(me, llinf, mtscc, QString("OK.")); } catch(SvException& e) { qCritical() << modus::event::Message(me, llerr, mterr, QString("Ошибка: %1").arg(e.error)); throw e; } } void closeModules() { qCritical() << modus::event::Message(me, llinf, mterr, "Закрываем устройства:"); try { for (int i = 0; i < ENTITIES.Modules()->items().count(); i++) { modus::ModuleAdaptor* module = ENTITIES.Modules()->items().at(i); delete module; qCritical() << modus::event::Message(me, lldbg2, mtdbg, module->config()->name); } qCritical() << modus::event::Message(me, llinf, mterr, QString("OK [Закрыто %1]").arg(ENTITIES.Modules()->items().count()) ); } catch(SvException& e) { qCritical() << modus::event::Message(me, llerr, mterr, QString("Ошибка: %1").arg(e.error)); } } void deleteSignals() { qCritical() << modus::event::Message(me, llinf, mterr, "Удаляем сигналы:"); for (int i = 0; i < ENTITIES.Signals()->items().count(); i++) { modus::Signal* signal = ENTITIES.Signals()->items().at(i); delete signal; qCritical() << modus::event::Message(me, lldbg2, mtdbg, signal->config()->name); } qCritical() << modus::event::Message(me, llinf, mterr, QString("OK [Удалено %1]").arg(ENTITIES.Signals()->items().count()) ); }