798 lines
30 KiB
C++
798 lines
30 KiB
C++
#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 <boost/property_tree/json_parser.hpp>
|
||
#include <sys/sysinfo.h>
|
||
|
||
#define TIME_NOW() std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now()).time_since_epoch().count()
|
||
|
||
typedef boost::property_tree::ptree::path_type json_path;
|
||
|
||
// пороговое значение сна
|
||
static constexpr int64_t SLEEP_THRESHOLD = 10;
|
||
|
||
static inline void rtrim(std::string &s) {
|
||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||
return !std::isspace(ch);
|
||
}).base(), s.end());
|
||
}
|
||
static inline const char* boolAsStr(bool value) {
|
||
return value ? "true" : "false";
|
||
}
|
||
|
||
class TerminalNetworkSettings {
|
||
public:
|
||
std::string managementIp, managementGateway, dataIp, serverName;
|
||
bool isL2 = true;
|
||
unsigned int dataMtu = 1500;
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
static constexpr const char* DEFAULT_SERVER_NAME = "RCSM-101";
|
||
#endif
|
||
#ifdef MODEM_IS_TDMA
|
||
static constexpr const char* DEFAULT_SERVER_NAME = "RCSM-101 TDMA";
|
||
#endif
|
||
|
||
TerminalNetworkSettings() = default;
|
||
TerminalNetworkSettings(const TerminalNetworkSettings& src) = default;
|
||
~TerminalNetworkSettings() = default;
|
||
|
||
TerminalNetworkSettings& operator= (const TerminalNetworkSettings& src) = default;
|
||
|
||
void loadDefaults() {
|
||
managementIp = "0.0.0.0";
|
||
managementGateway = "";
|
||
isL2 = true;
|
||
dataIp = "0.0.0.0";
|
||
dataMtu = 1500;
|
||
serverName = DEFAULT_SERVER_NAME;
|
||
}
|
||
};
|
||
|
||
class TerminalDeviceState {
|
||
public:
|
||
double adrv_temp{}, pl_temp{}, zynq_temp{};
|
||
|
||
#ifdef MODEM_IS_TDMA
|
||
DOWNLOAD_STATUS otaStatus{}; // Downloading Status
|
||
unsigned int otaPercent{}; // % downloaded data
|
||
std::string otaImage{}; // Name of downloading image file
|
||
#endif
|
||
|
||
modulator_state mod{};
|
||
demodulator_state demod{};
|
||
#ifdef MODEM_IS_SCPC
|
||
debug_metrics debug{};
|
||
CinC_state cinc{};
|
||
#endif
|
||
|
||
};
|
||
|
||
class TerminalFirmwareVersion {
|
||
public:
|
||
std::string version, modemId, modemSn, macMang, macData;
|
||
|
||
TerminalFirmwareVersion() = default;
|
||
TerminalFirmwareVersion(const TerminalFirmwareVersion& src) = default;
|
||
~TerminalFirmwareVersion() = default;
|
||
|
||
TerminalFirmwareVersion& operator= (const TerminalFirmwareVersion& src) = default;
|
||
};
|
||
|
||
static std::ostream& operator<<(std::ostream& out, CP_Result result) {
|
||
switch (result) {
|
||
case OK: out << "OK"; break;
|
||
case TIMEOUT: out << "TIMEOUT"; break;
|
||
case ERROR: out << "ERROR"; break;
|
||
case ABORT: out << "ABORT"; break;
|
||
case BUSY: out << "BUSY"; break;
|
||
default:
|
||
out << static_cast<int>(result);
|
||
}
|
||
return out;
|
||
}
|
||
|
||
std::string makeTimepointFromMillis(int64_t unix_time_ms) {
|
||
// Преобразуем миллисекунды в микросекунды для std::chrono
|
||
auto time_point = std::chrono::time_point<std::chrono::system_clock,
|
||
std::chrono::microseconds>(std::chrono::microseconds(unix_time_ms * 1000));
|
||
|
||
auto tp = std::chrono::system_clock::to_time_t(time_point);
|
||
tm* t = std::localtime(&tp);
|
||
|
||
std::stringstream ss;
|
||
ss << std::put_time(t, "%Y-%m-%d %H:%M:%S");
|
||
auto ms = (unix_time_ms % 1000);
|
||
ss << '.' << std::setw(3) << std::setfill('0') << ms;
|
||
return ss.str();
|
||
}
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
class api_driver::StatisticsLogger {
|
||
public:
|
||
StatisticsLogger(): timeStart(TIME_NOW()) {}
|
||
|
||
int64_t timeStart;
|
||
|
||
bool logEn = false;
|
||
std::atomic<int> logPeriodMs = 1000;
|
||
std::atomic<int> maxAgeMs = 10000;
|
||
|
||
/**
|
||
*
|
||
* @return {"en": bool, "logPeriodMs": int, ""}
|
||
*/
|
||
std::string getSettings() {
|
||
std::lock_guard _lock(mutex);
|
||
std::stringstream res;
|
||
res << "{\"en\":" << boolAsStr(this->logEn);
|
||
res << ",\"logPeriodMs\":" << logPeriodMs;
|
||
res << ",\"maxAgeMs\":" << maxAgeMs;
|
||
res << '}';
|
||
return res.str();
|
||
}
|
||
void setSettings(boost::property_tree::ptree &pt) {
|
||
const bool newEn = pt.get<bool>("en");
|
||
const int newInterval = pt.get<int>("logPeriodMs");
|
||
const int newMaxAgeMs = pt.get<int>("maxAgeMs");
|
||
|
||
std::lock_guard _lock(this->mutex);
|
||
this->logPeriodMs = newInterval;
|
||
this->maxAgeMs = newMaxAgeMs;
|
||
|
||
if (newEn != this->logEn) {
|
||
if (newEn) {
|
||
this->logFile.open("/tmp/weblog-statistics.csv", std::ios::out);
|
||
if (this->logFile.is_open()) {
|
||
const auto* header = "timestamp\tcnt ok\tcnt bad\tfine freq dem\tcrs freq dem\tcrs freq compensator\tcrs time est\tfine time est\tmax level corr\tcurrent delay\tSNR\tcurrent modcod\tfine freq compensator\tind freq grb\tind freq tochn\tind filt adapt\tfilter corr cinc\tcorr cnt\tRSS\tcor erl\tcor lat\tgc gain\tpower pl rx\n";
|
||
this->logFile.write(header, static_cast<std::streamsize>(strlen(header)));
|
||
this->logEn = true;
|
||
this->timeStart = TIME_NOW();
|
||
}
|
||
} else {
|
||
if (this->logFile.is_open()) {
|
||
this->logFile.close();
|
||
}
|
||
this->logEn = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Записать значение в "базу данных". Метку при этом вставлять не нужно, она будет вставлена автоматически.
|
||
* @param item
|
||
*/
|
||
void putItem(const debug_metrics& item) {
|
||
std::lock_guard _lock(this->mutex);
|
||
if (!logEn) return;
|
||
if (this->logFile.is_open()) {
|
||
std::stringstream res;
|
||
res << makeTimepointFromMillis(TIME_NOW()) << '\t';
|
||
res << item.cnt_ok << '\t';
|
||
res << item.cnt_bad << '\t';
|
||
res << item.fine_freq_dem << '\t';
|
||
res << item.crs_freq_dem << '\t';
|
||
res << item.crs_freq_compensator << '\t';
|
||
res << item.crs_time_est << '\t';
|
||
res << item.fine_time_est << '\t';
|
||
res << item.max_level_corr << '\t';
|
||
res << item.current_delay << '\t';
|
||
res << item.SNR << '\t';
|
||
res << item.current_modcod << '\t';
|
||
res << item.fine_freq_compensator << '\t';
|
||
res << item.ind_freq_grb << '\t';
|
||
res << item.ind_freq_tochn << '\t';
|
||
res << item.ind_filt_adapt << '\t';
|
||
res << item.filter_corr_cinc << '\t';
|
||
res << item.corr_cnt << '\t';
|
||
res << item.RSS << '\t';
|
||
res << item.cor_erl << '\t';
|
||
res << item.cor_lat << '\t';
|
||
res << item.gc_gain << '\t';
|
||
res << item.power_pl_rx << '\n';
|
||
|
||
const auto out = res.str();
|
||
this->logFile.write(out.c_str(), static_cast<std::streamsize>(out.length()));
|
||
this->logFile.flush();
|
||
}
|
||
}
|
||
|
||
// void collectExpiredItems();
|
||
|
||
// void resetLogs() {
|
||
// std::lock_guard _lock(mutex);
|
||
// logs.clear();
|
||
// }
|
||
|
||
~StatisticsLogger() = default;
|
||
private:
|
||
// std::pmr::deque<LogItem> logs;
|
||
std::fstream logFile{};
|
||
std::shared_mutex mutex;
|
||
};
|
||
#endif
|
||
|
||
|
||
api_driver::ApiDriver::ApiDriver() = default;
|
||
|
||
void api_driver::ApiDriver::startDaemon() {
|
||
if (daemon == nullptr) {
|
||
daemon = std::make_unique<TerminalApiDaemon>();
|
||
BOOST_LOG_TRIVIAL(info) << "api_driver::ApiDriver::startDaemon(): API daemon succes started!";
|
||
}
|
||
}
|
||
|
||
std::string api_driver::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;
|
||
}
|
||
for (start_pos = 0; start_pos < str.size() && (str[start_pos] == ' ' || str[start_pos] == '\n' || str[start_pos] == '\t'); start_pos++) {}
|
||
size_t end_pos = str.size() - 1;
|
||
for (; end_pos > start_pos && end_pos != 0 && (str[end_pos] == ' ' || str[end_pos] == '\n' || str[end_pos] == '\t'); end_pos--) {}
|
||
return "\"" + str.substr(start_pos, end_pos - start_pos + 1) + "\"";
|
||
}
|
||
|
||
static 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(daemon->getDeviceInitState());
|
||
result << ",\n\"testState\":" << boolAsStr(daemon->isTest());
|
||
|
||
auto state = daemon->getState();
|
||
#ifdef MODEM_IS_SCPC
|
||
const bool isCinC = this->daemon->getIsCinC();
|
||
#endif
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"isCinC\":" << boolAsStr(isCinC);
|
||
#endif
|
||
|
||
// формируем структуру для TX
|
||
result << ",\n\"tx.state\":" << boolAsStr(state.mod.is_tx_on);
|
||
result << ",\"tx.modcod\":" << state.mod.modcod;
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"tx.snr\":"; writeDouble(result, state.mod.snr_remote);
|
||
|
||
if (state.mod.is_short) { result << R"(,"tx.frameSizeNormal":false)"; }
|
||
else { result << R"(,"tx.frameSizeNormal":true)"; }
|
||
|
||
if (state.mod.is_pilots) { result << R"(,"tx.isPilots":true)"; }
|
||
else { result << R"(,"tx.isPilots":false)"; }
|
||
#else
|
||
{
|
||
modulator_settings modSet{};
|
||
daemon->getSettings(&modSet, nullptr, nullptr, nullptr);
|
||
result << ",\"tx.centerFreq\":"; writeDouble(result, modSet.central_freq_in_kGz);
|
||
result << ",\"tx.symSpeed\":"; writeDouble(result, (static_cast<double>(modSet.baudrate) / 1000.0));
|
||
}
|
||
#endif
|
||
result << ",\"tx.speedOnTxKbit\":"; writeDouble(result, static_cast<double>(state.mod.speed_in_bytes_tx) / 128.0);
|
||
result << ",\"tx.speedOnIifKbit\":"; writeDouble(result, (static_cast<double>(state.mod.speed_in_bytes_tx_iface) / 128.0));
|
||
|
||
// формируем структуру для RX
|
||
result << ",\n\"rx.state\":" << boolAsStr(state.demod.locks.sym_sync_lock && state.demod.locks.freq_lock && state.demod.locks.afc_lock && state.demod.locks.pkt_sync);
|
||
result << ",\"rx.sym_sync_lock\":" << boolAsStr(state.demod.locks.sym_sync_lock);
|
||
result << ",\"rx.freq_search_lock\":" << boolAsStr(state.demod.locks.freq_lock);
|
||
result << ",\"rx.afc_lock\":" << boolAsStr(state.demod.locks.afc_lock);
|
||
result << ",\"rx.pkt_sync\":" << boolAsStr(state.demod.locks.pkt_sync);
|
||
|
||
result << ",\"rx.snr\":"; writeDouble(result, state.demod.snr);
|
||
result << ",\"rx.rssi\":"; writeDouble(result, state.demod.rssi);
|
||
result << ",\"rx.modcod\":" << state.demod.modcod;
|
||
|
||
if (state.demod.is_short) {
|
||
result << R"(,"rx.frameSizeNormal":false)";
|
||
} else {
|
||
result << R"(,"rx.frameSizeNormal":true)";
|
||
}
|
||
|
||
if (state.demod.is_pilots) {
|
||
result << R"(,"rx.isPilots":true)";
|
||
} else {
|
||
result << R"(,"rx.isPilots":false)";
|
||
}
|
||
|
||
result << ",\n\"rx.symError\":"; writeDouble(result, state.demod.sym_err);
|
||
result << ",\"rx.freqErr\":"; writeDouble(result, state.demod.crs_freq_err);
|
||
result << ",\"rx.freqErrAcc\":"; writeDouble(result, state.demod.fine_freq_err);
|
||
result << ",\"rx.inputSignalLevel\":"; writeDouble(result, state.demod.if_overload);
|
||
result << ",\"rx.pllError\":"; writeDouble(result, state.demod.afc_err);
|
||
result << ",\"rx.speedOnRxKbit\":"; writeDouble(result, static_cast<double>(state.demod.speed_in_bytes_rx) / 128.0);
|
||
result << ",\"rx.speedOnIifKbit\":"; writeDouble(result, static_cast<double>(state.demod.speed_in_bytes_rx_iface) / 128.0);
|
||
result << ",\"rx.packetsOk\":" << state.demod.packet_ok_cnt;
|
||
result << ",\"rx.packetsBad\":" << state.demod.packet_bad_cnt;
|
||
result << ",\"rx.packetsDummy\":" << state.demod.dummy_cnt;
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
// формируем структуру для CinC
|
||
if (isCinC) {
|
||
if (state.mod.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)";
|
||
}
|
||
#endif
|
||
|
||
// структура температур девайса
|
||
result << ",\n\"device.adrv\":"; writeDouble(result, state.adrv_temp, 1);
|
||
result << ",\"device.fpga\":"; writeDouble(result, state.pl_temp, 1);
|
||
result << ",\"device.zynq\":"; writeDouble(result, state.zynq_temp, 1);
|
||
#ifdef MODEM_IS_TDMA
|
||
if (state.otaImage.empty()) {
|
||
result << R"(,
|
||
"device.upgradeStatus":"Нет обновлений","device.upgradePercent":0,"device.upgradeImage":"")";
|
||
} else {
|
||
switch (state.otaStatus) {
|
||
case NORM_RX_OBJECT_NEW_API: result << ",\n" R"("device.upgradeStatus": "Начало загрузки")"; break;
|
||
case NORM_RX_OBJECT_INFO_API: result << ",\n" R"("device.upgradeStatus": "Получено имя образа")"; break;
|
||
case NORM_RX_OBJECT_UPDATED_API: result << ",\n" R"("device.upgradeStatus": "Загружается")"; break;
|
||
case NORM_RX_OBJECT_COMPLETED_API: result << ",\n" R"("device.upgradeStatus": "Загрузка завершена")"; break;
|
||
case NORM_RX_OBJECT_ABORTED_API: result << ",\n" R"("device.upgradeStatus": "Загрузка прервана")"; break;
|
||
default: result << ",\n" R"("device.upgradeStatus": "?")";
|
||
|
||
}
|
||
result << ",\"device.upgradePercent\":" << state.otaPercent;
|
||
result << ",\"device.upgradeImage\":" << buildEscapedString(state.otaImage);
|
||
}
|
||
|
||
#endif
|
||
|
||
result << "}";
|
||
|
||
return result.str();
|
||
}
|
||
|
||
|
||
void api_driver::ApiDriver::resetPacketStatistics() const {
|
||
this->daemon->resetPacketStatistics();
|
||
}
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
struct ModcodDef_t {const char* modulation; const char* speed;};
|
||
const static ModcodDef_t ModcodDefs[] = {
|
||
{.modulation = "dummy", .speed = "0"},
|
||
{.modulation = "qpsk", .speed = "1/4"},
|
||
{.modulation = "qpsk", .speed = "1/3"},
|
||
{.modulation = "qpsk", .speed = "2/5"},
|
||
{.modulation = "qpsk", .speed = "1/2"},
|
||
{.modulation = "qpsk", .speed = "3/5"},
|
||
{.modulation = "qpsk", .speed = "2/3"},
|
||
{.modulation = "qpsk", .speed = "3/4"},
|
||
{.modulation = "qpsk", .speed = "4/5"},
|
||
{.modulation = "qpsk", .speed = "5/6"},
|
||
{.modulation = "qpsk", .speed = "8/9"},
|
||
{.modulation = "qpsk", .speed = "9/10"},
|
||
{.modulation = "8psk", .speed = "3/5"},
|
||
{.modulation = "8psk", .speed = "2/3"},
|
||
{.modulation = "8psk", .speed = "3/4"},
|
||
{.modulation = "8psk", .speed = "5/6"},
|
||
{.modulation = "8psk", .speed = "8/9"},
|
||
{.modulation = "8psk", .speed = "9/10"},
|
||
{.modulation = "16apsk", .speed = "2/3"},
|
||
{.modulation = "16apsk", .speed = "3/4"},
|
||
{.modulation = "16apsk", .speed = "4/5"},
|
||
{.modulation = "16apsk", .speed = "5/6"},
|
||
{.modulation = "16apsk", .speed = "8/9"},
|
||
{.modulation = "16apsk", .speed = "9/10"},
|
||
{.modulation = "32apsk", .speed = "3/4"},
|
||
{.modulation = "32apsk", .speed = "4/5"},
|
||
{.modulation = "32apsk", .speed = "5/6"},
|
||
{.modulation = "32apsk", .speed = "8/9"},
|
||
{.modulation = "32apsk", .speed = "9/10"},
|
||
};
|
||
|
||
static const char* extractModcodModulation(uint32_t modcod, bool defaultQpsk1_4 = true) {
|
||
modcod >>= 2;
|
||
const auto* d = defaultQpsk1_4 ? ModcodDefs : ModcodDefs + 1;
|
||
if (modcod < (sizeof(ModcodDefs) / sizeof(ModcodDef_t))) {
|
||
d = ModcodDefs + modcod;
|
||
}
|
||
return d->modulation;
|
||
}
|
||
|
||
static const char* extractModcodSpeed(uint32_t modcod, bool defaultQpsk1_4 = true) {
|
||
modcod >>= 2;
|
||
const auto* d = defaultQpsk1_4 ? ModcodDefs : ModcodDefs + 1;
|
||
if (modcod < (sizeof(ModcodDefs) / sizeof(ModcodDef_t))) {
|
||
d = ModcodDefs + modcod;
|
||
}
|
||
return d->speed;
|
||
}
|
||
#endif
|
||
|
||
std::string api_driver::ApiDriver::loadSettings() const {
|
||
if (daemon == nullptr) {
|
||
return R"({"error": "api daemon not started!"})";
|
||
}
|
||
|
||
modulator_settings modSettings{};
|
||
demodulator_settings demodSettings{};
|
||
buc_lnb_settings bucLnb{};
|
||
TerminalNetworkSettings network;
|
||
DPDI_parmeters dpdiSettings{};
|
||
#ifdef MODEM_IS_SCPC
|
||
ACM_parameters_serv_ acmSettings{};
|
||
daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb);
|
||
#else
|
||
daemon->getSettings(&modSettings, &demodSettings, &dpdiSettings, &bucLnb);
|
||
#endif
|
||
daemon->getNetworkSettings(network);
|
||
|
||
std::stringstream result;
|
||
result << "{\n\"txAutoStart\":" << boolAsStr(modSettings.is_save_current_state);
|
||
result << ",\"txEn\":" << boolAsStr(modSettings.tx_is_on);
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"txIsTestInput\":" << boolAsStr(modSettings.is_test_data);
|
||
#endif
|
||
result << ",\"txModulatorIsTest\":" << boolAsStr(!modSettings.is_carrier);
|
||
result << ",\"txCentralFreq\":"; writeDouble(result, modSettings.central_freq_in_kGz);
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"txBaudrate\":" << modSettings.baudrate;
|
||
result << ",\"txRolloff\":" << static_cast<int>(modSettings.rollof);
|
||
result << ",\"txGoldan\":" << static_cast<int>(modSettings.gold_seq_is_active);
|
||
#endif
|
||
result << ",\"txAttenuation\":"; writeDouble(result, modSettings.attenuation);
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\n\"isCinC\":" << boolAsStr(modSettings.is_cinc);
|
||
result << ",\n\"dvbServicePacketPeriod\":" << acmSettings.period_pack_acm;
|
||
result << ",\"dvbIsAcm\":" << boolAsStr(acmSettings.enable_acm);
|
||
result << ",\"txFrameSizeNormal\":" << boolAsStr((modSettings.modcod_tx & 2) == 0);
|
||
result << ",\"txIsPilots\":" << boolAsStr((modSettings.modcod_tx & 1) != 0);
|
||
|
||
result << R"(,"dvbCcmModulation":")" << extractModcodModulation(modSettings.modcod_tx) << "\"";
|
||
result << R"(,"dvbCcmSpeed":")" << extractModcodSpeed(modSettings.modcod_tx) << "\"";
|
||
result << R"(,"dvbAcmMinModulation":")" << extractModcodModulation(acmSettings.min_modcod_acm) << "\"";
|
||
result << R"(,"dvbAcmMinSpeed":")" << extractModcodSpeed(acmSettings.min_modcod_acm) << "\"";
|
||
result << R"(,"dvbAcmMaxModulation":")" << extractModcodModulation(acmSettings.max_modcod_acm) << "\"";
|
||
result << R"(,"dvbAcmMaxSpeed":")" << extractModcodSpeed(acmSettings.max_modcod_acm) << "\"";
|
||
result << ",\"dvbSnrReserve\":"; writeDouble(result, acmSettings.snr_threashold_acm);
|
||
|
||
result << ",\n\"aupcEn\":" << boolAsStr(acmSettings.enable_aupc);
|
||
result << ",\"aupcMinAttenuation\":"; writeDouble(result, acmSettings.min_attenuation_aupc);
|
||
result << ",\"aupcMaxAttenuation\":"; writeDouble(result, acmSettings.max_attenuation_aupc);
|
||
result << ",\"aupcRequiredSnr\":"; writeDouble(result, acmSettings.snr_threashold_aupc);
|
||
#endif
|
||
|
||
result << ",\n\"dpdiIsPositional\":" << boolAsStr(!dpdiSettings.is_delay_window);
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"dpdiSearchBandwidth\":" << dpdiSettings.freq_offset; // полоса поиска в кГц
|
||
#endif
|
||
result << ",\"dpdiPositionStationLatitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.latitude_station_grad, dpdiSettings.latitude_station_minute), 6);
|
||
result << ",\"dpdiPositionStationLongitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.longitude_station_grad, dpdiSettings.longitude_station_minute), 6);
|
||
result << ",\"dpdiPositionSatelliteLongitude\":"; writeDouble(result, translateCoordinates(dpdiSettings.longitude_sattelite_grad, dpdiSettings.longitude_sattelite_minute), 6);
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"dpdiDelayMin\":" << dpdiSettings.min_delay;
|
||
result << ",\"dpdiDelayMax\":" << dpdiSettings.max_delay;
|
||
#else
|
||
result << ",\"dpdiDelay\":" << dpdiSettings.max_delay;
|
||
#endif
|
||
|
||
result << ",\n\"rxAgcEn\":" << boolAsStr(demodSettings.is_aru_on);
|
||
result << ",\"rxSpectrumInversion\":" << boolAsStr(demodSettings.is_rvt_iq);
|
||
result << ",\"rxManualGain\":"; writeDouble(result, demodSettings.gain);
|
||
result << ",\"rxCentralFreq\":"; writeDouble(result, demodSettings.central_freq_in_kGz);
|
||
result << ",\"rxBaudrate\":" << demodSettings.baudrate;
|
||
result << ",\"rxRolloff\":" << static_cast<int>(demodSettings.rollof);
|
||
#ifdef MODEM_IS_SCPC
|
||
result << ",\"rxGoldan\":" << static_cast<int>(demodSettings.gold_seq_is_active);
|
||
#endif
|
||
|
||
// BUC LNB
|
||
result << ",\n\"bucRefClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_buc);
|
||
switch (bucLnb.buc) {
|
||
case voltage_buc::_24V: result << ",\"bucPowering\":24"; break;
|
||
#ifdef MODEM_IS_SCPC
|
||
case voltage_buc::_48V: result << ",\"bucPowering\":48"; break;
|
||
#endif
|
||
case voltage_buc::DISABLE:
|
||
default: result << ",\"bucPowering\":0";
|
||
}
|
||
|
||
result << ",\"lnbRefClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_lnb);
|
||
switch (bucLnb.lnb) {
|
||
case voltage_lnb::_13V: result << ",\"lnbPowering\":13"; break;
|
||
case voltage_lnb::_18V: result << ",\"lnbPowering\":18"; break;
|
||
case voltage_lnb::_24V: result << ",\"lnbPowering\":24"; break;
|
||
case voltage_lnb::DISABLE:
|
||
default: result << ",\"lnbPowering\":0";
|
||
}
|
||
|
||
result << ",\"srvRefClk10M\":" << boolAsStr(bucLnb.is_ref_10MHz_output);
|
||
result << ",\"bucLnbAutoStart\":" << boolAsStr(bucLnb.is_save_current_state);
|
||
|
||
// QoS
|
||
bool qosEnabled = false; std::string qosClasses;
|
||
daemon->getQosSettings(qosEnabled, qosClasses);
|
||
result << ",\n\"qosEnabled\":" << boolAsStr(qosEnabled);
|
||
result << ",\"qosProfile\":" << qosClasses;
|
||
|
||
// сеть
|
||
result << "\n,\"netManagementIp\":" << buildEscapedString(network.managementIp);
|
||
result << ",\"netIsL2\":" << boolAsStr(network.isL2);
|
||
result << ",\"netManagementGateway\":" << buildEscapedString(network.managementGateway);
|
||
result << ",\"netDataIp\":" << buildEscapedString(network.dataIp);
|
||
result << ",\"netDataMtu\":" << network.dataMtu;
|
||
result << ",\"netServerName\":" << buildEscapedString(network.serverName);
|
||
|
||
result << "}";
|
||
return result.str();
|
||
}
|
||
|
||
std::string api_driver::ApiDriver::loadFirmwareVersion() const {
|
||
if (daemon == nullptr) {
|
||
return R"({"error": "api daemon not started!"})";
|
||
}
|
||
|
||
std::stringstream result;
|
||
auto firmware = daemon->getFirmware();
|
||
result << "{\n\"fw.version\":" << buildEscapedString(firmware.version);
|
||
result << ",\"fw.modemId\":" << buildEscapedString(firmware.modemId);
|
||
result << ",\"fw.modemSn\":" << buildEscapedString(firmware.modemSn);
|
||
result << ",\"fw.macMang\":" << buildEscapedString(firmware.macMang);
|
||
result << ",\"fw.macData\":" << buildEscapedString(firmware.macData);
|
||
result << "\n}";
|
||
return result.str();
|
||
}
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
static uint32_t buildModcodFromPt(const boost::property_tree::ptree& pt, const std::string& name, bool isShortFrame, bool isPilots = false) {
|
||
uint32_t modcod = 0;
|
||
const auto mod = pt.get<std::string>(name + "Modulation");
|
||
const auto speed = pt.get<std::string>(name + "Speed");
|
||
uint32_t _index = 0;
|
||
for (const auto& m: ModcodDefs) {
|
||
if (mod == m.modulation) {
|
||
if (modcod == 0) modcod = _index;
|
||
if (speed == m.speed) {
|
||
modcod = _index;
|
||
break;
|
||
}
|
||
}
|
||
_index++;
|
||
}
|
||
return (modcod << 2)| (isShortFrame ? 2 : 0) | (isPilots ? 1 : 0);
|
||
}
|
||
#endif
|
||
|
||
void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) {
|
||
modulator_settings mod{};
|
||
demodulator_settings demod{};
|
||
#ifdef MODEM_IS_SCPC
|
||
ACM_parameters_serv_ acm{};
|
||
daemon->getSettings(&mod, &demod, &acm, nullptr, nullptr);
|
||
#else
|
||
daemon->getSettings(&mod, &demod, nullptr, nullptr);
|
||
#endif
|
||
|
||
// для модулятора
|
||
#ifdef MODEM_IS_SCPC
|
||
mod.is_cinc = pt.get<bool>("isCinC");
|
||
#endif
|
||
mod.tx_is_on = pt.get<bool>("txEn");
|
||
#ifdef MODEM_IS_SCPC
|
||
mod.is_save_current_state = pt.get<bool>("txAutoStart");
|
||
mod.is_test_data = pt.get<bool>("txIsTestInput");
|
||
#endif
|
||
mod.is_carrier = !pt.get<bool>("txModulatorIsTest");
|
||
mod.central_freq_in_kGz = pt.get<double>("txCentralFreq");
|
||
#ifdef MODEM_IS_SCPC
|
||
mod.baudrate = pt.get<uint32_t>("txBaudrate");
|
||
mod.rollof = pt.get<unsigned int>("txRolloff");
|
||
mod.gold_seq_is_active = pt.get<unsigned int>("txGoldan");
|
||
#endif
|
||
mod.attenuation = pt.get<double>("txAttenuation");
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
const bool acmIsShortFrame = !pt.get<bool>("txFrameSizeNormal");
|
||
const bool acmIsPilots = pt.get<bool>("txIsPilots");
|
||
mod.modcod_tx = buildModcodFromPt(pt, "dvbCcm", acmIsShortFrame, acmIsPilots);
|
||
#endif
|
||
|
||
// демодулятор
|
||
demod.is_aru_on = pt.get<bool>("rxAgcEn");
|
||
demod.gain = pt.get<double>("rxManualGain");
|
||
demod.is_rvt_iq = pt.get<bool>("rxSpectrumInversion");
|
||
demod.central_freq_in_kGz = pt.get<double>("rxCentralFreq");
|
||
demod.baudrate = pt.get<uint32_t>("rxBaudrate");
|
||
demod.rollof = pt.get<unsigned int>("rxRolloff");
|
||
#ifdef MODEM_IS_SCPC
|
||
demod.gold_seq_is_active = pt.get<unsigned int>("rxGoldan");
|
||
#endif
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
// ACM
|
||
acm.period_pack_acm = pt.get<uint32_t>("dvbServicePacketPeriod");
|
||
acm.enable_acm = pt.get<bool>("dvbIsAcm");
|
||
#ifdef MODEM_IS_SCPC
|
||
acm.min_modcod_acm = buildModcodFromPt(pt, "dvbAcmMin", acmIsShortFrame, acmIsPilots);
|
||
acm.max_modcod_acm = buildModcodFromPt(pt, "dvbAcmMax", acmIsShortFrame, acmIsPilots);
|
||
#else
|
||
acm.min_modcod_acm = buildModcodFromPt(pt, "dvbAcmMin", acmIsShortFrame);
|
||
acm.max_modcod_acm = buildModcodFromPt(pt, "dvbAcmMax", acmIsShortFrame);
|
||
#endif
|
||
acm.snr_threashold_acm = pt.get<double>("dvbSnrReserve"); // запас ОСШ
|
||
acm.enable_aupc = pt.get<bool>(json_path("aupcEn", '/'));
|
||
acm.min_attenuation_aupc = pt.get<int>("aupcMinAttenuation");
|
||
acm.max_attenuation_aupc = pt.get<int>("aupcMaxAttenuation");
|
||
acm.snr_threashold_aupc= pt.get<double>("aupcRequiredSnr");
|
||
|
||
daemon->setSettingsRxTx(mod, demod, acm);
|
||
#else
|
||
daemon->setSettingsRxTx(mod, demod);
|
||
#endif
|
||
}
|
||
|
||
void api_driver::ApiDriver::setDpdiSettings(boost::property_tree::ptree &pt) {
|
||
DPDI_parmeters s{};
|
||
#ifdef MODEM_IS_SCPC
|
||
this->daemon->getSettings(nullptr, nullptr, nullptr, &s, nullptr);
|
||
#else
|
||
this->daemon->getSettings(nullptr, nullptr, &s, nullptr);
|
||
#endif
|
||
|
||
s.is_delay_window = !pt.get<bool>("dpdiIsPositional");
|
||
#ifdef MODEM_IS_SCPC
|
||
s.freq_offset = pt.get<uint32_t>("dpdiSearchBandwidth");
|
||
#endif
|
||
|
||
auto ctmp = translateCoordinates(pt.get<double>("dpdiPositionStationLatitude"));
|
||
s.latitude_station_grad = std::get<0>(ctmp);
|
||
s.latitude_station_minute = std::get<1>(ctmp);
|
||
|
||
ctmp = translateCoordinates(pt.get<double>("dpdiPositionStationLongitude"));
|
||
s.longitude_station_grad = std::get<0>(ctmp);
|
||
s.longitude_station_minute = std::get<1>(ctmp);
|
||
|
||
ctmp = translateCoordinates(pt.get<double>("dpdiPositionSatelliteLongitude"));
|
||
s.longitude_sattelite_grad = std::get<0>(ctmp);
|
||
s.longitude_sattelite_minute = std::get<1>(ctmp);
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
s.min_delay = pt.get<uint32_t>("dpdiDelayMin");
|
||
s.max_delay = pt.get<uint32_t>("dpdiDelayMax");
|
||
#else
|
||
s.min_delay = 0;
|
||
s.max_delay = pt.get<uint32_t>("dpdiDelay");
|
||
#endif
|
||
|
||
this->daemon->setSettingsDpdi(s);
|
||
}
|
||
|
||
void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) {
|
||
buc_lnb_settings s{};
|
||
#ifdef MODEM_IS_SCPC
|
||
daemon->getSettings(nullptr, nullptr, nullptr, nullptr, &s);
|
||
#else
|
||
daemon->getSettings(nullptr, nullptr, nullptr, &s);
|
||
#endif
|
||
|
||
auto tmp = pt.get<int>("bucPowering");
|
||
switch (tmp) {
|
||
case 24: s.buc = voltage_buc::_24V; break;
|
||
#ifdef MODEM_IS_SCPC
|
||
case 48: s.buc = voltage_buc::_48V; break;
|
||
#endif
|
||
case 0:
|
||
default:
|
||
s.buc = voltage_buc::DISABLE;
|
||
}
|
||
s.is_ref_10MHz_buc = pt.get<bool>("bucRefClk10M");
|
||
|
||
tmp = pt.get<int>("lnbPowering");
|
||
switch (tmp) {
|
||
case 13: s.lnb = voltage_lnb::_13V; break;
|
||
case 18: s.lnb = voltage_lnb::_18V; break;
|
||
case 24: s.lnb = voltage_lnb::_24V; break;
|
||
case 0:
|
||
default:
|
||
s.lnb = voltage_lnb::DISABLE;
|
||
}
|
||
s.is_ref_10MHz_lnb = pt.get<bool>("lnbRefClk10M");
|
||
|
||
s.is_ref_10MHz_output = pt.get<bool>("srvRefClk10M");
|
||
s.is_save_current_state = pt.get<bool>("bucLnbAutoStart");
|
||
|
||
this->daemon->setSettingsBucLnb(s);
|
||
}
|
||
|
||
void api_driver::ApiDriver::setQosSettings(boost::property_tree::ptree &pt) {
|
||
bool enabled = pt.get<bool>("en");
|
||
pt.erase("en");
|
||
|
||
std::ostringstream oss;
|
||
write_json(oss, pt);
|
||
|
||
this->daemon->setQosSettings(enabled, oss.str());
|
||
}
|
||
|
||
void api_driver::ApiDriver::setNetworkSettings(boost::property_tree::ptree &pt) {
|
||
TerminalNetworkSettings s;
|
||
daemon->getNetworkSettings(s);
|
||
|
||
s.managementIp = pt.get<std::string>("netManagementIp");
|
||
// s.managementGateway = pt.get<std::string>(json_path("network.managementGateway", '/'));
|
||
s.isL2 = pt.get<bool>("netIsL2");
|
||
s.dataIp = pt.get<std::string>("netDataIp");
|
||
s.dataMtu = pt.get<unsigned int>("netDataMtu");
|
||
s.serverName = pt.get<std::string>("netServerName");
|
||
|
||
daemon->setNetworkSettings(s);
|
||
}
|
||
|
||
void api_driver::ApiDriver::resetDefaultSettings() {
|
||
daemon->resetDefaultSettings();
|
||
}
|
||
|
||
void api_driver::ApiDriver::executeInApi(const std::function<void(TSID sid)>& callback) {
|
||
try {
|
||
std::lock_guard lock(this->daemon->cpApiMutex);
|
||
callback(this->daemon->sid);
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(error) << "ApiDriver::executeInApi(): failed to exec with error: " << e.what();
|
||
}
|
||
}
|
||
|
||
#ifdef MODEM_IS_TDMA
|
||
std::string api_driver::ApiDriver::getOtaFileLocation() const {
|
||
return daemon->getState().otaImage;
|
||
}
|
||
#endif
|
||
|
||
#ifdef MODEM_IS_SCPC
|
||
std::string api_driver::ApiDriver::getLoggingStatisticsSettings() {
|
||
return this->daemon->statsLogs.getSettings();
|
||
}
|
||
|
||
void api_driver::ApiDriver::setLoggingStatisticsSettings(boost::property_tree::ptree &pt) {
|
||
this->daemon->statsLogs.setSettings(pt);
|
||
}
|
||
#endif
|
||
|
||
api_driver::ApiDriver::~ApiDriver() = default;
|