diff --git a/bin/static/script.js b/bin/static/script.js index 55aecaf..4eae34e 100644 --- a/bin/static/script.js +++ b/bin/static/script.js @@ -130,11 +130,20 @@ function updateUI(data) { document.getElementById('state-note').textContent = data.state.note; // 更新DIS系统状态 - document.getElementById('dis-sys-state').textContent = JSON.stringify(data.status.disSys, null, 2); + // document.getElementById('dis-sys-state').textContent = JSON.stringify(data.status.disSys, null, 2); // 更新CCU状态 - document.getElementById('ccu-state').textContent = JSON.stringify(data.status.ccuState, null, 2); + // document.getElementById('ccu-state').textContent = JSON.stringify(data.status.ccuState, null, 2); + // 更新进程消息面板 + console.log('Received WebSocket message:', data); + if (data && data.processes && data.processes.list) { + console.log('Updating processes:', data.processes); + updateProcesses(data.processes); + } else { + console.warn('Invalid processes data received:', data); + } + // 更新运行日志 document.getElementById('log-data').textContent = data.status.logs.join('\n'); } @@ -179,3 +188,74 @@ document.getElementById('moos-send-btn').addEventListener('click', sendMoosNotif // 初始化WebSocket连接 initWebSocket(); + +// 更新进程列表 +function updateProcesses(processes) { + const processList = document.getElementById('process-list'); + processList.innerHTML = ''; // 清空现有内容 + + const totalCount = document.getElementById('process-count'); + totalCount.textContent = processes.count; + + processes.list.forEach(process => { + const processElement = document.createElement('div'); + processElement.className = 'process-card'; + + const header = document.createElement('div'); + header.className = 'process-header'; + header.innerHTML = ` + ${process.name} + 订阅:${process.subscribed.length} 发布:${process.published.length} + `; + + const body = document.createElement('div'); + body.className = 'process-body'; + body.innerHTML = ` +
+

订阅主题

+ +
+
+

发布主题

+ +
+ `; + + processElement.appendChild(header); + processElement.appendChild(body); + processList.appendChild(processElement); + }); +} + +// 渲染单个进程信息 +function renderProcess(process) { + const template = document.getElementById('process-template'); + const clone = document.importNode(template.content, true); + + const processEntry = clone.querySelector('.process-entry'); + processEntry.querySelector('.process-name').textContent = process.name; + + const subscribedList = processEntry.querySelector('.subscribed-list'); + process.subscribed.forEach(topic => { + const li = document.createElement('li'); + li.textContent = topic; + subscribedList.appendChild(li); + }); + + const publishedList = processEntry.querySelector('.published-list'); + process.published.forEach(topic => { + const li = document.createElement('li'); + li.textContent = topic; + publishedList.appendChild(li); + }); + + return processEntry; +} + +// 清空进程列表 +function clearProcessList() { + const processLog = document.getElementById('log-data'); + while (processLog.firstChild && processLog.firstChild.tagName !== 'TEMPLATE') { + processLog.removeChild(processLog.firstChild); + } +} diff --git a/bin/static/style.css b/bin/static/style.css index ee81fa8..45e0fc0 100644 --- a/bin/static/style.css +++ b/bin/static/style.css @@ -708,3 +708,154 @@ body { position: relative; overflow: hidden; } + +.process-log { + background-color: rgba(97, 218, 251, 0.05); + padding: 15px; + border-radius: 8px; + max-height: 600px; + overflow-y: auto; + border: 1px solid rgba(97, 218, 251, 0.2); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +/* 进程消息容器样式 */ +.process-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 15px; + padding: 10px; + margin-top: 10px; +} + +.process-card { + background: linear-gradient(145deg, #3a3f4b, #2e323a); + border-radius: 8px; + padding: 15px; + border: 1px solid rgba(97, 218, 251, 0.3); + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + transition: all 0.3s ease; +} + +.process-card:hover { + transform: translateY(-3px); + box-shadow: 0 4px 8px rgba(0,0,0,0.2); + border-color: rgba(97, 218, 251, 0.6); +} + +.process-header { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 10px; + margin-bottom: 10px; + border-bottom: 1px solid rgba(97, 218, 251, 0.2); +} + +.process-name { + color: #61dafb; + font-weight: bold; + font-size: 16px; +} + +.topic-count { + color: #4caf50; + font-size: 14px; + background: rgba(76, 175, 80, 0.1); + padding: 4px 8px; + border-radius: 4px; +} + +.topic-group h4 { + color: #ffeb3b; + margin: 10px 0 5px 0; + font-size: 14px; +} + +.subscribed-list, .published-list { + list-style: none; + padding: 0; + margin: 0; +} + +.subscribed-list li, .published-list li { + padding: 6px 10px; + margin: 3px 0; + background: rgba(97, 218, 251, 0.05); + border-radius: 4px; + border-left: 3px solid rgba(97, 218, 251, 0.3); + transition: all 0.2s ease; +} + +.subscribed-list li:hover, .published-list li:hover { + background: rgba(97, 218, 251, 0.1); + border-left-color: #61dafb; + transform: translateX(5px); +} + +#process-count { + background: rgba(97, 218, 251, 0.2); + padding: 2px 8px; + border-radius: 4px; + margin-left: 10px; + font-size: 14px; +} + +.process-entry:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + border-color: rgba(97, 218, 251, 0.5); +} + +.process-name { + font-weight: bold; + margin-bottom: 10px; + color: #61dafb; + font-size: 16px; + display: flex; + align-items: center; + gap: 8px; +} + +.process-name::before { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: #61dafb; + box-shadow: 0 0 8px #61dafb; + animation: glow 2s infinite alternate; +} + +.message-type { + font-style: italic; + color: #61dafb; + margin: 10px 0 5px 0; + font-size: 14px; + padding: 4px 8px; + background: rgba(97, 218, 251, 0.1); + border-radius: 4px; + display: inline-block; +} + +.subscribed-list, .published-list { + margin-left: 20px; + list-style-type: none; + padding-left: 0; +} + +.subscribed-list li, .published-list li { + margin: 5px 0; + padding: 6px 10px; + background: rgba(97, 218, 251, 0.05); + border-radius: 4px; + border-left: 3px solid rgba(97, 218, 251, 0.3); + transition: all 0.2s ease; +} + +.subscribed-list li:hover, .published-list li:hover { + background: rgba(97, 218, 251, 0.1); + border-left-color: #61dafb; + transform: translateX(4px); +} diff --git a/bin/templates/index.html b/bin/templates/index.html index eae5ec5..ed72bf3 100644 --- a/bin/templates/index.html +++ b/bin/templates/index.html @@ -68,8 +68,21 @@
-

运行日志

-
正在加载...
+

进程消息 0

+
+
+            
+        
diff --git a/src/appPowerMangerWebUI/CMakeLists.txt b/src/appPowerMangerWebUI/CMakeLists.txt index 2e25378..0f56c4b 100644 --- a/src/appPowerMangerWebUI/CMakeLists.txt +++ b/src/appPowerMangerWebUI/CMakeLists.txt @@ -18,6 +18,7 @@ find_package(SQLite3 REQUIRED) SET(SRC main.cpp + DBImage.cpp ) ADD_EXECUTABLE(appPowerMangerWebUI ${SRC}) diff --git a/src/appPowerMangerWebUI/DBImage.cpp b/src/appPowerMangerWebUI/DBImage.cpp new file mode 100644 index 0000000..ac66d68 --- /dev/null +++ b/src/appPowerMangerWebUI/DBImage.cpp @@ -0,0 +1,230 @@ +/////////////////////////////////////////////////////////////////////////// +// +// MOOS - Mission Oriented Operating Suite +// +// A suit of Applications and Libraries for Mobile Robotics Research +// Copyright (C) 2001-2005 Massachusetts Institute of Technology and +// Oxford University. +// +// This software was written by Paul Newman at MIT 2001-2002 and Oxford +// University 2003-2005. email: pnewman@robots.ox.ac.uk. +// +// This file is part of a MOOS Utility Component. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +////////////////////////// END_GPL ////////////////////////////////// +// DBImage.cpp: implementation of the CDBImage class. + + + +// + + +////////////////////////////////////////////////////////////////////// + +#include "DBImage.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +bool CDBImage::Set(MOOSMSG_LIST & InMail) +{ + MOOSMSG_LIST::iterator p; + + + m_Lock.Lock(); + //MOOSTrace("Rx'd %d \n",InMail.size()); + + for(p = InMail.begin();p!=InMail.end();p++) + { + CMOOSMsg & rMsg= *p; + //MOOSTrace("Rx'd %s \n",rMsg.m_sKey.c_str()); + + if(!m_Mask.empty() && m_Mask.find(rMsg.GetSource())!=m_Mask.end()) + { + continue; + } + + if(rMsg.IsDataType(MOOS_NOT_SET) && !m_bShowPending) + { + continue; + } + + int nNdx = GetIndex(rMsg.m_sKey); + if(nNdx<0) + { + m_IndexMap[rMsg.m_sKey] = m_DBData.size(); + m_DBData.push_back(CVar(rMsg)); + } + else + { + bool bChanging = !( rMsg.GetTime()==m_DBData[nNdx].GetTimeVal()); + m_DBData[nNdx]= CVar(rMsg); + m_DBData[nNdx].Changed(bChanging); + } + } + m_Lock.UnLock(); + + return true; + +} + + + + + + + + +int CDBImage::GetIndex(const std::string & sName) +{ + std::map::iterator q = m_IndexMap.find(sName); + + if(q!=m_IndexMap.end()) + { + int n = q->second; + return n; + } + else + { + return -1; + } + +} + +bool CDBImage:: HasChanged(int n) +{ + if(n >= static_cast (m_DBData.size()) || n<0) + return false; + return m_DBData[n].IsChanged(); +} + + +bool CDBImage::Get(CVar & rVar, int n) +{ + m_Lock.Lock(); + + if(n>=static_cast (m_DBData.size()) || n<0) + { + m_Lock.UnLock(); + return false; + } + + rVar = m_DBData[n]; + + m_Lock.UnLock(); + + return true; +} + + +bool CDBImage::SetProcInfo(MOOSMSG_LIST & InMail) +{ + m_Lock.Lock(); + // TODO: Add your control notification handler code here + m_nClients = InMail.size(); + m_Processes.clear(); + MOOSMSG_LIST::iterator p; + for(p=InMail.begin();p!=InMail.end();p++) + { + + CMOOSMsg & rMsg = *p; + std::string sTmp = rMsg.m_sVal; + std::string sWho = MOOSChomp(sTmp,":"); + + if(m_Processes.find(sWho)==m_Processes.end()) + { + ProcInfo Info; + m_Processes[sWho] = Info; + } + + std::string sSubscribed,sPublished; + sSubscribed = MOOSChomp(sTmp,"PUBLISHED="); + sPublished = sTmp; + MOOSChomp(sSubscribed,"SUBSCRIBED="); + + while(!sSubscribed.empty()) + { + sTmp = MOOSChomp(sSubscribed,","); + if(!sTmp.empty()) + { + m_Processes[sWho].m_Subscribed.push_front(sTmp); + } + } + + while(!sPublished.empty()) + { + sTmp = MOOSChomp(sPublished,","); + if(!sTmp.empty()) + { + m_Processes[sWho].m_Published.push_front(sTmp); + } + } + } + m_Lock.UnLock(); + return true; +} + +bool CDBImage::GetProcInfo(const std::string & sWhat,STRING_LIST & sSubs,STRING_LIST & sPubs) +{ + bool bRes = false; + m_Lock.Lock(); + PROC_INFO_MAP::iterator q = m_Processes.find(sWhat); + if(q!=m_Processes.end()) + { + sSubs = q->second.m_Subscribed; + sPubs = q->second.m_Published; + bRes = true; + } + m_Lock.UnLock(); + return bRes; + +} + +bool CDBImage::GetProcesses(STRING_LIST & sProcs) +{ + m_Lock.Lock(); + + sProcs.clear(); + + PROC_INFO_MAP::iterator q; + for(q=m_Processes.begin();q!=m_Processes.end();q++) + { + sProcs.push_back(q->first.c_str()); + } + m_Lock.UnLock(); + + return true; +} + + +bool CDBImage::SetMask(std::set Mask) +{ + m_Mask = Mask; + return true; +} + +bool CDBImage::Clear() +{ + m_Lock.Lock(); + m_DBData.clear(); + m_IndexMap.clear(); + m_Lock.UnLock(); + + return true; +} diff --git a/src/appPowerMangerWebUI/DBImage.h b/src/appPowerMangerWebUI/DBImage.h new file mode 100644 index 0000000..10ab42e --- /dev/null +++ b/src/appPowerMangerWebUI/DBImage.h @@ -0,0 +1,170 @@ +/////////////////////////////////////////////////////////////////////////// +// +// MOOS - Mission Oriented Operating Suite +// +// A suit of Applications and Libraries for Mobile Robotics Research +// Copyright (C) 2001-2005 Massachusetts Institute of Technology and +// Oxford University. +// +// This software was written by Paul Newman at MIT 2001-2002 and Oxford +// University 2003-2005. email: pnewman@robots.ox.ac.uk. +// +// This file is part of a MOOS Utility Component. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +// 02111-1307, USA. +// +////////////////////////// END_GPL ////////////////////////////////// +// DBImage.h: interface for the CDBImage class. + +// + +////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 + #pragma warning(disable : 4786) +#endif + + +#if !defined(AFX_DBIMAGE_H__E4F9EC4D_7049_4EC8_86BA_0AF21BBCA23C__INCLUDED_) + +#define AFX_DBIMAGE_H__E4F9EC4D_7049_4EC8_86BA_0AF21BBCA23C__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "MOOS/libMOOS/MOOSLib.h" +#include +#include +#include + + +class CDBImage +{ +public: + bool Clear(); + + CDBImage() + { + m_bShowPending = false; + } + + struct ProcInfo + { + std::string m_sName; + STRING_LIST m_Published; + STRING_LIST m_Subscribed; + }; + + typedef std::map PROC_INFO_MAP; + + class CVar + + { + public: + CVar() + { + m_dfTime = -1; + } + + CVar(CMOOSMsg & rMsg) + { + m_dfTime = rMsg.m_dfTime; + switch(rMsg.m_cDataType) + { + case MOOS_STRING: + m_eType = STR; + break; + case MOOS_DOUBLE: + m_eType = DBL; + break; + case MOOS_BINARY_STRING: + m_eType = BIN; + break; + default: + m_eType = UNKNOWN; + } + m_sStr = rMsg.GetAsString(); + m_sCommunity = rMsg.m_sOriginatingCommunity; + m_sName = rMsg.m_sKey; + m_sFreq = MOOSFormat("%.1f",rMsg.m_dfVal2); + m_sSource = rMsg.m_sSrc; + m_bChanged = false; + } + + std::string GetTime(){return MOOSFormat("%.3f",m_dfTime);}; + double GetTimeVal(){return m_dfTime;}; + std::string GetValue(){return m_eType==UNKNOWN?"":m_sStr;}; + std::string GetCommunity(){return m_sCommunity;}; + std::string GetName(){return m_sName;}; + std::string GetFrequency(){return m_sFreq;}; + std::string GetSource(){return m_sSource;}; + bool IsChanged(){return m_bChanged;}; + void Changed(bool b){m_bChanged = b;}; + std::string GetType() + { + switch(m_eType) + { + case STR: + return "$"; + case DBL: + return "D"; + case BIN: + return "B"; + default: + return "?"; + } + } + + + protected: + double m_dfTime; + std::string m_sStr; + enum Type{ STR,DBL,BIN,UNKNOWN}; + Type m_eType; + std::string m_sCommunity; + std::string m_sSrc; + std::string m_sName; + std::string m_sFreq; + std::string m_sSource; + bool m_bChanged; + }; + + bool Set(MOOSMSG_LIST & InMail); + bool Get(CVar & rVar, int n); + bool HasChanged(int n); + bool SetProcInfo(MOOSMSG_LIST & InMail) ; + bool GetProcesses(STRING_LIST & sProcs); + int GetNumVariables(){return m_DBData.size();} + bool GetProcInfo(const std::string & sWhat,STRING_LIST & sSubs,STRING_LIST & sPubs); + bool SetMask(std::set Mask); + bool ShowPending(bool b){m_bShowPending = b;return true;}; +protected: + int GetIndex(const std::string & sName); + std::vector m_DBData; + std::map m_IndexMap; + std::set m_Mask; + + bool m_bShowPending ; + int m_nClients; + int m_nVariables; + PROC_INFO_MAP m_Processes; + CMOOSLock m_Lock; +}; + + +#endif // !defined(AFX_DBIMAGE_H__E4F9EC4D_7049_4EC8_86BA_0AF21BBCA23C__INCLUDED_) + diff --git a/src/appPowerMangerWebUI/main.cpp b/src/appPowerMangerWebUI/main.cpp index 4d708d6..0a21b89 100644 --- a/src/appPowerMangerWebUI/main.cpp +++ b/src/appPowerMangerWebUI/main.cpp @@ -13,6 +13,7 @@ #include "../pPowerManger/SQLiteDB.h" #include "../pPowerManger/pmSysvariable.h" #include +#include "DBImage.h" using namespace std; @@ -23,6 +24,9 @@ mutex wsMutex; vector wsConnections; shared_ptr db; shared_ptr moosClient; +shared_ptr dbImage; + +bool bContinue = true; // WebSocket消息处理 void sendStateUpdate() { @@ -33,6 +37,7 @@ void sendStateUpdate() { db->getStateData(stateData, time); } + // 基本状态信息 msg["state"] = { {"time", time}, {"state_id", stateData.state_id}, @@ -43,20 +48,65 @@ void sendStateUpdate() { {"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数据库状态信息 + // MOOS数据库状态信息 msg["moos_db_status"] = { {"connected", moosClient->IsConnected()}, {"host", "localhost"}, {"port", 9000} }; - + + // 进程详细信息 + { + lock_guard 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::vector subs_vec(subs.begin(), subs.end()); + std::vector 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 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 lock(wsMutex); for(auto conn : wsConnections) { @@ -65,10 +115,62 @@ void sendStateUpdate() { } } +// 订阅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("/home/zjk/project/H100/H100PowerManger/data/power_data.db"); moosClient = make_shared(); + dbImage = make_shared(); moosClient->Run("localhost", 9000, "appPowerMangerWebUI"); // WebSocket路由 @@ -97,6 +199,13 @@ int main() { } }).detach(); + //MOOS数据库更新定时器 + std::thread fetchThread([&](){ + while(bContinue) { + FetchLoop(moosClient.get(), dbImage.get()); + } + }); + // 绑定主页路由 CROW_ROUTE(app, "/")([]() { auto page = crow::mustache::load_text("index.html");