terminal-web-server/src/terminal_api_driver.cpp

418 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "terminal_api_driver.h"
#include <cmath>
#include "terminal_api/ControlProtoCInterface.h"
#include <sstream>
#include <iomanip>
#include <shared_mutex>
#include <boost/thread.hpp>
#include <boost/log/trivial.hpp>
#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::milliseconds>(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::milliseconds>(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::milliseconds>(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<TerminalApiDaemon>(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<double>(deg) + static_cast<double>(min) / 60;
}
std::tuple<uint8_t, uint8_t> translateCoordinates(double abs) {
auto deg = static_cast<uint8_t>(abs);
double min_double = (abs - deg) * 60;
auto min = static_cast<uint8_t>(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<double>(modulator.speed_in_bytes_tx) / 128.0);
result << ",\"tx.speedOnIifKbit\":"; writeDouble(result, (static_cast<double>(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<double>(demodulator.speed_in_bytes_rx) / 128.0);
result << ",\"rx.speedOnIifKbit\":"; writeDouble(result, static_cast<double>(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<int>(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<int>(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;
}