298 lines
9.1 KiB
C++
298 lines
9.1 KiB
C++
#include <string>
|
|
#include "crow.h"
|
|
#include <iostream>
|
|
#include "json/json.h"
|
|
#include <sys/inotify.h>
|
|
#include <unistd.h>
|
|
#include <dirent.h>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <memory>
|
|
#include "../pPowerManger/SQLiteDB.h"
|
|
#include "../pPowerManger/pmSysvariable.h"
|
|
#include <MOOS/libMOOS/Comms/MOOSAsyncCommClient.h>
|
|
#include "DBImage.h"
|
|
|
|
using namespace std;
|
|
|
|
// 定义全局变量
|
|
ccuState ccustate;
|
|
StateData stateData;
|
|
mutex wsMutex;
|
|
vector<crow::websocket::connection*> wsConnections;
|
|
shared_ptr<SQLiteDB> db;
|
|
shared_ptr<MOOS::MOOSAsyncCommClient> moosClient;
|
|
shared_ptr<CDBImage> dbImage;
|
|
|
|
bool bContinue = true;
|
|
|
|
// WebSocket消息处理
|
|
void sendStateUpdate() {
|
|
crow::json::wvalue msg;
|
|
string time;
|
|
{
|
|
lock_guard<mutex> lock(wsMutex);
|
|
db->getStateData(stateData, time);
|
|
}
|
|
|
|
// 基本状态信息
|
|
msg["state"] = {
|
|
{"time", time},
|
|
{"state_id", stateData.state_id},
|
|
{"state_name", stateData.state_name},
|
|
{"event", stateData.event},
|
|
{"event_id", stateData.event_id},
|
|
{"fault_code", stateData.fault_code},
|
|
{"note", stateData.note}
|
|
};
|
|
|
|
// 数据库状态信息
|
|
msg["db_status"] = {
|
|
{"connected", db->isDatabaseConnected("/home/zjk/project/H100/H100PowerManger/data", "power_data.db")},
|
|
{"database", db->getDBPatch()},
|
|
{"size", db->getDatabaseSize()}
|
|
};
|
|
|
|
// MOOS数据库状态信息
|
|
msg["moos_db_status"] = {
|
|
{"connected", moosClient->IsConnected()},
|
|
{"host", "localhost"},
|
|
{"port", 9000}
|
|
};
|
|
|
|
// 进程详细信息
|
|
{
|
|
lock_guard<mutex> lock(wsMutex);
|
|
STRING_LIST processes;
|
|
dbImage->GetProcesses(processes);
|
|
|
|
crow::json::wvalue::list processList;
|
|
for(const auto& proc : processes) {
|
|
STRING_LIST subs, pubs;
|
|
if(dbImage->GetProcInfo(proc, subs, pubs)) {
|
|
crow::json::wvalue processInfo;
|
|
processInfo["name"] = proc;
|
|
// 将STRING_LIST转换为std::vector<std::string>
|
|
std::vector<std::string> subs_vec(subs.begin(), subs.end());
|
|
std::vector<std::string> pubs_vec(pubs.begin(), pubs.end());
|
|
processInfo["subscribed"] = subs_vec;
|
|
processInfo["published"] = pubs_vec;
|
|
processList.push_back(std::move(processInfo));
|
|
}
|
|
}
|
|
|
|
msg["processes"] = {
|
|
{"count", processes.size()},
|
|
{"list", std::move(processList)}
|
|
};
|
|
}
|
|
|
|
// 数据库变量信息
|
|
{
|
|
lock_guard<mutex> lock(wsMutex);
|
|
int varCount = dbImage->GetNumVariables();
|
|
CDBImage::CVar lastVar;
|
|
bool hasLastVar = (varCount > 0) && dbImage->Get(lastVar, varCount-1);
|
|
|
|
msg["variables"] = {
|
|
{"count", varCount},
|
|
{"last_updated", {
|
|
{"name", hasLastVar ? lastVar.GetName() : ""},
|
|
{"value", hasLastVar ? lastVar.GetValue() : ""},
|
|
{"time", hasLastVar ? lastVar.GetTime() : "0.0"}
|
|
}}
|
|
};
|
|
}
|
|
|
|
// 发送WebSocket消息
|
|
{
|
|
lock_guard<mutex> lock(wsMutex);
|
|
for(auto conn : wsConnections) {
|
|
conn->send_text(msg.dump());
|
|
}
|
|
}
|
|
}
|
|
|
|
// 订阅MOOS变量
|
|
bool FetchLoop(MOOS::MOOSAsyncCommClient* comms, CDBImage* m_DBImage)
|
|
{
|
|
int nCounts = 0;
|
|
while(true) // 外部需要控制循环退出条件
|
|
{
|
|
if(comms->IsConnected())
|
|
{
|
|
// 每5次循环获取一次进程摘要
|
|
if(nCounts++ % 5 == 0)
|
|
{
|
|
MOOSMSG_LIST procMail;
|
|
if(comms->ServerRequest("PROC_SUMMARY", procMail))
|
|
{
|
|
m_DBImage->SetProcInfo(procMail);
|
|
|
|
// 打印进程信息
|
|
STRING_LIST processes;
|
|
m_DBImage->GetProcesses(processes);
|
|
std::cout << "Active processes: " << processes.size() << std::endl;
|
|
}
|
|
}
|
|
|
|
// 每次循环都获取数据库摘要
|
|
MOOSMSG_LIST dbMail;
|
|
if(comms->ServerRequest("ALL", dbMail))
|
|
{
|
|
m_DBImage->Set(dbMail);
|
|
|
|
// 打印数据库变量信息
|
|
int varCount = m_DBImage->GetNumVariables();
|
|
std::cout << "Database variables: " << varCount << std::endl;
|
|
|
|
// 打印最近更新的变量
|
|
if(varCount > 0) {
|
|
CDBImage::CVar lastVar;
|
|
if(m_DBImage->Get(lastVar, varCount-1)) {
|
|
std::cout << "Last updated variable: "
|
|
<< lastVar.GetName() << " = "
|
|
<< lastVar.GetValue() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 保持大约4Hz的频率
|
|
MOOSPause(250);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int main() {
|
|
crow::SimpleApp app;
|
|
db = make_shared<SQLiteDB>("/home/zjk/project/H100/H100PowerManger/data/power_data.db");
|
|
moosClient = make_shared<MOOS::MOOSAsyncCommClient>();
|
|
dbImage = make_shared<CDBImage>();
|
|
moosClient->Run("localhost", 9000, "appPowerMangerWebUI");
|
|
|
|
// WebSocket路由
|
|
CROW_WEBSOCKET_ROUTE(app, "/ws/state")
|
|
.onopen([&](crow::websocket::connection& conn){
|
|
lock_guard<mutex> lock(wsMutex);
|
|
wsConnections.push_back(&conn);
|
|
cout << "New WebSocket connection" << endl;
|
|
})
|
|
.onclose([&](crow::websocket::connection& conn, const string& /*reason*/, uint16_t /*code*/){
|
|
lock_guard<mutex> lock(wsMutex);
|
|
wsConnections.erase(
|
|
remove(wsConnections.begin(), wsConnections.end(), &conn),
|
|
wsConnections.end());
|
|
cout << "WebSocket connection closed" << endl;
|
|
})
|
|
.onmessage([&](crow::websocket::connection& conn, const string& data, bool is_binary){
|
|
cout << "Received WebSocket message: " << data << endl;
|
|
});
|
|
|
|
// 状态更新定时器
|
|
thread([](){
|
|
while(true) {
|
|
sendStateUpdate();
|
|
this_thread::sleep_for(chrono::seconds(1));
|
|
}
|
|
}).detach();
|
|
|
|
//MOOS数据库更新定时器
|
|
std::thread fetchThread([&](){
|
|
while(bContinue) {
|
|
FetchLoop(moosClient.get(), dbImage.get());
|
|
}
|
|
});
|
|
|
|
// 绑定主页路由
|
|
CROW_ROUTE(app, "/")([]() {
|
|
auto page = crow::mustache::load_text("index.html");
|
|
return page;
|
|
});
|
|
|
|
// MOOS通知API
|
|
CROW_ROUTE(app, "/api/moos-notify")
|
|
.methods("POST"_method)
|
|
([](const crow::request& req) {
|
|
auto json = crow::json::load(req.body);
|
|
if (!json) {
|
|
return crow::response(400, "Invalid JSON");
|
|
}
|
|
|
|
string var = json["var"].s();
|
|
string key = json["key"].s();
|
|
|
|
if (var.empty() || key.empty()) {
|
|
return crow::response(400, "Var and Key are required");
|
|
}
|
|
|
|
try {
|
|
moosClient->Notify(var, key);
|
|
return crow::response(200, "{\"success\":true}");
|
|
} catch (const exception& e) {
|
|
return crow::response(500,
|
|
crow::json::wvalue{{"success", false}, {"message", e.what()}}.dump());
|
|
}
|
|
});
|
|
|
|
// 下载数据库API
|
|
CROW_ROUTE(app, "/api/download-db")
|
|
([]() {
|
|
const std::string dbPath = "/home/zjk/project/H100/H100PowerManger/data/power_data.db";
|
|
const size_t chunkSize = 4096; // 4KB chunks
|
|
|
|
std::ifstream file(dbPath, std::ios::binary | std::ios::ate);
|
|
if (!file) {
|
|
return crow::response(404, "Database file not found");
|
|
}
|
|
|
|
// 获取文件大小
|
|
std::streamsize fileSize = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
if (fileSize == 0) {
|
|
return crow::response(500, "Database file is empty");
|
|
}
|
|
|
|
// 创建响应
|
|
crow::response res;
|
|
res.code = 200;
|
|
res.set_header("Content-Type", "application/octet-stream");
|
|
res.set_header("Content-Disposition", "attachment; filename=power_data.db");
|
|
res.set_header("Content-Length", std::to_string(fileSize));
|
|
|
|
try {
|
|
// 分块读取和写入文件
|
|
auto buffer = std::make_unique<char[]>(chunkSize);
|
|
size_t totalSent = 0;
|
|
|
|
while (totalSent < fileSize) {
|
|
size_t remaining = fileSize - totalSent;
|
|
size_t readSize = std::min(remaining, chunkSize);
|
|
|
|
file.read(buffer.get(), readSize);
|
|
if (file.gcount() <= 0) break;
|
|
|
|
res.write(std::string(buffer.get(), file.gcount()));
|
|
totalSent += file.gcount();
|
|
}
|
|
|
|
file.close();
|
|
return res;
|
|
} catch (const std::exception& e) {
|
|
file.close();
|
|
return crow::response(500, std::string("Download failed: ") + e.what());
|
|
}
|
|
});
|
|
|
|
// 启动服务器
|
|
app.port(18080)
|
|
.multithreaded()
|
|
.concurrency(16)
|
|
.run();
|
|
|
|
return 0;
|
|
}
|