diff --git a/src/pPowerManger/httpserver/httpserver.cpp b/src/pPowerManger/httpserver/httpserver.cpp index dd833ca..a5569c6 100644 --- a/src/pPowerManger/httpserver/httpserver.cpp +++ b/src/pPowerManger/httpserver/httpserver.cpp @@ -54,6 +54,11 @@ void HttpServer::handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_ } } } + } else if (ev == MG_EV_WS_MSG) { + // WebSocket消息处理 + struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; + std::string msg(wm->data.buf, wm->data.len); + handleLogRequest(c, msg); } else if (ev == MG_EV_HTTP_MSG) { // HTTP请求处理 struct mg_http_message *hm = (struct mg_http_message *) ev_data; @@ -216,11 +221,18 @@ bool HttpServer::serverThreadFunc() { // 定期推送系统数据 static uint64_t last_push_time = 0; + static uint64_t last_log_check_time = 0; uint64_t now = mg_millis(); + if (now - last_push_time > 1000) { // 每秒推送一次 last_push_time = now; pushSystemData(); } + + if (now - last_log_check_time > 500) { // 每500ms检查一次日志 + last_log_check_time = now; + monitorLogFile(); + } } return true; } @@ -550,3 +562,154 @@ void HttpServer::stop() { bool HttpServer::isRunning() const { return running_; } + +void HttpServer::handleLogRequest(struct mg_connection* c, const std::string& msg) { + try { + Json::Value json; + Json::CharReaderBuilder builder; + std::unique_ptr reader(builder.newCharReader()); + const char* msg_cstr = msg.c_str(); + std::string errors; + + if (!reader->parse(msg_cstr, msg_cstr + msg.size(), &json, &errors)) { + LOG_F(ERROR, "Failed to parse log request JSON: %s", errors.c_str()); + return; + } + + std::string type = json["type"].asString(); + if (type == "get_logs") { + int page = json["page"].asInt(); + int pageSize = json["pageSize"].asInt(); + std::string level = json["filter"]["level"].asString(); + std::string search = json["filter"]["search"].asString(); + bool reverse = json["reverse"].asBool(); // 新增参数,控制是否倒序显示 + + auto logs = readLogFile(page, pageSize, level, search); + + Json::Value response; + response["type"] = "log"; + response["page"] = page; + response["pageSize"] = pageSize; + response["total"] = logs.size(); // 返回总条数 + response["content"] = Json::arrayValue; + + if (reverse) { + // 如果需要倒序,反转日志顺序 + for (auto it = logs.rbegin(); it != logs.rend(); ++it) { + response["content"].append(*it); + } + } else { + // 否则按原顺序返回 + for (const auto& log : logs) { + response["content"].append(log); + } + } + + Json::StreamWriterBuilder builder; + std::string responseStr = Json::writeString(builder, response); + mg_ws_send(c, responseStr.c_str(), responseStr.size(), WEBSOCKET_OP_TEXT); + } + } catch (const std::exception& e) { + LOG_F(ERROR, "Error handling log request: %s", e.what()); + } +} + +std::vector HttpServer::readLogFile(int page, int pageSize, const std::string& level, const std::string& search) { + std::vector logs; + std::ifstream file(m_logFilePath); + if (!file.is_open()) { + LOG_F(ERROR, "Failed to open log file: %s", m_logFilePath.c_str()); + return logs; + } + + // 获取文件大小 + file.seekg(0, std::ios::end); + std::streampos fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + + std::string line; + std::vector filteredLogs; + + // 读取并过滤所有行 + while (std::getline(file, line)) { + // 过滤日志级别和搜索内容 + if ((level.empty() || level == "ALL" || line.find(level) != std::string::npos) && + (search.empty() || line.find(search) != std::string::npos)) { + filteredLogs.push_back(line); + } + } + + // 计算总页数 + int totalLines = filteredLogs.size(); + + // 如果没有内容,直接返回空 + if (totalLines == 0) { + return logs; + } + + // 计算当前页的起始和结束索引 + int start = std::max(0, totalLines - (page * pageSize)); + int end = std::max(0, totalLines - ((page - 1) * pageSize)); + + // 返回当前页的日志(已经是倒序的) + return std::vector(filteredLogs.begin() + start, filteredLogs.begin() + end); +} + +void HttpServer::monitorLogFile() { + static std::streampos lastPos = 0; + std::ifstream file(m_logFilePath); + + if (!file.is_open()) { + LOG_F(ERROR, "Failed to open log file for monitoring: %s", m_logFilePath.c_str()); + return; + } + + // 获取文件大小 + file.seekg(0, std::ios::end); + std::streampos currentPos = file.tellg(); + + // 如果文件大小没有变化,直接返回 + if (currentPos == lastPos) { + return; + } + + // 如果是第一次读取或文件被截断,从头开始读 + if (currentPos < lastPos) { + lastPos = 0; + } + + // 移动到上次读取的位置 + file.seekg(lastPos); + + std::string line; + std::vector newLogs; + + // 读取新的日志行 + while (std::getline(file, line)) { + newLogs.push_back(line); + } + + // 更新最后读取位置 + lastPos = currentPos; + + // 如果有新日志,推送给所有客户端 + if (!newLogs.empty()) { + Json::Value json; + json["type"] = "new_log"; + json["content"] = Json::arrayValue; + + // 只保留最新的100行 + size_t start = newLogs.size() > 100 ? newLogs.size() - 100 : 0; + for (size_t i = start; i < newLogs.size(); ++i) { + json["content"].append(newLogs[i]); + } + + Json::StreamWriterBuilder builder; + std::string msg = Json::writeString(builder, json); + + std::lock_guard lock(ws_connections_mutex_); + for (auto c : ws_connections_) { + mg_ws_send(c, msg.c_str(), msg.size(), WEBSOCKET_OP_TEXT); + } + } +} diff --git a/src/pPowerManger/httpserver/httpserver.h b/src/pPowerManger/httpserver/httpserver.h index 0589c55..0df1f26 100644 --- a/src/pPowerManger/httpserver/httpserver.h +++ b/src/pPowerManger/httpserver/httpserver.h @@ -51,6 +51,12 @@ public: // 监控日志文件变化并推送新内容 void monitorLogFile(); + // 处理日志请求 + void handleLogRequest(struct mg_connection* c, const std::string& msg); + + // 读取日志文件 + std::vector readLogFile(int page, int pageSize, const std::string& level, const std::string& search); + private: // WebSocket事件处理函数 void handleWebSocketEvent(struct mg_connection* c, int ev, void* ev_data, void* fn_data); @@ -72,12 +78,6 @@ private: std::vector ws_connections_; // WebSocket连接列表 std::unordered_map ws_last_io_time_; // 连接最后活动时间 - // 读取日志文件 - std::vector readLogFile(int page, int pageSize, const std::string& level, const std::string& search); - - // 处理WebSocket消息 - void handleWebSocketMessage(struct mg_connection* c, const std::string& msg); - // 日志文件相关 std::string m_logFilePath = "pPowerManger.log"; std::ifstream m_logFile; @@ -87,4 +87,4 @@ private: }; -#endif // HTTP_SERVER_H \ No newline at end of file +#endif // HTTP_SERVER_H diff --git a/src/pPowerManger/httpserver/index.h b/src/pPowerManger/httpserver/index.h index c4b4fe3..90423e1 100644 --- a/src/pPowerManger/httpserver/index.h +++ b/src/pPowerManger/httpserver/index.h @@ -213,7 +213,11 @@ body {

Power Manager System

- 🟢 系统状态: 运行中 + 🟢 系统状态: 运行中 + ⚡ 应用频率: 0 Hz + 📡 通信频率: 0 Hz + 💻 CPU负载: 0% + ⏱️ 迭代间隔: 0ms 🕒 更新时间:
@@ -243,6 +247,15 @@ function initWebSocket() { ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('收到系统数据:', data); + + // 更新MOOSDB信息 + if (data.moosdb) { + document.getElementById('appFreq').textContent = data.moosdb.appFreq.toFixed(2); + document.getElementById('commFreq').textContent = data.moosdb.commFreq.toFixed(2); + document.getElementById('cpuLoad').textContent = data.moosdb.cpuLoad.toFixed(1); + document.getElementById('timeSinceIterate').textContent = data.moosdb.timeSinceIterate.toFixed(0); + } + updateTime(); }; diff --git a/src/pPowerManger/httpserver/logpage.h b/src/pPowerManger/httpserver/logpage.h index c6ed08b..12b19e1 100644 --- a/src/pPowerManger/httpserver/logpage.h +++ b/src/pPowerManger/httpserver/logpage.h @@ -373,17 +373,37 @@ function initWebSocket() { ws.onmessage = (event) => { const data = JSON.parse(event.data); if (data.type === 'log') { + // 初始加载的日志数据 + allLogs = data.content; + filterAndDisplayLogs(); + } else if (data.type === 'new_log') { + // 新的日志条目 if (Array.isArray(data.content)) { - // 初始加载的日志数据 - allLogs = data.content; - filterAndDisplayLogs(); - } else { - // 新的日志条目 - allLogs.push(data.content); - if (shouldShowLog(data.content)) { - appendLogEntry(data.content); + let shouldUpdate = false; + data.content.forEach(log => { + allLogs.push(log); + if (shouldShowLog(log)) { + filteredLogs.push(log); + shouldUpdate = true; + // 如果在最后一页或自动滚动模式下,直接添加到显示 + if (currentPage === totalPages || autoScroll) { + appendLogEntry(log); + } + } + }); + + if (shouldUpdate) { + totalPages = Math.ceil(filteredLogs.length / pageSize); + updatePagination(); + + // 如果开启了自动滚动,确保滚动到底部 + if (autoScroll) { + logContent.scrollTop = logContent.scrollHeight; + } } } + } else if (data.type === 'system_status') { + updateStatus(data.status, true); } updateTime(); }; @@ -391,7 +411,12 @@ function initWebSocket() { ws.onclose = () => { console.log('WebSocket连接已关闭'); updateStatus('已断开', false); - setTimeout(initWebSocket, 5000); + // 增加重连延迟,避免快速重连 + setTimeout(() => { + if (!ws || ws.readyState === WebSocket.CLOSED) { + initWebSocket(); + } + }, 2000); }; ws.onerror = (error) => { @@ -505,9 +530,9 @@ function appendLogEntry(content) { entry.className = 'log-entry'; // 根据日志内容设置不同的样式 - if (content.includes('ERROR')) { + if (content.includes('ERROR') || content.includes('ERR')) { entry.classList.add('error'); - } else if (content.includes('WARNING')) { + } else if (content.includes('WARNING') || content.includes('WARN')) { entry.classList.add('warning'); } else if (content.includes('INFO')) { entry.classList.add('info'); @@ -515,7 +540,8 @@ function appendLogEntry(content) { entry.textContent = content; logContent.appendChild(entry); - + + // 只在自动滚动模式下滚动到底部 if (autoScroll) { logContent.scrollTop = logContent.scrollHeight; }