增加了moos变量显示的webui功能

This commit is contained in:
zjk
2025-03-04 11:17:16 +08:00
parent 16cffa296a
commit 341630f547
7 changed files with 761 additions and 7 deletions
+82 -2
View File
@@ -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 = `
<span class="process-name">${process.name}</span>
<span class="topic-count">订阅:${process.subscribed.length} 发布:${process.published.length}</span>
`;
const body = document.createElement('div');
body.className = 'process-body';
body.innerHTML = `
<div class="topic-group">
<h4>订阅主题</h4>
<ul class="subscribed-list">${process.subscribed.map(t => `<li>${t}</li>`).join('')}</ul>
</div>
<div class="topic-group">
<h4>发布主题</h4>
<ul class="published-list">${process.published.map(t => `<li>${t}</li>`).join('')}</ul>
</div>
`;
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);
}
}
+151
View File
@@ -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);
}
+15 -2
View File
@@ -68,8 +68,21 @@
</div>
<div class="panel log-panel">
<h2>运行日志</h2>
<pre id="log-data">正在加载...</pre>
<h2>进程消息 <span class="badge" id="process-count">0</span></h2>
<div id="process-list" class="process-container"></div>
<pre id="log-data" class="process-log">
<template id="process-template">
<div class="process-entry">
<div class="process-name"></div>
<div class="message-list">
<div class="message-type">订阅的消息</div>
<ul class="subscribed-list"></ul>
<div class="message-type">发布的消息</div>
<ul class="published-list"></ul>
</div>
</div>
</template>
</pre>
</div>
<div class="panel state-machine-panel">
+1
View File
@@ -18,6 +18,7 @@ find_package(SQLite3 REQUIRED)
SET(SRC
main.cpp
DBImage.cpp
)
ADD_EXECUTABLE(appPowerMangerWebUI ${SRC})
+230
View File
@@ -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<std::string,int>::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<int> (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<int> (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<std::string> Mask)
{
m_Mask = Mask;
return true;
}
bool CDBImage::Clear()
{
m_Lock.Lock();
m_DBData.clear();
m_IndexMap.clear();
m_Lock.UnLock();
return true;
}
+170
View File
@@ -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 <string>
#include <map>
#include <vector>
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<std::string,ProcInfo> 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<std::string> Mask);
bool ShowPending(bool b){m_bShowPending = b;return true;};
protected:
int GetIndex(const std::string & sName);
std::vector<CVar> m_DBData;
std::map<std::string,int> m_IndexMap;
std::set<std::string> 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_)
+112 -3
View File
@@ -13,6 +13,7 @@
#include "../pPowerManger/SQLiteDB.h"
#include "../pPowerManger/pmSysvariable.h"
#include <MOOS/libMOOS/Comms/MOOSAsyncCommClient.h>
#include "DBImage.h"
using namespace std;
@@ -23,6 +24,9 @@ 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() {
@@ -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<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) {
@@ -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<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路由
@@ -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");