init
This commit is contained in:
commit
8ced83c325
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
[submodule "third_party/imgui"]
|
||||
path = third_party/imgui
|
||||
url = https://www.nanako.site:49004/Nanako/imgui.git
|
||||
[submodule "third_party/cpp-httplib"]
|
||||
path = third_party/cpp-httplib
|
||||
url = https://github.com/yhirose/cpp-httplib.git
|
85
src/data_types.h
Normal file
85
src/data_types.h
Normal file
@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
inline struct session_data {
|
||||
std::string session_cookie;
|
||||
std::string bmc_ip;
|
||||
std::string csrf_token;
|
||||
static session_data from_json(const rapidjson::Value& obj) {
|
||||
return {
|
||||
.session_cookie = obj["SESSION_COOKIE"].GetString(),
|
||||
.bmc_ip = obj["BMC_IP_ADDR"].GetString(),
|
||||
.csrf_token = obj["CSRFTOKEN"].GetString()
|
||||
};
|
||||
}
|
||||
} session;
|
||||
|
||||
struct fan_info {
|
||||
int id;
|
||||
int present;
|
||||
int status;
|
||||
int speed_percent;
|
||||
int speed_rpm;
|
||||
static fan_info from_json(const rapidjson::Value& obj) {
|
||||
return {
|
||||
.id = obj["Id"].GetInt(),
|
||||
.present = obj["Present"].GetInt(),
|
||||
.status = obj["Status"].GetInt(),
|
||||
.speed_percent = obj["SpeedPercent"].GetInt(),
|
||||
.speed_rpm = obj["SpeedRPM"].GetInt()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct running_time {
|
||||
int days;
|
||||
int hours;
|
||||
static running_time from_json(const rapidjson::Value& obj) {
|
||||
return {
|
||||
.days = obj["Days"].GetInt(),
|
||||
.hours = obj["Hours"].GetInt(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct psu_status {
|
||||
int id;
|
||||
int present;
|
||||
int power_status;
|
||||
int temperature;
|
||||
int pwr_in_watts;
|
||||
int input_power;
|
||||
int input_volt;
|
||||
int output_volt;
|
||||
int input_current;
|
||||
int output_current;
|
||||
int err_status;
|
||||
std::string fw_version;
|
||||
int output_power_max;
|
||||
std::string mfr_model;
|
||||
std::string mfr_id;
|
||||
std::string sn;
|
||||
|
||||
static psu_status from_json(const rapidjson::Value& obj) {
|
||||
return {
|
||||
.id = obj["Id"].GetInt(),
|
||||
.present = obj["Present"].GetInt(),
|
||||
.power_status = obj["PowerStatus"].GetInt(),
|
||||
.temperature = obj["Temperature"].GetInt(),
|
||||
.pwr_in_watts = obj["PwrInWatts"].GetInt(),
|
||||
.input_power = obj["InputPower"].GetInt(),
|
||||
.input_volt = obj["InputVolt"].GetInt(),
|
||||
.output_volt = obj["OutputVolt"].GetInt(),
|
||||
.input_current = obj["InputCurrent"].GetInt(),
|
||||
.output_current = obj["OutputCurrent"].GetInt(),
|
||||
.err_status = obj["ErrStatus"].GetInt(),
|
||||
.fw_version = obj["FWVersion"].GetString(),
|
||||
.output_power_max = obj["OutputPowerMax"].GetInt(),
|
||||
.mfr_model = obj["MFRModel"].GetString(),
|
||||
.mfr_id = obj["MFRID"].GetString(),
|
||||
.sn = obj["SN"].GetString()
|
||||
};
|
||||
}
|
||||
};
|
33
src/main.cpp
Normal file
33
src/main.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "imgui_main.h"
|
||||
#include "widget/login.h"
|
||||
#include "widget/main_page.h"
|
||||
|
||||
std::string get_window_title() {
|
||||
return "Inspur BMC";
|
||||
}
|
||||
|
||||
// 配置imgui, 比如加载字体, 启用多视口等
|
||||
void configure_imgui(ImGuiIO& io) {
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
|
||||
io.ConfigViewportsNoAutoMerge = true;
|
||||
}
|
||||
|
||||
// 执行一些数据更新代码在这里
|
||||
void tick_imgui(float delta_time) {
|
||||
update_infos();
|
||||
}
|
||||
|
||||
// 执行imgui绘制(在tick_imgui之后执行)
|
||||
void draw_imgui(float delta_time) {
|
||||
ImGui::DockSpaceOverViewport();
|
||||
draw_login();
|
||||
draw_page();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
run_imgui();
|
||||
return 0;
|
||||
}
|
63
src/request.h
Normal file
63
src/request.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "httplib.h"
|
||||
#include "rapidjson/document.h"
|
||||
|
||||
inline httplib::SSLClient* cli = nullptr;
|
||||
inline httplib::Headers headers;
|
||||
|
||||
template<typename T>
|
||||
bool json_to_obj(const std::string& json, std::vector<T>& out) {
|
||||
if (json.empty())
|
||||
return false;
|
||||
rapidjson::Document d;
|
||||
d.Parse(json.c_str());
|
||||
|
||||
const auto& obj_array = d.GetArray();
|
||||
for (const auto& obj: obj_array) {
|
||||
out.push_back(T::from_json(obj));
|
||||
}
|
||||
return !out.empty();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool json_to_obj(const std::string& json, T& out) {
|
||||
std::vector<T> vec;
|
||||
const bool ok = json_to_obj<T>(json, vec);
|
||||
if (ok) {
|
||||
out = vec[0];
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
inline std::string post_request(const std::string& api, const httplib::Params& params) {
|
||||
if (!cli)
|
||||
return "";
|
||||
auto res = cli->Post("/rpc/" + api + ".asp", headers, params);
|
||||
if (!res || res->status != 200)
|
||||
return "";
|
||||
return parser_js(res->body);
|
||||
}
|
||||
|
||||
inline std::string get_request(const std::string& api) {
|
||||
if (!cli)
|
||||
return "";
|
||||
auto res = cli->Get("/rpc/" + api + ".asp", headers);
|
||||
if (!res || res->status != 200)
|
||||
return "";
|
||||
return parser_js(res->body);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool post_request(const std::string& api, const httplib::Params& params, T& out) {
|
||||
return json_to_obj(post_request(api, params), out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool get_request(const std::string& api, T& out) {
|
||||
return json_to_obj(get_request(api), out);
|
||||
}
|
||||
|
135
src/request_task.h
Normal file
135
src/request_task.h
Normal file
@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <httplib.h>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "data_types.h"
|
||||
#include "request.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define SIMPLE_GET_TASK(name, api, type) \
|
||||
class name : public get_task<type> { \
|
||||
public: \
|
||||
explicit name() : get_task(api) {} \
|
||||
void setup() {} \
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class task {
|
||||
public:
|
||||
using result_type = T;
|
||||
virtual ~task() = default;
|
||||
|
||||
explicit task(const char* api) : api_(api) {}
|
||||
std::future<T> async_process() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
return process();
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] const char* api() const {
|
||||
return api_;
|
||||
}
|
||||
protected:
|
||||
virtual T process() = 0;
|
||||
|
||||
const char* api_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class task_executor {
|
||||
public:
|
||||
template<typename ...Args>
|
||||
static auto run_task(Args&&... args) {
|
||||
static T task_instance;
|
||||
task_instance.setup(std::forward<Args>(args)...);
|
||||
return task_instance.async_process();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class post_task : public task<T> {
|
||||
public:
|
||||
explicit post_task(const char* api) : task<T>(api) {
|
||||
}
|
||||
|
||||
void set_params(const httplib::Params& params) {
|
||||
params_ = params;
|
||||
}
|
||||
|
||||
protected:
|
||||
T process() override {
|
||||
T result;
|
||||
post_request(task<T>::api(), params_, result);
|
||||
return result;
|
||||
}
|
||||
httplib::Params params_;
|
||||
};
|
||||
|
||||
template<>
|
||||
inline void post_task<void>::process() {
|
||||
post_request(api(), params_);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class get_task : public task<T> {
|
||||
public:
|
||||
explicit get_task(const char* api) : task<T>(api) {
|
||||
}
|
||||
|
||||
protected:
|
||||
T process() override {
|
||||
T result;
|
||||
get_request(task<T>::api(), result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
class login_task : public post_task<session_data> {
|
||||
public:
|
||||
login_task() : post_task("WEBSES/create") {}
|
||||
|
||||
void setup(const std::string& ip, const char* username, const char* password) {
|
||||
ip_ = ip;
|
||||
httplib::Params params;
|
||||
params.emplace("WEBVAR_USERNAME", encode_content(username));
|
||||
params.emplace("WEBVAR_PASSWORD", encode_content(password));
|
||||
set_params(params);
|
||||
}
|
||||
protected:
|
||||
session_data process() override {
|
||||
delete cli;
|
||||
headers.clear();
|
||||
headers.emplace("Content-Type", "application/json;charset=UTF-8");
|
||||
|
||||
cli = new httplib::SSLClient(ip_); // host
|
||||
cli->enable_server_certificate_verification(false);
|
||||
|
||||
auto result = post_task::process();
|
||||
|
||||
headers.emplace("X-CSRFTOKEN", result.csrf_token);
|
||||
headers.emplace("Cookie", "test=1; SessionExpired=false; SessionCookie=" + result.session_cookie + "; BMP_IP_ADDR=" + result.bmc_ip + "; ");
|
||||
return result;
|
||||
}
|
||||
private:
|
||||
std::string ip_;
|
||||
};
|
||||
|
||||
class set_fan_speed_task : public post_task<void> {
|
||||
public:
|
||||
set_fan_speed_task() : post_task("setfanspeed") {}
|
||||
void setup(int fan_id, int speed_percent) {
|
||||
httplib::Params params;
|
||||
params.emplace("ID", std::to_string(fan_id));
|
||||
params.emplace("PERCENT", std::to_string(speed_percent));
|
||||
set_params(params);
|
||||
}
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
SIMPLE_GET_TASK(get_fan_info_task, "getfaninfo", std::vector<fan_info>)
|
||||
SIMPLE_GET_TASK(get_running_time_task, "getrunningtime", running_time)
|
||||
SIMPLE_GET_TASK(get_psu_info_task, "getallpsuinfo", std::vector<psu_status>)
|
50
src/utils.h
Normal file
50
src/utils.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include <iomanip>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <bits/ranges_algo.h>
|
||||
|
||||
// 定义包含需要编码的字符的集合
|
||||
const std::set<char> CUSTOM_ENCODE_SET = {
|
||||
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+',
|
||||
',', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']',
|
||||
'^', '`', '{', '|', '}', '~'
|
||||
};
|
||||
|
||||
// URL编码函数
|
||||
inline std::string encode_content(const std::string &value) {
|
||||
std::ostringstream escaped;
|
||||
escaped.fill('0');
|
||||
escaped << std::hex;
|
||||
|
||||
for (char c : value) {
|
||||
// 如果字符在自定义的编码集合中,进行百分号编码
|
||||
if (CUSTOM_ENCODE_SET.contains(c) || !isprint(c)) {
|
||||
escaped << '%' << std::setw(2) << int((unsigned char)c);
|
||||
} else {
|
||||
escaped << c;
|
||||
}
|
||||
}
|
||||
|
||||
return escaped.str();
|
||||
}
|
||||
|
||||
inline std::string parser_js(const std::string& in_js) {
|
||||
// 第一个正则表达式
|
||||
// std::regex re(R"(\[\s*\{[^{}]*\}\s*,\s*\{[^{}]*\}\s*\])");
|
||||
// std::regex re(R"(\[.*?\])", std::regex::dotall);
|
||||
std::regex bracket_regex(R"(\[([\s\S]*?)\])");
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_search(in_js, match, bracket_regex)) {
|
||||
std::string matched_str = match.str();
|
||||
// remove ", {}"
|
||||
if (auto f = matched_str.find(", {}"); f != std::string::npos)
|
||||
matched_str = matched_str.erase(f, 5);
|
||||
// replace ' with "
|
||||
std::ranges::replace(matched_str, '\'', '\"');
|
||||
return matched_str;
|
||||
}
|
||||
return "";
|
||||
}
|
51
src/widget/login.h
Normal file
51
src/widget/login.h
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
#include <future>
|
||||
|
||||
#include "data_types.h"
|
||||
#include "imgui.h"
|
||||
#include "request_task.h"
|
||||
#define BUF_SIZE 256
|
||||
|
||||
inline bool is_login = false;
|
||||
inline char ip_buf[BUF_SIZE] { "192.168.1.10" };
|
||||
inline char username_buf[BUF_SIZE] { "admin" };
|
||||
inline char password_buf[BUF_SIZE] { "admin" };
|
||||
inline std::future<session_data> login_future;
|
||||
|
||||
inline void draw_login() {
|
||||
if (is_login)
|
||||
return;
|
||||
ImGui::Begin("Login");
|
||||
ImGui::Text("BMC IP: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##bmc_ip", ip_buf, BUF_SIZE);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("User: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##user", username_buf, BUF_SIZE);
|
||||
ImGui::NewLine();
|
||||
|
||||
ImGui::Text("Password: ");
|
||||
ImGui::SameLine();
|
||||
ImGui::InputText("##password", password_buf, BUF_SIZE);
|
||||
ImGui::NewLine();
|
||||
|
||||
if (ImGui::Button("Login")) {
|
||||
if (!login_future.valid())
|
||||
login_future = task_executor<login_task>::run_task(ip_buf, username_buf, password_buf);
|
||||
}
|
||||
if (login_future.valid()) {
|
||||
ImGui::Text("Logging in...");
|
||||
if (login_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
session_data session = login_future.get();
|
||||
if (session.session_cookie.empty()) {
|
||||
ImGui::Text("Login failed");
|
||||
} else {
|
||||
is_login = true;
|
||||
ImGui::Text("Login success");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
106
src/widget/main_page.h
Normal file
106
src/widget/main_page.h
Normal file
@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "data_types.h"
|
||||
#include "imgui.h"
|
||||
#include "request_task.h"
|
||||
|
||||
#define FUTURE_PARAM(task_type, name) \
|
||||
inline std::future<task_type::result_type> name##_future; \
|
||||
inline task_type::result_type name##_; \
|
||||
inline void update_##name() { \
|
||||
if (name##_future.valid()) { \
|
||||
if (name##_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { \
|
||||
name##_ = name##_future.get(); \
|
||||
} \
|
||||
} else { \
|
||||
name##_future = task_executor<task_type>::run_task(); \
|
||||
} \
|
||||
}
|
||||
|
||||
FUTURE_PARAM(get_fan_info_task, fan_infos)
|
||||
FUTURE_PARAM(get_running_time_task, running_time)
|
||||
FUTURE_PARAM(get_psu_info_task, psu_status)
|
||||
|
||||
inline void update_infos() {
|
||||
update_fan_infos();
|
||||
update_running_time();
|
||||
update_psu_status();
|
||||
}
|
||||
|
||||
inline void draw_running_time() {
|
||||
ImGui::Text("Server running time: %d days %d hours", running_time_.days, running_time_.hours);
|
||||
}
|
||||
|
||||
inline void draw_fan_infos() {
|
||||
for (auto& fan_info : fan_infos_) {
|
||||
fan_info.status;
|
||||
|
||||
ImGui::Text("Fan %d: %d RPM", fan_info.id, fan_info.speed_rpm);
|
||||
std::string label = "Speed##" + std::to_string(fan_info.id);
|
||||
ImGui::InputInt(label.c_str(), &fan_info.speed_percent, 1, 100);
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
// 更新风扇转速
|
||||
fan_info.speed_percent = std::clamp(fan_info.speed_percent, 0, 100);
|
||||
auto future = task_executor<set_fan_speed_task>::run_task(fan_info.id, fan_info.speed_percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_psu_infos() {
|
||||
// 将psu信息绘制成表格
|
||||
ImGui::Columns(9, "psu_status");
|
||||
ImGui::Separator();
|
||||
ImGui::Text("ID");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Present");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Power Status");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Temperature");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Power In Watts");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Input Power");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Input Volt");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Output Volt");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("Input Current");
|
||||
ImGui::NextColumn();
|
||||
ImGui::Separator();
|
||||
for (const auto& psu : psu_status_) {
|
||||
ImGui::Text("%d", psu.id);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.present);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.power_status);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.temperature);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.pwr_in_watts);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.input_power);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.input_volt);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%f", psu.output_volt / 100.f);
|
||||
ImGui::NextColumn();
|
||||
ImGui::Text("%d", psu.input_current);
|
||||
ImGui::NextColumn();
|
||||
}
|
||||
}
|
||||
|
||||
inline void draw_page() {
|
||||
ImGui::Begin("Page");
|
||||
draw_running_time();
|
||||
ImGui::Separator();
|
||||
// 绘制可折叠
|
||||
if (ImGui::CollapsingHeader("Fan Infos")) {
|
||||
draw_fan_infos();
|
||||
}
|
||||
if (ImGui::CollapsingHeader("PSU Infos")) {
|
||||
draw_psu_infos();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
1
third_party/cpp-httplib
vendored
Submodule
1
third_party/cpp-httplib
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c8bcaf8a913f1ac087b076488e0cbbc272cccd19
|
1
third_party/imgui
vendored
Submodule
1
third_party/imgui
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 79b37f14318110c08d72c26003f622bf6bbdcb54
|
Loading…
x
Reference in New Issue
Block a user