diff --git a/src/main.cpp b/src/main.cpp index dfe9038..be6b59b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -212,6 +212,18 @@ public: rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); })); + s.resources.emplace_back(std::make_unique("/api/get/aboutFirmware", this->auth, 0, [this](const auto& req, auto& rep) { + if (req.method != "GET") { + http::server::stockReply(http::server::bad_request, rep); + } + + rep.status = http::server::ok; + rep.headers.clear(); + rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); + const auto result = api->loadFirmwareVersion(); + rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); + })); + s.resources.emplace_back(std::make_unique("/api/resetPacketStatistics", this->auth, http::auth::User::RESET_PACKET_STATISTICS, [this](const auto& req, auto& rep) { if (req.method != "POST") { http::server::stockReply(http::server::bad_request, rep); @@ -337,6 +349,62 @@ public: } })); + s.resources.emplace_back(std::make_unique("/api/set/network", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { + if (req.method != "POST") { + http::server::stockReply(http::server::bad_request, rep); + } + + rep.status = http::server::ok; + rep.headers.clear(); + rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); + + try { + std::stringstream ss; + ss.str(std::string(req.payload.begin(), req.payload.end())); + boost::property_tree::ptree pt; + read_json(ss, pt); + + api->setNetworkSettings(pt); + + std::string result = R"({"status":"ok","settings":)"; + result += api->loadSettings(); + result += "}"; + rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what(); + const std::string result = R"({"status":"error"})"; + rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); + } + })); + + s.resources.emplace_back(std::make_unique("/api/set/debugSend", this->auth, http::auth::User::EDIT_SETTINGS, [this](const auto& req, auto& rep) { + if (req.method != "POST") { + http::server::stockReply(http::server::bad_request, rep); + } + + rep.status = http::server::ok; + rep.headers.clear(); + rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); + + try { + std::stringstream ss; + ss.str(std::string(req.payload.begin(), req.payload.end())); + boost::property_tree::ptree pt; + read_json(ss, pt); + + api->setDebugSendSettings(pt); + + std::string result = R"({"status":"ok","settings":)"; + result += api->loadSettings(); + result += "}"; + rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); + } catch (std::exception& e) { + BOOST_LOG_TRIVIAL(error) << "WebHandle(/api/set/rxtx): Can't set RX/TX settings: " << e.what(); + const std::string result = R"({"status":"error"})"; + rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); + } + })); + s.resources.emplace_back(std::make_unique("/api/firmwareUpdate", this->auth, http::auth::User::UPDATE_FIRMWARE, [this](const auto& req, auto& rep) { if (req.method != "PUT") { http::server::stockReply(http::server::bad_request, rep); @@ -348,7 +416,7 @@ public: rep.headers.push_back({.name = "Content-Type", .value = toString(mime_types::json)}); std::string result = R"({"status":"ok","fwsize":)"; result += std::to_string(req.payload.size()); - result += ",\"sha256\":\""; + result += R"(,"sha256":")"; result += http::utils::sha256(req.payload.data(), req.payload.size()); result += "\"}"; rep.content.insert(rep.content.end(), result.c_str(), result.c_str() + result.size()); diff --git a/src/terminal_api_driver.cpp b/src/terminal_api_driver.cpp index e36e886..a6abb7b 100644 --- a/src/terminal_api_driver.cpp +++ b/src/terminal_api_driver.cpp @@ -30,6 +30,34 @@ static int calculateSubnetMask(const std::string& subnet_mask) { return mask; } +/** + * Преобразует строку вида `1.2.3.4/24` в пару строк вида `1.2.3.4` `255.255.255.0` + */ +std::pair splitIpAndMask(const std::string& input) { + auto pos = input.find('/'); + if (pos == std::string::npos) { + // Обработка ошибки: нет символа '/' + throw std::runtime_error("address not contains mask"); + } + std::string ip = input.substr(0, pos); + const unsigned int mask_int = std::stoul(input.substr(pos + 1)); + + if (mask_int > 32) { + throw std::runtime_error("invalid mask"); + } + + std::string mask_binary = std::string(mask_int, '1') + std::string(32 - mask_int, '0'); + std::string mask_str; + + for (unsigned int i = 0; i < 4; ++i) { + std::string octet = mask_binary.substr(i * 8u, 8); + int octet_value = std::stoi(octet, nullptr, 2); + mask_str += std::to_string(octet_value) + (i < 3 ? "." : ""); + } + + return std::make_pair(ip, mask_str); +} + class TerminalNetworkSettings { public: std::string managementIp, managementGateway, mode, dataIp; @@ -42,6 +70,17 @@ public: TerminalNetworkSettings& operator= (const TerminalNetworkSettings& src) = default; }; +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; +}; + /** * Этот демон нужен для того, чтобы получать статистику из API, а так же корректно сохранять настройки */ @@ -50,6 +89,26 @@ private: TSID sid; boost::thread daemon; + void updateFirmwareSettings() { + std::string version, chip_id, sn, mac0, mac1; + std::lock_guard lock(this->cpApiMutex); + + CP_GetNetwork(sid, "version", &version); + CP_GetNetwork(sid, "chip_id", &chip_id); + CP_GetNetwork(sid, "serial", &sn); + CP_GetNetwork(sid, "mac_eth0", &mac0); + CP_GetNetwork(sid, "mac_eth1", &mac1); + + { + std::lock_guard lock2(this->firmwareMutex); + this->firmware.version = version; + this->firmware.modemId = chip_id; + this->firmware.modemSn = sn; + this->firmware.macMang = mac0; + this->firmware.macData = mac1; + } + } + void updateStatistics() { modulator_state modulator{}; demodulator_state demodulator{}; @@ -132,6 +191,7 @@ private: void run() { // это демон, который в бесконечном цикле опрашивает API + updateFirmwareSettings(); struct IntervalUpdate_t { int64_t lastUpdate; @@ -175,10 +235,10 @@ private: // обновление кеша настроек сети (делается отдельно) {.lastUpdate = 0, .periodMs = CACHE_SETTINGS_UPDATE_MS, .callback = [this]() { try { - this->updateSettings(); - BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateSettings(): success update!"; + this->updateNetworkSettings(); + BOOST_LOG_TRIVIAL(debug) << "api_driver::TerminalApiDaemon::updateNetworkSettings(): success update!"; } catch (std::exception& e) { - BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateSettings(): " << e.what(); + BOOST_LOG_TRIVIAL(error) << "api_driver::TerminalApiDaemon::updateNetworkSettings(): " << e.what(); } }}, // обновление кеша QoS @@ -213,6 +273,9 @@ private: std::mutex cpApiMutex; + std::shared_mutex firmwareMutex; + TerminalFirmwareVersion firmware; + std::shared_mutex stateMutex; modulator_state modState{}; demodulator_state demodState{}; @@ -277,6 +340,11 @@ public: json = this->qosClassesJson; } + void getFirmwareVersion(TerminalFirmwareVersion& fw) { + std::shared_lock lock(this->settingsMutex); + fw = this->firmware; + } + void setSettingsRxTx(modulator_settings& mod, demodulator_settings& demod, ACM_parameters_serv_& acm, bool readback = true) { std::lock_guard lock(this->cpApiMutex); CP_SetDmaDebug(sid, "begin_save_config", ""); @@ -341,6 +409,50 @@ public: CP_SetDmaDebug(sid, "save_config", ""); } + void setNetworkSettings(TerminalNetworkSettings& s, bool readback = true) { + const auto [mAddr, mMask] = splitIpAndMask(s.managementIp); + const auto [dAddr, dMask] = splitIpAndMask(s.dataIp); + bool isL2; + if (s.mode == "l2") { isL2 = true; } + else if (s.mode == "l3") { isL2 = false; } + else { throw std::runtime_error("invalid mode"); } + + std::lock_guard lock(this->cpApiMutex); + CP_SetDmaDebug(sid, "begin_save_config", ""); + CP_SetNetwork(sid, "mode", isL2 ? "tap" : "tun"); + CP_SetNetwork(sid, "addr", mAddr.c_str()); + CP_SetNetwork(sid, "mask", mMask.c_str()); + CP_SetNetwork(sid, "gateway", s.managementGateway.c_str()); + if (!isL2) { + CP_SetNetwork(sid, "data_addr", dAddr.c_str()); + // TODO маска не устанавливается, потому что в API этого нет + } + // TODO MTU не устанавливается, потому что в API этого нет + + if (readback) { + std::string tmp; + CP_GetNetwork(sid, "addr", &s.managementIp); + CP_GetNetwork(sid, "mask", &tmp); + s.managementIp += "/"; + s.managementIp += std::to_string(calculateSubnetMask(tmp)); + CP_GetNetwork(sid, "gateway", &s.managementGateway); + tmp.clear(); CP_GetNetwork(sid, "mode", &tmp); + if (tmp == "tun") { + s.mode = "l3"; + CP_GetNetwork(sid, "addr_data", &s.dataIp); + } else { + s.mode = "l2"; + s.dataIp = "0.0.0.0/24"; + } + s.dataMtu = 1500; + { + std::lock_guard lock2(this->networkSettingsMutex); + this->networkSettings = s; + } + } + CP_SetDmaDebug(sid, "save_config", ""); + } + void resetPacketStatistics() { std::string tmp; std::lock_guard lock(this->cpApiMutex); @@ -527,13 +639,6 @@ std::string api_driver::ApiDriver::loadSettings() const { daemon->getSettings(&modSettings, &demodSettings, &acmSettings, &dpdiSettings, &bucLnb); daemon->getNetworkSettings(network); - // 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); @@ -610,6 +715,23 @@ std::string api_driver::ApiDriver::loadSettings() const { return result.str(); } +std::string api_driver::ApiDriver::loadFirmwareVersion() const { + if (daemon == nullptr) { + return R"({"error": "api daemon not started!"})"; + } + + TerminalFirmwareVersion firmware; + daemon->getFirmwareVersion(firmware); + std::stringstream result; + 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(); +} + void api_driver::ApiDriver::setRxTxSettings(boost::property_tree::ptree &pt) { modulator_settings mod{}; demodulator_settings demod{}; @@ -708,7 +830,6 @@ void api_driver::ApiDriver::setBucLnbSettings(boost::property_tree::ptree &pt) { default: s.lnb = voltage_lnb::DISABLE; } - // { "lnb": {"powering": 0} } s.is_ref_10MHz_buc = pt.get(json_path("buc.refClk10M", '/')); @@ -728,6 +849,21 @@ void api_driver::ApiDriver::setQosSettings(boost::property_tree::ptree &pt) { this->daemon->setQosSettings(enabled, oss.str()); } +void api_driver::ApiDriver::setNetworkSettings(boost::property_tree::ptree &pt) { + TerminalNetworkSettings s; + s.managementIp = pt.get(json_path("network.managementIp", '/')); + s.managementGateway = pt.get(json_path("network.managementGateway", '/')); + s.mode = pt.get(json_path("network.mode", '/')); + s.dataIp = pt.get(json_path("network.dataIp", '/')); + s.dataMtu = pt.get(json_path("network.dataMtu", '/')); + + daemon->setNetworkSettings(s); +} + +void api_driver::ApiDriver::setDebugSendSettings(boost::property_tree::ptree &pt) { + boost::ignore_unused(pt); +} + bool api_driver::ApiDriver::getIsCinC() const { modulator_settings s{}; daemon->getSettings(&s, nullptr, nullptr, nullptr, nullptr); diff --git a/src/terminal_api_driver.h b/src/terminal_api_driver.h index 8c2847b..4b2c819 100644 --- a/src/terminal_api_driver.h +++ b/src/terminal_api_driver.h @@ -39,6 +39,8 @@ namespace api_driver { std::string loadSettings() const; + std::string loadFirmwareVersion() const; + /** * Установить настройки RX/TX, readback можно получить используя loadTerminalState */ @@ -59,6 +61,10 @@ namespace api_driver { */ void setQosSettings(boost::property_tree::ptree &pt); + void setNetworkSettings(boost::property_tree::ptree & pt); + + void setDebugSendSettings(boost::property_tree::ptree & pt); + ~ApiDriver(); private: diff --git a/static/main.html b/static/main.html index e3179ed..9b50db1 100644 --- a/static/main.html +++ b/static/main.html @@ -21,6 +21,16 @@ #content { padding-top: var(--header-height); } + + .l3-proto-label { + margin: 0 0 0 0.5em; + } + .l3-proto-label > * { + display: inline-block; + } + .l3-proto-label input[type=checkbox] { + width: auto; + } @@ -482,21 +492,19 @@ -