diff --git a/src/main.cpp b/src/main.cpp index 580210c..f3207c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -98,6 +98,7 @@ public: ServerResources(const ServerResources&) = delete; ServerResources(): sf(std::make_unique()), api(std::make_unique()) { + api->startDaemon(); auth.users.emplace_back(std::make_shared("admin")); sf->registerFile(INDEX_HTML, mime_types::text_html, false); diff --git a/src/terminal_api_driver.cpp b/src/terminal_api_driver.cpp index b57d0f0..5afd4f5 100644 --- a/src/terminal_api_driver.cpp +++ b/src/terminal_api_driver.cpp @@ -1,19 +1,187 @@ #include "terminal_api_driver.h" #include - #include "terminal_api/ControlProtoCInterface.h" #include #include - +#include +#include +#include #include "../dependencies/control_system/common/protocol_commands.h" +/** + * Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки + */ +class api_driver::TerminalApiDaemon { +private: + TSID sid; + boost::thread daemon; + + void updateStatistics() { + modulator_state modulator{}; + CP_GetModulatorState(sid, modulator); + + demodulator_state demodulator{}; + CP_GetDemodulatorState(sid, demodulator); + + device_state device{}; + CP_GetDeviceState(sid, device); + + { + std::lock_guard lock(this->stateMutex); + this->modState = modulator; + this->demodState = demodulator; + this->devState = device; + } + } + + void updateSettings() { + modulator_settings mod{}; + CP_GetModulatorSettings(sid, mod); + // uint32_t modulatorModcod; + // CP_GetModulatorParams(sid, "modcod", &modulatorModcod); + demodulator_settings demod{}; + CP_GetDemodulatorSettings(sid, demod); + ACM_parameters_serv_ acm{}; + CP_GetAcmParams(sid, &acm); + DPDI_parmeters dpdi{}; + CP_GetDpdiParams(sid, &dpdi); + buc_lnb_settings bucLnb{}; + CP_GetBUC_LNB_settings(sid, bucLnb); + + { + std::lock_guard lock(this->settingsMutex); + this->modSettings = mod; + this->demodSettings = demod; + this->acmSettings = acm; + this->dpdiSettings = dpdi; + this->bucLnbSettings = bucLnb; + } + } + + void run() { + // это демон, который в бесконечном цикле опрашивает API + + struct IntervalUpdate_t { + int64_t lastUpdate; + int64_t periodMs; + + bool checkNeedUpdate(int64_t now) { + // тут нет смысла спать меньше чем на 20мс, поэтому можно разрешить чтение на некоторое время раньше + return now - lastUpdate >= (periodMs - 20); + } + + int64_t getNextUpdate(int64_t now) { + if (checkNeedUpdate(now)) { + return 0; + } + auto next = now - lastUpdate; + return next < 0 ? 0 : next; + } + }; + + IntervalUpdate_t statUpdate{.lastUpdate = 0, .periodMs = CACHE_STATISTICS_UPDATE_MS}; + IntervalUpdate_t settingsUpdate{.lastUpdate = 0, .periodMs = CACHE_SETTINGS_UPDATE_MS}; + + while (true) { + auto now = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + if (statUpdate.checkNeedUpdate(now)) { + statUpdate.lastUpdate = now; + try { + updateStatistics(); + BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateStatistics(): success update!"; + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateStatistics(): " << e.what(); + } + now = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + } + if (settingsUpdate.checkNeedUpdate(now)) { + settingsUpdate.lastUpdate = now; + try { + updateSettings(); + BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateSettings(): success update!"; + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateSettings(): " << e.what(); + } + now = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + } + + auto sleepTime = statUpdate.getNextUpdate(now); + sleepTime = std::min(sleepTime, settingsUpdate.getNextUpdate(now)); + if (sleepTime > 0) { + boost::this_thread::sleep_for(boost::chrono::duration(boost::chrono::milliseconds(sleepTime))); + } + } + } + + std::shared_mutex stateMutex; + modulator_state modState{}; + demodulator_state demodState{}; + device_state devState{}; + + std::shared_mutex settingsMutex; + modulator_settings modSettings{}; + demodulator_settings demodSettings{}; + ACM_parameters_serv_ acmSettings{}; + DPDI_parmeters dpdiSettings{}; + buc_lnb_settings bucLnbSettings{}; + +public: + explicit TerminalApiDaemon(TSID sid): sid(sid), daemon([this]() { this->run(); }) {} + + /** + * Получение статистики, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет. + * @param mod статистика модулятра + * @param demod статистика демодулятора + * @param dev статистика устройства (температуры) + */ + void getStatistics(modulator_state* mod, demodulator_state* demod, device_state* dev) { + if (mod != nullptr || demod != nullptr || dev != nullptr) { + std::shared_lock lock(this->stateMutex); + if (mod) { *mod = this->modState; } + if (demod) { *demod = this->demodState; } + if (dev) { *dev = this->devState; } + } + } + + /** + * Получение настроек, копирует текущие значения в структуры, переданные по указателю. Если передан пустой указатель, копирования не произойдет. + */ + void getSettings(modulator_settings* mod, demodulator_settings* demod, ACM_parameters_serv_* acm, DPDI_parmeters* dpdi, buc_lnb_settings* bucLnb) { + if (mod || demod || acm || dpdi || bucLnb) { + std::shared_lock lock(this->settingsMutex); + if (mod) { *mod = this->modSettings; } + if (demod) { *demod = this->demodSettings; } + if (acm) { *acm = this->acmSettings; } + if (dpdi) { *dpdi = this->dpdiSettings; } + if (bucLnb) { *bucLnb = this->bucLnbSettings; } + } + } + + ~TerminalApiDaemon() { + try { + daemon.interrupt(); + daemon.try_join_for(boost::chrono::seconds(2)); + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "api_driver::~TerminalApiDaemon(): " << e.what(); + } + } +}; + + api_driver::ApiDriver::ApiDriver() { CP_Login("admin", "pass", &sid, &access); CP_GetDmaDebug(sid, "status_init", &deviceInitState); } +void api_driver::ApiDriver::startDaemon() { + if (daemon == nullptr) { + daemon = std::make_unique(this->sid); + BOOST_LOG_TRIVIAL(info) << "api_driver::ApiDriver::startDaemon(): API daemon succes started!"; + } +} + static const char* boolAsStr(bool value) { return value ? "true" : "false"; } @@ -28,12 +196,6 @@ static std::string buildEscapedString(const std::string& source) { return "\"" + str + "\""; } -static bool DriverCP_GetCinC(TSID sid) { - modulator_settings s{}; - CP_GetModulatorSettings(sid, s); - return s.is_cinc; -} - void writeDouble(std::ostream& out, double value, int prec = 2) { if (std::isnan(value) || std::isinf(value)) { out << "\"nan\""; @@ -55,19 +217,18 @@ std::tuple translateCoordinates(double abs) { std::string api_driver::ApiDriver::loadTerminalState() const { + if (daemon == nullptr) { + return R"({"error": "api daemon not started!"})"; + } std::stringstream result; + result << "{\n\"initState\":" << buildEscapedString(this->deviceInitState); modulator_state modulator{}; - CP_GetModulatorState(sid, modulator); - demodulator_state demodulator{}; - CP_GetDemodulatorState(sid, demodulator); - device_state device{}; - CP_GetDeviceState(sid, device); - - const bool isCinC = DriverCP_GetCinC(sid); + daemon->getStatistics(&modulator, &demodulator, &device); + const bool isCinC = this->getIsCinC(); result << ",\"isCinC\":" << boolAsStr(isCinC); @@ -166,19 +327,23 @@ void api_driver::ApiDriver::resetPacketStatistics() const { } std::string api_driver::ApiDriver::loadSettings() const { - modulator_settings modSettings{}; - CP_GetModulatorSettings(sid, modSettings); - uint32_t modulatorModcod; - CP_GetModulatorParams(sid, "modcod", &modulatorModcod); + if (daemon == nullptr) { + return R"({"error": "api daemon not started!"})"; + } + modulator_settings modSettings{}; demodulator_settings demodSettings{}; - CP_GetDemodulatorSettings(sid, demodSettings); ACM_parameters_serv_ acmSettings{}; - CP_GetAcmParams(sid, &acmSettings); DPDI_parmeters dpdiSettings{}; - CP_GetDpdiParams(sid, &dpdiSettings); buc_lnb_settings bucLnb{}; - CP_GetBUC_LNB_settings(sid, bucLnb); + daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb); + + uint32_t modulatorModcod; + { + modulator_state ms{}; + daemon->getStatistics(&ms, nullptr, nullptr); + modulatorModcod = ms.modcod; + } std::stringstream result; result << "{\n\"general.isCinC\":" << boolAsStr(modSettings.is_cinc); @@ -244,3 +409,9 @@ std::string api_driver::ApiDriver::loadSettings() const { } api_driver::ApiDriver::~ApiDriver() = default; + +bool api_driver::ApiDriver::getIsCinC() const { + modulator_settings s{}; + daemon->getSettings(&s, nullptr, nullptr, nullptr, nullptr); + return s.is_cinc; +} diff --git a/src/terminal_api_driver.h b/src/terminal_api_driver.h index 43bdea7..07ee81e 100644 --- a/src/terminal_api_driver.h +++ b/src/terminal_api_driver.h @@ -1,11 +1,18 @@ #ifndef TERMINAL_API_DRIVER_H #define TERMINAL_API_DRIVER_H +#include #include #include namespace api_driver { + + constexpr int CACHE_STATISTICS_UPDATE_MS = 500; + constexpr int CACHE_SETTINGS_UPDATE_MS = 5000; + + class TerminalApiDaemon; + /** * Это ApiDriver. Все ответы он будет возвращать в виде json. */ @@ -13,6 +20,11 @@ namespace api_driver { public: explicit ApiDriver(); + /** + * Запуск демона + */ + void startDaemon(); + /** * Запросить общее состояние терминала * @return {"txState":false,"rxState":false,"rx.sym_sync_lock":false,"rx.freq_search_lock":false,"rx.afc_lock":false,"rx.pkt_sync":false} @@ -33,6 +45,9 @@ namespace api_driver { unsigned int access{0}; std::string deviceInitState; + std::unique_ptr daemon; + + bool getIsCinC() const; }; } diff --git a/static/main.html b/static/main.html index dc11098..298ea2b 100644 --- a/static/main.html +++ b/static/main.html @@ -28,7 +28,7 @@
{{ initState }}
-
+

Статистика приема

@@ -59,7 +59,7 @@
-
+

Статистика передачи

@@ -73,7 +73,7 @@
-
+

Статистика режима CinC

@@ -85,7 +85,7 @@
-
+

Состояние устройства