#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"; } static std::string buildEscapedString(const std::string& source) { std::string str(source); size_t start_pos = 0; while((start_pos = str.find('\"', start_pos)) != std::string::npos) { str.replace(start_pos, 1, "\\\""); start_pos += 2; } return "\"" + str + "\""; } void writeDouble(std::ostream& out, double value, int prec = 2) { if (std::isnan(value) || std::isinf(value)) { out << "\"nan\""; } else { out << std::fixed << std::setprecision(prec) << value; } } double translateCoordinates(uint8_t deg, uint8_t min) { return static_cast(deg) + static_cast(min) / 60; } std::tuple translateCoordinates(double abs) { auto deg = static_cast(abs); double min_double = (abs - deg) * 60; auto min = static_cast(min_double); return std::make_tuple(deg, min); } 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{}; demodulator_state demodulator{}; device_state device{}; daemon->getStatistics(&modulator, &demodulator, &device); const bool isCinC = this->getIsCinC(); result << ",\"isCinC\":" << boolAsStr(isCinC); // формируем структуру для TX result << ",\n\"tx.state\":" << boolAsStr(modulator.is_tx_on); result << ",\"tx.modcod\":" << modulator.modcod; result << ",\"tx.snr\":"; writeDouble(result, modulator.snr_remote); if (modulator.is_short) { result << R"(,"tx.frameSize":"short")"; } else { result << R"(,"tx.frameSize":"normal")"; } if (modulator.is_pilots) { result << R"(,"tx.pilots":"pilots")"; } else { result << R"(,"tx.pilots":"no pilots")"; } result << ",\"tx.speedOnTxKbit\":"; writeDouble(result, static_cast(modulator.speed_in_bytes_tx) / 128.0); result << ",\"tx.speedOnIifKbit\":"; writeDouble(result, (static_cast(modulator.speed_in_bytes_tx_iface) / 128.0)); // формируем структуру для RX result << ",\n\"rx.state\":" << boolAsStr(demodulator.locks.sym_sync_lock && demodulator.locks.freq_lock && demodulator.locks.afc_lock && demodulator.locks.pkt_sync); result << ",\"rx.sym_sync_lock\":" << boolAsStr(demodulator.locks.sym_sync_lock); result << ",\"rx.freq_search_lock\":" << boolAsStr(demodulator.locks.freq_lock); result << ",\"rx.afc_lock\":" << boolAsStr(demodulator.locks.afc_lock); result << ",\"rx.pkt_sync\":" << boolAsStr(demodulator.locks.pkt_sync); result << ",\"rx.snr\":"; writeDouble(result, demodulator.snr); result << ",\"rx.rssi\":"; writeDouble(result, demodulator.rssi); result << ",\"rx.modcod\":" << demodulator.modcod; if (demodulator.is_short) { result << R"(,"rx.frameSize":"short")"; } else { result << R"(,"rx.frameSize":"normal")"; } if (demodulator.is_pilots) { result << R"(,"rx.pilots":"pilots")"; } else { result << R"(,"rx.pilots":"no pilots")"; } result << ",\n\"rx.symError\":"; writeDouble(result, demodulator.sym_err); result << ",\"rx.freqErr\":"; writeDouble(result, demodulator.crs_freq_err); result << ",\"rx.freqErrAcc\":"; writeDouble(result, demodulator.fine_freq_err); result << ",\"rx.inputSignalLevel\":"; writeDouble(result, demodulator.if_overload); result << ",\"rx.pllError\":"; writeDouble(result, demodulator.afc_err); result << ",\"rx.speedOnRxKbit\":"; writeDouble(result, static_cast(demodulator.speed_in_bytes_rx) / 128.0); result << ",\"rx.speedOnIifKbit\":"; writeDouble(result, static_cast(demodulator.speed_in_bytes_rx_iface) / 128.0); result << ",\"rx.packetsOk\":" << demodulator.packet_ok_cnt; result << ",\"rx.packetsBad\":" << demodulator.packet_bad_cnt; result << ",\"rx.packetsDummy\":" << demodulator.dummy_cnt; // формируем структуру для CinC if (isCinC) { CinC_state state_cinc{}; CP_GetCinCState(sid,state_cinc); if (modulator.is_tx_on) { if (state_cinc.carrier_lock) { result << R"(,"cinc.correlator":true)"; } else { result << R"(,"cinc.correlator":false)"; } } else { result << R"(,"cinc.correlator":null)"; } result << ",\n\"cinc.occ\":"; writeDouble(result, state_cinc.ratio_signal_signal, 3); result << ",\"cinc.correlatorFails\":" << state_cinc.cnt_bad_lock; result << ",\"cinc.freqErr\":" << state_cinc.freq_error_offset; result << ",\"cinc.freqErrAcc\":" << state_cinc.freq_fine_estimate; result << ",\"cinc.channelDelay\":" << state_cinc.delay_dpdi; } else { result << R"(,"cinc.correlator":null)"; } // структура температур девайса result << ",\n\"device.adrv\":"; writeDouble(result, device.adrv_temp, 1); result << ",\"device.fpga\":"; writeDouble(result, device.pl_temp, 1); result << ",\"device.zynq\":"; writeDouble(result, device.zynq_temp, 1); result << "}"; return result.str(); } void api_driver::ApiDriver::resetPacketStatistics() const { std::string tmp; CP_GetDmaDebug(sid, "reset_cnt_rx", &tmp); } std::string api_driver::ApiDriver::loadSettings() const { if (daemon == nullptr) { return R"({"error": "api daemon not started!"})"; } modulator_settings modSettings{}; demodulator_settings demodSettings{}; ACM_parameters_serv_ acmSettings{}; DPDI_parmeters dpdiSettings{}; buc_lnb_settings 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); result << ",\"general.txEn\":" << boolAsStr(modSettings.tx_is_on); result << ",\"general.modulatorMode\":" << (modSettings.is_carrier ? "\"normal\"" : "\"test\""); result << ",\"general.autoStartTx\":" << boolAsStr(modSettings.is_save_current_state); result << ",\"general.isTestInputData\":" << boolAsStr(modSettings.is_test_data); result << ",\n\"tx.attenuation\":"; writeDouble(result, modSettings.attenuation); result << ",\"tx.rolloff\":" << static_cast(modSettings.rollof * 100); result << ",\"tx.cymRate\":" << modSettings.baudrate; result << ",\"tx.centerFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz, 3); result << ",\n\"dvbs2.isAcm\":" << boolAsStr(acmSettings.enable); result << ",\"dvbs2.frameSize\":" << ((modulatorModcod & 2) ? "\"short\"" : "\"normal\""); // result << ",\"dvbs2.pilots\":" << "null"; result << ",\"dvbs2.ccm_modcod\":" << (modulatorModcod >> 4); result << ",\"dvbs2.acm_maxModcod\":" << (acmSettings.max_modcod >> 2); result << ",\"dvbs2.acm_minModcod\":" << (acmSettings.min_modcod >> 2); result << ",\"dvbs2.snrReserve\":"; writeDouble(result, acmSettings.min_attenuation); result << ",\"dvbs2.servicePacketPeriod\":" << acmSettings.period_pack; result << ",\n\"acm.en\":" << boolAsStr(acmSettings.enable_auto_atten); result << ",\"acm.maxAttenuation\":"; writeDouble(result, acmSettings.max_attenuation); result << ",\"acm.minAttenuation\":"; writeDouble(result, acmSettings.min_attenuation); result << ",\"acm.requiredSnr\":"; writeDouble(result, acmSettings.snr_treashold_acm); result << ",\n\"rx.gainMode\":" << (demodSettings.is_aru_on ? "\"auto\"" : "\"manual\""); result << ",\"rx.manualGain\":"; writeDouble(result, demodSettings.gain); result << ",\"rx.spectrumInversion\":" << boolAsStr(demodSettings.is_rvt_iq); result << ",\"rx.rolloff\":" << static_cast(demodSettings.rollof * 100); result << ",\"rx.cymRate\":" << demodSettings.baudrate; result << ",\"rx.centerFreq\":"; writeDouble(result, demodSettings.central_freq_in_kGz); result << ",\n\"cinc.mode\":" << (dpdiSettings.is_delay_window ? "\"delay\"" : "\"positional\""); result << ",\"cinc.searchBandwidth\":" << dpdiSettings.freq_offset; // полоса поиска в кГц result << ",\"cinc.position.station.latitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.latitude_station_grad, dpdiSettings.latitude_station_minute), 6); result << ",\"cinc.position.station.longitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.longitude_station_grad, dpdiSettings.longitude_station_minute), 6); result << ",\"cinc.position.satelliteLongitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.longitude_sattelite_grad, dpdiSettings.longitude_sattelite_minute), 6); result << ",\"cinc.delayMin\":" << dpdiSettings.min_delay; result << ",\"cinc.delayMax\":" << dpdiSettings.max_delay; result << ",\n\"buc.refClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_buc); switch (bucLnb.buc) { case voltage_buc::DISABLE: result << ",\"buc.powering\":0"; break; case voltage_buc::_24V: result << ",\"buc.powering\":24"; break; case voltage_buc::_48V: result << ",\"buc.powering\":48"; break; } result << ",\n\"lnb.refClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_lnb); switch (bucLnb.lnb) { case voltage_lnb::DISABLE: result << ",\"lnb.powering\":0"; break; case voltage_lnb::_13V: result << ",\"lnb.powering\":13"; break; case voltage_lnb::_18V: result << ",\"lnb.powering\":18"; break; case voltage_lnb::_24V: result << ",\"lnb.powering\":24"; break; } result << ",\n\"serviceSettings.refClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_output); result << ",\"serviceSettings.autoStart\":" << boolAsStr(bucLnb.is_save_current_state); result << "}"; return result.str(); } 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; }