基本实现系统状态监控,待完善
This commit is contained in:
parent
61947bb15f
commit
df3b7f7853
74
XNMonitorServer/.vscode/settings.json
vendored
Normal file
74
XNMonitorServer/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"qsqlrecord": "cpp",
|
||||||
|
"*.tcc": "cpp",
|
||||||
|
"qsharedpointer": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"variant": "cpp",
|
||||||
|
"cctype": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"codecvt": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"complex": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"unordered_set": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"regex": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"future": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"ranges": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"stop_token": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeindex": "cpp",
|
||||||
|
"typeinfo": "cpp"
|
||||||
|
}
|
||||||
|
}
|
63
XNMonitorServer/CMakeLists.txt
Normal file
63
XNMonitorServer/CMakeLists.txt
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(XNMonitorServer LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
if(NOT fastcdr_FOUND)
|
||||||
|
find_package(fastcdr 2 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT fastdds_FOUND)
|
||||||
|
find_package(fastdds 3 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 获取环境变量
|
||||||
|
if(DEFINED ENV{XNCore})
|
||||||
|
set(XNCore_PATH $ENV{XNCore})
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Environment variable XNCore is not set.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GLOB DDS_XNIDL_SOURCES_CXX "../XNCore/XNIDL/*.cxx")
|
||||||
|
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
find_package(nlohmann_json 3.9.1 REQUIRED)
|
||||||
|
|
||||||
|
add_library(XNMonitorServer SHARED
|
||||||
|
XNMonitorServer_global.h
|
||||||
|
TopicManager.h
|
||||||
|
TopicManager.cpp
|
||||||
|
DataReaderListenerImpl.h
|
||||||
|
TypeDefine.h
|
||||||
|
XNMonitorInterface.h
|
||||||
|
XNMonitorInterface.cpp
|
||||||
|
SystemInfoMonitor.h
|
||||||
|
SystemInfoMonitor.cpp
|
||||||
|
${DDS_XNIDL_SOURCES_CXX}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加头文件搜索路径
|
||||||
|
target_include_directories(XNMonitorServer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
target_link_libraries(XNMonitorServer PRIVATE
|
||||||
|
nlohmann_json::nlohmann_json
|
||||||
|
fastcdr
|
||||||
|
fastdds
|
||||||
|
OpenSSL::SSL
|
||||||
|
OpenSSL::Crypto
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(XNMonitorServer PRIVATE LOGIN_LIBRARY)
|
||||||
|
|
||||||
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||||
|
set(CMAKE_INSTALL_PREFIX "${XNCore_PATH}" CACHE PATH "Install path prefix" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
install(TARGETS XNMonitorServer
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION .
|
||||||
|
)
|
51
XNMonitorServer/DataReaderListenerImpl.h
Executable file
51
XNMonitorServer/DataReaderListenerImpl.h
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* @file DataReaderListenerImpl.h
|
||||||
|
* @author jinchao
|
||||||
|
* @brief 数据读取器监听器模板类
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-03-10
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2025 COMAC
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TypeDefine.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 数据读取器监听器模板类
|
||||||
|
* @tparam T 数据类型
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class DataReaderListenerImpl : public XNDataReaderListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
* @param callback 回调函数
|
||||||
|
*/
|
||||||
|
DataReaderListenerImpl(std::function<void(const T &)> callback) : callback_(callback) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 数据可用回调函数
|
||||||
|
* @param reader 数据读取器
|
||||||
|
*/
|
||||||
|
void on_data_available(XNDataReader *reader) override
|
||||||
|
{
|
||||||
|
XNSampleInfo info;
|
||||||
|
if (reader->take_next_sample(&data_, &info) == eprosima::fastdds::dds::RETCODE_OK
|
||||||
|
&& info.valid_data) {
|
||||||
|
callback_(data_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 数据
|
||||||
|
*/
|
||||||
|
T data_;
|
||||||
|
/**
|
||||||
|
* @brief 回调函数
|
||||||
|
*/
|
||||||
|
std::function<void(const T &)> callback_;
|
||||||
|
};
|
134
XNMonitorServer/SystemInfoMonitor.cpp
Normal file
134
XNMonitorServer/SystemInfoMonitor.cpp
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#include "SystemInfoMonitor.h"
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
SystemInfoMonitor::~SystemInfoMonitor()
|
||||||
|
{
|
||||||
|
//注销引擎状态订阅
|
||||||
|
TopicManager::Instance()->unregisterSubscriber("XNSim::XNSimStatus::XNEngineStatus");
|
||||||
|
//注销线程状态订阅
|
||||||
|
TopicManager::Instance()->unregisterSubscriber("XNSim::XNSimStatus::XNThreadStatus");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SystemInfoMonitor::Initialize()
|
||||||
|
{
|
||||||
|
//注册引擎状态订阅
|
||||||
|
XNDDSErrorCode ret =
|
||||||
|
TopicManager::Instance()->registerSubscriber<XNSim::XNSimStatus::XNEngineStatusPubSubType>(
|
||||||
|
"XNSim::XNSimStatus::XNEngineStatus",
|
||||||
|
std::bind(&SystemInfoMonitor::EngineStatusListener, this, std::placeholders::_1));
|
||||||
|
if (ret != XNDDSErrorCode::SUCCESS) {
|
||||||
|
return "Failed to register engine status subscriber, error code: "
|
||||||
|
+ std::to_string(static_cast<int>(ret));
|
||||||
|
}
|
||||||
|
//注册线程状态订阅
|
||||||
|
ret =
|
||||||
|
TopicManager::Instance()->registerSubscriber<XNSim::XNSimStatus::XNThreadStatusPubSubType>(
|
||||||
|
"XNSim::XNSimStatus::XNThreadStatus",
|
||||||
|
std::bind(&SystemInfoMonitor::ThreadStatusListener, this, std::placeholders::_1));
|
||||||
|
if (ret != XNDDSErrorCode::SUCCESS) {
|
||||||
|
return "Failed to register thread status subscriber, error code: "
|
||||||
|
+ std::to_string(static_cast<int>(ret));
|
||||||
|
}
|
||||||
|
m_ThreadCycleCount.clear();
|
||||||
|
m_EngineStatusUpdate = false;
|
||||||
|
return "Success";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SystemInfoMonitor::GetSystemInfo()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_EngineStatusMutex);
|
||||||
|
json jsonObj;
|
||||||
|
|
||||||
|
// 创建引擎状态JSON对象
|
||||||
|
json engineStatusObj;
|
||||||
|
engineStatusObj["engineName"] = m_EngineStatus.XNEngineName();
|
||||||
|
engineStatusObj["engineID"] = m_EngineStatus.XNEngineID();
|
||||||
|
engineStatusObj["engineStatus"] = m_EngineStatus.XNEngineSt();
|
||||||
|
std::string affinity;
|
||||||
|
for (int i = 0; i < sizeof(m_EngineStatus.XNEngineAff()); i++) {
|
||||||
|
if ((m_EngineStatus.XNEngineAff() >> i) & 1 == 1) {
|
||||||
|
affinity += std::to_string(i) + ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!affinity.empty()) {
|
||||||
|
affinity.pop_back(); // 移除最后一个逗号
|
||||||
|
}
|
||||||
|
engineStatusObj["engineAffinity"] = affinity;
|
||||||
|
engineStatusObj["threadCount"] = m_EngineStatus.XNThCnt();
|
||||||
|
|
||||||
|
// 创建核心状态JSON对象
|
||||||
|
json coreStatusObj;
|
||||||
|
const auto &coreStatus = m_EngineStatus.XNCoreSt();
|
||||||
|
coreStatusObj["fwStatus"] = coreStatus.XNFWStatus();
|
||||||
|
coreStatusObj["tmStatus"] = coreStatus.XNTMStatus();
|
||||||
|
coreStatusObj["emStatus"] = coreStatus.XNEMStatus();
|
||||||
|
coreStatusObj["sdStatus"] = coreStatus.XNSDStatus();
|
||||||
|
coreStatusObj["thmStatus"] = coreStatus.XNThMStatus();
|
||||||
|
coreStatusObj["mmStatus"] = coreStatus.XNMMStatus();
|
||||||
|
coreStatusObj["smStatus"] = coreStatus.XNSMStatus();
|
||||||
|
coreStatusObj["dmStatus"] = coreStatus.XNDMStatus();
|
||||||
|
|
||||||
|
// 将核心状态添加到引擎状态中
|
||||||
|
engineStatusObj["coreStatus"] = coreStatusObj;
|
||||||
|
|
||||||
|
// 将引擎状态添加到主JSON对象中
|
||||||
|
jsonObj["engineStatus"] = engineStatusObj;
|
||||||
|
|
||||||
|
m_EngineStatusUpdate = false;
|
||||||
|
return jsonObj.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SystemInfoMonitor::GetAllThreadInfo()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_ThreadStatusMutex);
|
||||||
|
json jsonObj;
|
||||||
|
|
||||||
|
// 创建线程状态JSON对象
|
||||||
|
json threadStatusObj;
|
||||||
|
for (const auto &status : m_ThreadStatus) {
|
||||||
|
json threadObj;
|
||||||
|
threadObj["threadName"] = status.second.XNThreadName();
|
||||||
|
std::string affinity;
|
||||||
|
for (int i = 0; i < sizeof(status.second.XNThreadAff()); i++) {
|
||||||
|
if ((status.second.XNThreadAff() >> i) & 1 == 1) {
|
||||||
|
affinity += std::to_string(i) + ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!affinity.empty()) {
|
||||||
|
affinity.pop_back(); // 移除最后一个逗号
|
||||||
|
}
|
||||||
|
threadObj["threadAffinity"] = affinity;
|
||||||
|
threadObj["threadPro"] = status.second.XNThreadPro();
|
||||||
|
uint64_t cycleCount = status.second.XNThRunCnt();
|
||||||
|
if (m_ThreadCycleCount.find(status.first) != m_ThreadCycleCount.end()) {
|
||||||
|
if (cycleCount <= m_ThreadCycleCount[status.first]) {
|
||||||
|
threadObj["threadStatus"] = 99;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
threadObj["threadRunCount"] = cycleCount;
|
||||||
|
m_ThreadCycleCount[status.first] = cycleCount;
|
||||||
|
threadObj["threadStatus"] = status.second.XNThreadSt();
|
||||||
|
threadObj["threadCurrentFrequency"] = status.second.XNThCurFreq();
|
||||||
|
threadObj["threadSetFrequency"] = status.second.XNThSetFreq();
|
||||||
|
threadStatusObj[std::to_string(status.first)] = threadObj;
|
||||||
|
}
|
||||||
|
jsonObj["threadStatus"] = threadStatusObj;
|
||||||
|
|
||||||
|
return jsonObj.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemInfoMonitor::EngineStatusListener(const XNSim::XNSimStatus::XNEngineStatus &status)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_EngineStatusMutex);
|
||||||
|
m_EngineStatusUpdate = true;
|
||||||
|
m_EngineStatus = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SystemInfoMonitor::ThreadStatusListener(const XNSim::XNSimStatus::XNThreadStatus &status)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_ThreadStatusMutex);
|
||||||
|
m_ThreadStatus[status.XNThreadID()] = status;
|
||||||
|
}
|
59
XNMonitorServer/SystemInfoMonitor.h
Normal file
59
XNMonitorServer/SystemInfoMonitor.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef SYSTEMINFOMONITOR_H
|
||||||
|
#define SYSTEMINFOMONITOR_H
|
||||||
|
|
||||||
|
#include "XNMonitorServer_global.h"
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include "TypeDefine.h"
|
||||||
|
#include "TopicManager.h"
|
||||||
|
#include "../XNCore/XNIDL/XNSimStatusPubSubTypes.hpp"
|
||||||
|
|
||||||
|
class XNMONITORSERVER_EXPORT SystemInfoMonitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SystemInfoMonitor() {}
|
||||||
|
virtual ~SystemInfoMonitor();
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string Initialize();
|
||||||
|
|
||||||
|
std::string GetSystemInfo();
|
||||||
|
std::string GetAllThreadInfo();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 引擎状态监听器
|
||||||
|
* @param status 引擎状态
|
||||||
|
*/
|
||||||
|
void EngineStatusListener(const XNSim::XNSimStatus::XNEngineStatus &status);
|
||||||
|
/**
|
||||||
|
* @brief 线程状态监听器
|
||||||
|
* @param status 线程状态
|
||||||
|
*/
|
||||||
|
void ThreadStatusListener(const XNSim::XNSimStatus::XNThreadStatus &status);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 互斥锁
|
||||||
|
*/
|
||||||
|
std::mutex m_EngineStatusMutex;
|
||||||
|
std::mutex m_ThreadStatusMutex;
|
||||||
|
/**
|
||||||
|
* @brief 引擎状态
|
||||||
|
*/
|
||||||
|
XNSim::XNSimStatus::XNEngineStatus m_EngineStatus;
|
||||||
|
/**
|
||||||
|
* @brief 引擎状态更新
|
||||||
|
*/
|
||||||
|
bool m_EngineStatusUpdate = false;
|
||||||
|
/**
|
||||||
|
* @brief 线程状态
|
||||||
|
*/
|
||||||
|
std::map<uint32_t, XNSim::XNSimStatus::XNThreadStatus> m_ThreadStatus;
|
||||||
|
/**
|
||||||
|
* @brief 线程周期计数
|
||||||
|
*/
|
||||||
|
std::map<uint32_t, uint64_t> m_ThreadCycleCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SYSTEMINFOMONITOR_H
|
16
XNMonitorServer/TopicManager.cpp
Executable file
16
XNMonitorServer/TopicManager.cpp
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @file TopicManager.cpp
|
||||||
|
* @author jinchao
|
||||||
|
* @brief 主题管理类
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-03-10
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2025 COMAC
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "TopicManager.h"
|
||||||
|
|
||||||
|
// 静态成员变量定义
|
||||||
|
TopicManager *TopicManager::instance = nullptr;
|
||||||
|
// 互斥锁
|
||||||
|
std::mutex TopicManager::instanceMutex;
|
299
XNMonitorServer/TopicManager.h
Executable file
299
XNMonitorServer/TopicManager.h
Executable file
@ -0,0 +1,299 @@
|
|||||||
|
/**
|
||||||
|
* @file TopicManager.h
|
||||||
|
* @author jinchao
|
||||||
|
* @brief 主题管理类
|
||||||
|
* @version 1.0
|
||||||
|
* @date 2025-03-10
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2025 COMAC
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include "DataReaderListenerImpl.h"
|
||||||
|
#include <fastdds/dds/core/status/StatusMask.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 主题管理类
|
||||||
|
*/
|
||||||
|
class TopicManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 删除拷贝构造
|
||||||
|
*/
|
||||||
|
TopicManager(const TopicManager &) = delete;
|
||||||
|
/**
|
||||||
|
* @brief 删除赋值操作
|
||||||
|
*/
|
||||||
|
TopicManager &operator=(const TopicManager &) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取单例
|
||||||
|
* @return TopicManager*: 主题管理类实例
|
||||||
|
*/
|
||||||
|
static TopicManager *Instance()
|
||||||
|
{
|
||||||
|
if (instance == nullptr) {
|
||||||
|
std::lock_guard<std::mutex> locker(instanceMutex);
|
||||||
|
if (instance == nullptr) { // 双重检查锁定
|
||||||
|
instance = new TopicManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清理参与者
|
||||||
|
*/
|
||||||
|
static void cleanupParticipant()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(instanceMutex);
|
||||||
|
if (instance != nullptr) {
|
||||||
|
instance->clearAllTopic(); // 清理所有主题
|
||||||
|
if (instance->m_Participant != nullptr) {
|
||||||
|
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()
|
||||||
|
->delete_participant(instance->m_Participant); // 删除参与者
|
||||||
|
instance->m_Participant = nullptr; // 设置参与者为空
|
||||||
|
}
|
||||||
|
delete instance; // 删除单例
|
||||||
|
instance = nullptr; // 设置单例为空
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 显式构造函数
|
||||||
|
*/
|
||||||
|
explicit TopicManager() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 析构函数
|
||||||
|
*/
|
||||||
|
virtual ~TopicManager() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 初始化参与者
|
||||||
|
* @param domainId: 域ID
|
||||||
|
*/
|
||||||
|
XNDDSErrorCode initializeParticipant(int domainId)
|
||||||
|
{
|
||||||
|
XNParticipantQos participantQos = eprosima::fastdds::dds::PARTICIPANT_QOS_DEFAULT;
|
||||||
|
participantQos.name("XNMonitor"); // 设置参与者名称
|
||||||
|
m_Participant =
|
||||||
|
eprosima::fastdds::dds::DomainParticipantFactory::get_instance()->create_participant(
|
||||||
|
domainId, participantQos); // 创建参与者
|
||||||
|
if (m_Participant == nullptr) {
|
||||||
|
return XNDDSErrorCode::INIT_FAILED;
|
||||||
|
}
|
||||||
|
return XNDDSErrorCode::SUCCESS;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注册发布者模板函数
|
||||||
|
* @tparam T: 类型
|
||||||
|
* @param topicName: 主题名称
|
||||||
|
* @return XNDataWriter*: 数据写入器
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
XNDDSErrorCode registerPublisher(const std::string &topicName,
|
||||||
|
XNDataWriter *dataWriter = nullptr)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||||
|
if (topics_.find(topicName) == topics_.end()) {
|
||||||
|
topics_[topicName] = TopicInfo(); // 创建主题信息
|
||||||
|
TopicInfo &tmp = topics_[topicName]; // 获取主题信息
|
||||||
|
XNTypeSupport typeSupport(new T()); // 创建类型支持
|
||||||
|
typeSupport.register_type(m_Participant); // 注册类型
|
||||||
|
tmp.topic =
|
||||||
|
m_Participant->create_topic(topicName.c_str(), typeSupport.get_type_name(),
|
||||||
|
eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); // 创建主题
|
||||||
|
if (tmp.topic == nullptr) {
|
||||||
|
topics_.erase(topicName); // 移除主题
|
||||||
|
return XNDDSErrorCode::TOPIC_CREATE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TopicInfo &topicInfo = topics_[topicName]; // 获取主题信息
|
||||||
|
if (topicInfo.publisher == nullptr) {
|
||||||
|
topicInfo.publisher = m_Participant->create_publisher(
|
||||||
|
eprosima::fastdds::dds::PUBLISHER_QOS_DEFAULT); // 创建发布者
|
||||||
|
if (topicInfo.publisher == nullptr) {
|
||||||
|
return XNDDSErrorCode::PUBLISHER_CREATE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topicInfo.dataWriter == nullptr) {
|
||||||
|
XNDataWriterQos dataWriterQos;
|
||||||
|
// 设置数据写入器的持久性策略, 使用瞬态本地持久性
|
||||||
|
dataWriterQos.durability().kind = eprosima::fastdds::dds::VOLATILE_DURABILITY_QOS;
|
||||||
|
// 设置数据写入器的生命周期策略, 设置为5秒
|
||||||
|
dataWriterQos.lifespan().duration = eprosima::fastdds::dds::Duration_t(5, 0);
|
||||||
|
topicInfo.dataWriter = topicInfo.publisher->create_datawriter(
|
||||||
|
topicInfo.topic, dataWriterQos); // 创建数据写入器
|
||||||
|
if (topicInfo.dataWriter == nullptr) {
|
||||||
|
return XNDDSErrorCode::DATAWRITER_CREATE_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataWriter = topicInfo.dataWriter;
|
||||||
|
return XNDDSErrorCode::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注销发布者
|
||||||
|
* @param topicName: 主题名称
|
||||||
|
*/
|
||||||
|
void unregisterPublisher(const std::string &topicName)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||||
|
auto it = topics_.find(topicName);
|
||||||
|
if (it != topics_.end()) {
|
||||||
|
TopicInfo &topicInfo = it->second; // 获取主题信息
|
||||||
|
if (topicInfo.dataWriter != nullptr) {
|
||||||
|
topicInfo.publisher->delete_datawriter(topicInfo.dataWriter); // 删除数据写入器
|
||||||
|
topicInfo.dataWriter = nullptr; // 设置数据写入器为空
|
||||||
|
}
|
||||||
|
if (topicInfo.publisher != nullptr) {
|
||||||
|
m_Participant->delete_publisher(topicInfo.publisher); // 删除发布者
|
||||||
|
topicInfo.publisher = nullptr; // 设置发布者为空
|
||||||
|
}
|
||||||
|
if (topicInfo.publisher == nullptr && topicInfo.subscriber == nullptr
|
||||||
|
&& topicInfo.topic != nullptr) {
|
||||||
|
m_Participant->delete_topic(topicInfo.topic); // 删除主题
|
||||||
|
topicInfo.topic = nullptr; // 设置主题为空
|
||||||
|
topics_.erase(it); // 移除主题
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注册订阅者模板函数
|
||||||
|
* @tparam T: 类型
|
||||||
|
* @param topicName: 主题名称
|
||||||
|
* @param fun: 回调函数
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
XNDDSErrorCode registerSubscriber(const std::string &topicName,
|
||||||
|
std::function<void(const typename T::type &)> fun)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||||
|
if (topics_.find(topicName) == topics_.end()) {
|
||||||
|
topics_[topicName] = TopicInfo(); // 创建主题信息
|
||||||
|
TopicInfo &tmp = topics_[topicName]; // 获取主题信息
|
||||||
|
XNTypeSupport typeSupport(new T()); // 创建类型支持
|
||||||
|
typeSupport.register_type(m_Participant); // 注册类型
|
||||||
|
tmp.topic =
|
||||||
|
m_Participant->create_topic(topicName.c_str(), typeSupport.get_type_name(),
|
||||||
|
eprosima::fastdds::dds::TOPIC_QOS_DEFAULT); // 创建主题
|
||||||
|
if (tmp.topic == nullptr) {
|
||||||
|
topics_.erase(topicName); // 移除主题
|
||||||
|
return XNDDSErrorCode::TOPIC_CREATE_FAILED; // 返回
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TopicInfo &topicInfo = topics_[topicName]; // 获取主题信息
|
||||||
|
if (topicInfo.subscriber == nullptr) {
|
||||||
|
topicInfo.subscriber = m_Participant->create_subscriber(
|
||||||
|
eprosima::fastdds::dds::SUBSCRIBER_QOS_DEFAULT); // 创建订阅者
|
||||||
|
if (topicInfo.subscriber == nullptr) {
|
||||||
|
return XNDDSErrorCode::SUBSCRIBER_CREATE_FAILED; // 返回
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (topicInfo.dataReader == nullptr) {
|
||||||
|
XNDataReaderQos dataReaderQos;
|
||||||
|
dataReaderQos.durability().kind =
|
||||||
|
eprosima::fastdds::dds::VOLATILE_DURABILITY_QOS; // 设置数据读取器的持久性策略
|
||||||
|
topicInfo.listener =
|
||||||
|
new DataReaderListenerImpl<typename T::type>(fun); // 创建数据读取器监听器
|
||||||
|
topicInfo.dataReader = topicInfo.subscriber->create_datareader(
|
||||||
|
topicInfo.topic, dataReaderQos, topicInfo.listener); // 创建数据读取器
|
||||||
|
if (topicInfo.dataReader == nullptr) {
|
||||||
|
return XNDDSErrorCode::DATAREADER_CREATE_FAILED; // 返回
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto oldListener = topicInfo.dataReader->get_listener(); // 获取旧的监听器
|
||||||
|
topicInfo.listener =
|
||||||
|
new DataReaderListenerImpl<typename T::type>(fun); // 创建新的监听器
|
||||||
|
topicInfo.dataReader->set_listener(
|
||||||
|
topicInfo.listener,
|
||||||
|
eprosima::fastdds::dds::StatusMask::all()); // 设置新的监听器
|
||||||
|
delete oldListener; // 删除旧的监听器
|
||||||
|
}
|
||||||
|
return XNDDSErrorCode::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 注销订阅者
|
||||||
|
* @param topicName: 主题名称
|
||||||
|
*/
|
||||||
|
void unregisterSubscriber(const std::string &topicName)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||||
|
auto it = topics_.find(topicName);
|
||||||
|
if (it != topics_.end()) {
|
||||||
|
TopicInfo &topicInfo = it->second; // 获取主题信息
|
||||||
|
if (topicInfo.dataReader != nullptr) {
|
||||||
|
topicInfo.subscriber->delete_datareader(topicInfo.dataReader); // 删除数据读取器
|
||||||
|
topicInfo.dataReader = nullptr; // 设置数据读取器为空
|
||||||
|
}
|
||||||
|
if (topicInfo.listener != nullptr) {
|
||||||
|
delete topicInfo.listener; // 删除监听器
|
||||||
|
topicInfo.listener = nullptr; // 设置监听器为空
|
||||||
|
}
|
||||||
|
if (topicInfo.subscriber != nullptr) {
|
||||||
|
m_Participant->delete_subscriber(topicInfo.subscriber); // 删除订阅者
|
||||||
|
topicInfo.subscriber = nullptr; // 设置订阅者为空
|
||||||
|
}
|
||||||
|
if (topicInfo.subscriber == nullptr && topicInfo.publisher == nullptr
|
||||||
|
&& topicInfo.topic != nullptr) {
|
||||||
|
m_Participant->delete_topic(topicInfo.topic); // 删除主题
|
||||||
|
topicInfo.topic = nullptr; // 设置主题为空
|
||||||
|
topics_.erase(it); // 移除主题
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 清除所有主题
|
||||||
|
*/
|
||||||
|
void clearAllTopic()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||||
|
if (m_Participant != nullptr) {
|
||||||
|
while (!topics_.empty()) {
|
||||||
|
unregisterPublisher(topics_.begin()->first); // 注销发布者
|
||||||
|
unregisterSubscriber(topics_.begin()->first); // 注销订阅者
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 单例指针
|
||||||
|
*/
|
||||||
|
static TopicManager *instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 单例互斥锁
|
||||||
|
*/
|
||||||
|
static std::mutex instanceMutex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 参与者
|
||||||
|
*/
|
||||||
|
XNParticipant *m_Participant = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 主题映射
|
||||||
|
*/
|
||||||
|
std::map<std::string, TopicInfo> topics_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 主题访问互斥锁
|
||||||
|
*/
|
||||||
|
std::mutex m_Mutex;
|
||||||
|
};
|
280
XNMonitorServer/TypeDefine.h
Executable file
280
XNMonitorServer/TypeDefine.h
Executable file
@ -0,0 +1,280 @@
|
|||||||
|
/**
|
||||||
|
* @file TypeDefine.h
|
||||||
|
* @brief 类型定义头文件
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <fastdds/dds/domain/DomainParticipant.hpp>
|
||||||
|
#include <fastdds/dds/domain/DomainParticipantFactory.hpp>
|
||||||
|
#include <fastdds/dds/topic/TypeSupport.hpp>
|
||||||
|
#include <fastdds/dds/subscriber/DataReaderListener.hpp>
|
||||||
|
#include <fastdds/dds/subscriber/DataReader.hpp>
|
||||||
|
#include <fastdds/dds/subscriber/qos/DataReaderQos.hpp>
|
||||||
|
#include <fastdds/dds/subscriber/SampleInfo.hpp>
|
||||||
|
#include <fastdds/dds/subscriber/Subscriber.hpp>
|
||||||
|
#include <fastdds/dds/topic/TypeSupport.hpp>
|
||||||
|
#include <fastdds/dds/topic/Topic.hpp>
|
||||||
|
#include <fastdds/dds/topic/TopicDataType.hpp>
|
||||||
|
#include <fastdds/dds/publisher/Publisher.hpp>
|
||||||
|
#include <fastdds/dds/publisher/qos/PublisherQos.hpp>
|
||||||
|
#include <fastdds/dds/publisher/DataWriter.hpp>
|
||||||
|
#include <fastdds/dds/publisher/qos/DataWriterQos.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 域参与者
|
||||||
|
*/
|
||||||
|
using XNParticipant = eprosima::fastdds::dds::DomainParticipant;
|
||||||
|
/**
|
||||||
|
* @brief 域参与者Qos
|
||||||
|
*/
|
||||||
|
using XNParticipantQos = eprosima::fastdds::dds::DomainParticipantQos;
|
||||||
|
/**
|
||||||
|
* @brief 域参与者工厂
|
||||||
|
*/
|
||||||
|
using XNParticipantFactory = eprosima::fastdds::dds::DomainParticipantFactory;
|
||||||
|
/**
|
||||||
|
* @brief 数据读取器监听器
|
||||||
|
*/
|
||||||
|
using XNDataReaderListener = eprosima::fastdds::dds::DataReaderListener;
|
||||||
|
/**
|
||||||
|
* @brief 数据读取器
|
||||||
|
*/
|
||||||
|
using XNDataReader = eprosima::fastdds::dds::DataReader;
|
||||||
|
/**
|
||||||
|
* @brief 数据读取器Qos
|
||||||
|
*/
|
||||||
|
using XNDataReaderQos = eprosima::fastdds::dds::DataReaderQos;
|
||||||
|
/**
|
||||||
|
* @brief 样本信息
|
||||||
|
*/
|
||||||
|
using XNSampleInfo = eprosima::fastdds::dds::SampleInfo;
|
||||||
|
/**
|
||||||
|
* @brief 订阅者
|
||||||
|
*/
|
||||||
|
using XNSubscriber = eprosima::fastdds::dds::Subscriber;
|
||||||
|
/**
|
||||||
|
* @brief 类型支持
|
||||||
|
*/
|
||||||
|
using XNTypeSupport = eprosima::fastdds::dds::TypeSupport;
|
||||||
|
/**
|
||||||
|
* @brief 数据写入器
|
||||||
|
*/
|
||||||
|
using XNDataWriter = eprosima::fastdds::dds::DataWriter;
|
||||||
|
/**
|
||||||
|
* @brief 数据写入器Qos
|
||||||
|
*/
|
||||||
|
using XNDataWriterQos = eprosima::fastdds::dds::DataWriterQos;
|
||||||
|
/**
|
||||||
|
* @brief 发布者
|
||||||
|
*/
|
||||||
|
using XNPublisher = eprosima::fastdds::dds::Publisher;
|
||||||
|
/**
|
||||||
|
* @brief 发布者Qos
|
||||||
|
*/
|
||||||
|
using XNPublisherQos = eprosima::fastdds::dds::PublisherQos;
|
||||||
|
/**
|
||||||
|
* @brief 主题
|
||||||
|
*/
|
||||||
|
using XNTopic = eprosima::fastdds::dds::Topic;
|
||||||
|
/**
|
||||||
|
* @brief 主题数据类型
|
||||||
|
*/
|
||||||
|
using XNTopicDataType = eprosima::fastdds::dds::TopicDataType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 主题信息
|
||||||
|
*/
|
||||||
|
struct TopicInfo {
|
||||||
|
/**
|
||||||
|
* @brief 主题
|
||||||
|
*/
|
||||||
|
XNTopic *topic;
|
||||||
|
/**
|
||||||
|
* @brief 发布者
|
||||||
|
*/
|
||||||
|
XNPublisher *publisher;
|
||||||
|
/**
|
||||||
|
* @brief 数据写入器
|
||||||
|
*/
|
||||||
|
XNDataWriter *dataWriter;
|
||||||
|
/**
|
||||||
|
* @brief 订阅者
|
||||||
|
*/
|
||||||
|
XNSubscriber *subscriber;
|
||||||
|
/**
|
||||||
|
* @brief 数据读取器
|
||||||
|
*/
|
||||||
|
XNDataReader *dataReader;
|
||||||
|
/**
|
||||||
|
* @brief 数据读取器监听器
|
||||||
|
*/
|
||||||
|
XNDataReaderListener *listener;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 错误码定义
|
||||||
|
enum class XNDDSErrorCode {
|
||||||
|
SUCCESS = 0,
|
||||||
|
INIT_FAILED = -1,
|
||||||
|
TOPIC_CREATE_FAILED = -2,
|
||||||
|
PUBLISHER_CREATE_FAILED = -3,
|
||||||
|
SUBSCRIBER_CREATE_FAILED = -4,
|
||||||
|
DATAWRITER_CREATE_FAILED = -5,
|
||||||
|
DATAREADER_CREATE_FAILED = -6,
|
||||||
|
INVALID_PARAM = -7,
|
||||||
|
NOT_INITIALIZED = -8
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 运行时数据
|
||||||
|
*/
|
||||||
|
struct XNRuntimeData {
|
||||||
|
/**
|
||||||
|
* @brief 名称
|
||||||
|
*/
|
||||||
|
std::string m_name;
|
||||||
|
/**
|
||||||
|
* @brief 线程ID或模型ID
|
||||||
|
*/
|
||||||
|
unsigned int m_id;
|
||||||
|
/**
|
||||||
|
* @brief 运行状态
|
||||||
|
*/
|
||||||
|
unsigned int m_RunningState;
|
||||||
|
/**
|
||||||
|
* @brief CPU亲和性掩码
|
||||||
|
* @note 用作线程信息时存储CPU亲和性掩码,从低位到高位依次为CPU 0~31 的亲和性掩码
|
||||||
|
* @note 用作模型信息时存储存储线程号
|
||||||
|
*/
|
||||||
|
unsigned int m_AffinityMask;
|
||||||
|
/**
|
||||||
|
* @brief 节点ID
|
||||||
|
* @note 用作模型信息时存储节点号
|
||||||
|
*/
|
||||||
|
unsigned int m_NodeID;
|
||||||
|
/**
|
||||||
|
* @brief 优先级
|
||||||
|
*/
|
||||||
|
unsigned int m_Priority;
|
||||||
|
/**
|
||||||
|
* @brief 设定频率
|
||||||
|
*/
|
||||||
|
double m_SetFrequency;
|
||||||
|
/**
|
||||||
|
* @brief 当前频率
|
||||||
|
*/
|
||||||
|
std::vector<double> m_CurrentFrequency;
|
||||||
|
/**
|
||||||
|
* @brief 最大频率
|
||||||
|
*/
|
||||||
|
double m_MaxFrequency;
|
||||||
|
/**
|
||||||
|
* @brief 最小频率
|
||||||
|
*/
|
||||||
|
double m_MinFrequency;
|
||||||
|
/**
|
||||||
|
* @brief 平均频率
|
||||||
|
*/
|
||||||
|
double m_AvgFrequency;
|
||||||
|
/**
|
||||||
|
* @brief 周期计数
|
||||||
|
*/
|
||||||
|
double m_CycleCount;
|
||||||
|
/**
|
||||||
|
* @brief 设定周期
|
||||||
|
*/
|
||||||
|
double m_SetPeriod;
|
||||||
|
/**
|
||||||
|
* @brief 当前周期
|
||||||
|
*/
|
||||||
|
std::vector<double> m_CurrentPeriod;
|
||||||
|
/**
|
||||||
|
* @brief 最大周期
|
||||||
|
*/
|
||||||
|
double m_MaxPeriod;
|
||||||
|
/**
|
||||||
|
* @brief 最小周期
|
||||||
|
*/
|
||||||
|
double m_MinPeriod;
|
||||||
|
/**
|
||||||
|
* @brief 平均周期
|
||||||
|
*/
|
||||||
|
double m_AvgPeriod;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 成员变量定义结构体
|
||||||
|
* @note 成员变量包含数据类型、变量名、是否为数组、数组大小、描述
|
||||||
|
*/
|
||||||
|
struct MemberVariable {
|
||||||
|
/**
|
||||||
|
* @brief 数据类型
|
||||||
|
*/
|
||||||
|
std::string dataType;
|
||||||
|
/**
|
||||||
|
* @brief 变量名
|
||||||
|
*/
|
||||||
|
std::string variableName;
|
||||||
|
/**
|
||||||
|
* @brief 是否为数组
|
||||||
|
*/
|
||||||
|
bool isArray;
|
||||||
|
/**
|
||||||
|
* @brief 数组大小
|
||||||
|
*/
|
||||||
|
std::vector<int> arraySizes;
|
||||||
|
/**
|
||||||
|
* @brief 描述
|
||||||
|
*/
|
||||||
|
std::string description;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 接口结构体定义结构体
|
||||||
|
* @note 接口结构体定义包含结构体名称、成员变量
|
||||||
|
*/
|
||||||
|
struct StructDefinition {
|
||||||
|
/**
|
||||||
|
* @brief 结构体名称
|
||||||
|
*/
|
||||||
|
std::string structName;
|
||||||
|
/**
|
||||||
|
* @brief 成员变量
|
||||||
|
*/
|
||||||
|
std::vector<std::shared_ptr<MemberVariable>> memberVariables;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 命名空间定义结构体
|
||||||
|
* @note 命名空间定义包含命名空间名称、接口结构体定义、子命名空间定义
|
||||||
|
*/
|
||||||
|
struct NamespaceDefinition {
|
||||||
|
/**
|
||||||
|
* @brief 命名空间名称
|
||||||
|
*/
|
||||||
|
std::string namespaceName;
|
||||||
|
/**
|
||||||
|
* @brief 结构体定义
|
||||||
|
*/
|
||||||
|
std::vector<std::shared_ptr<StructDefinition>> structDefinitions;
|
||||||
|
/**
|
||||||
|
* @brief 子命名空间
|
||||||
|
*/
|
||||||
|
std::vector<std::shared_ptr<NamespaceDefinition>> childNamespaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 模型接口定义
|
||||||
|
* @note 模型接口定义包含模型名称和接口命名空间定义
|
||||||
|
*/
|
||||||
|
struct ModelDefinition {
|
||||||
|
/**
|
||||||
|
* @brief 模型名称
|
||||||
|
*/
|
||||||
|
std::string modelName;
|
||||||
|
/**
|
||||||
|
* @brief 命名空间定义
|
||||||
|
*/
|
||||||
|
std::vector<std::shared_ptr<NamespaceDefinition>> namespaceDefinitions;
|
||||||
|
};
|
372
XNMonitorServer/XNMonitorInterface.cpp
Normal file
372
XNMonitorServer/XNMonitorInterface.cpp
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
/**
|
||||||
|
* @file XNMonitorInterface.cpp
|
||||||
|
* @brief 监控服务器接口实现
|
||||||
|
*/
|
||||||
|
#include "XNMonitorInterface.h"
|
||||||
|
#include "TopicManager.h"
|
||||||
|
#include <string>
|
||||||
|
#include <mutex>
|
||||||
|
#include "SystemInfoMonitor.h"
|
||||||
|
|
||||||
|
// 全局变量
|
||||||
|
static std::string g_lastError;
|
||||||
|
static std::mutex g_errorMutex;
|
||||||
|
|
||||||
|
static bool g_initialized = false;
|
||||||
|
|
||||||
|
SystemInfoMonitor *systemInfoMonitor = nullptr;
|
||||||
|
bool g_systemInfoMonitorStarted = false;
|
||||||
|
|
||||||
|
// 设置错误信息
|
||||||
|
static void SetLastError(const char *error)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(g_errorMutex);
|
||||||
|
g_lastError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化函数实现
|
||||||
|
int XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg, int errorMsgSize)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (g_initialized) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, "DDSMonitor Initialized Successfully", errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (!domainId || domainIdLen <= 0) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, "Invalid domainId", errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建临时字符串,确保以null结尾
|
||||||
|
std::string domainIdStr(domainId, domainIdLen);
|
||||||
|
int domainIdInt = std::stoi(domainIdStr);
|
||||||
|
if (domainIdInt < 0 || domainIdInt > 232) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, "Invalid domainId", errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
XNDDSErrorCode ret = TopicManager::Instance()->initializeParticipant(domainIdInt);
|
||||||
|
if (ret != XNDDSErrorCode::SUCCESS) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
std::string error = "Failed to initialize DDSMonitor, error code: "
|
||||||
|
+ std::to_string(static_cast<int>(ret));
|
||||||
|
strncpy(errorMsg, error.c_str(), errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_initialized = true;
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, "DDSMonitor Initialized Successfully", errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, e.what(), errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int XN_StartMonitorSystemInfo(char *errorMsg, int errorMsgSize)
|
||||||
|
{
|
||||||
|
if (!g_initialized) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, "DDSMonitor Not Initialized", errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
systemInfoMonitor = new SystemInfoMonitor();
|
||||||
|
std::string ret = systemInfoMonitor->Initialize();
|
||||||
|
if (ret == "Success") {
|
||||||
|
g_systemInfoMonitorStarted = true;
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, ret.c_str(), errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, ret.c_str(), errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
if (errorMsg && errorMsgSize > 0) {
|
||||||
|
strncpy(errorMsg, e.what(), errorMsgSize - 1);
|
||||||
|
errorMsg[errorMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int XN_GetSystemInfo(char *infoMsg, int infoMsgSize)
|
||||||
|
{
|
||||||
|
if (!g_systemInfoMonitorStarted) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "System Info Monitor Not Started", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string info = systemInfoMonitor->GetSystemInfo();
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, info.c_str(), infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, e.what(), infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int XN_GetAllThreadInfo(char *infoMsg, int infoMsgSize)
|
||||||
|
{
|
||||||
|
if (!g_systemInfoMonitorStarted) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, "System Info Monitor Not Started", infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string info = systemInfoMonitor->GetAllThreadInfo();
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, info.c_str(), infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
if (infoMsg && infoMsgSize > 0) {
|
||||||
|
strncpy(infoMsg, e.what(), infoMsgSize - 1);
|
||||||
|
infoMsg[infoMsgSize - 1] = '\0';
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void XN_StopMonitorSystemInfo()
|
||||||
|
{
|
||||||
|
if (g_systemInfoMonitorStarted) {
|
||||||
|
delete systemInfoMonitor;
|
||||||
|
systemInfoMonitor = nullptr;
|
||||||
|
g_systemInfoMonitorStarted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理函数实现
|
||||||
|
void XN_Cleanup()
|
||||||
|
{
|
||||||
|
if (g_initialized) {
|
||||||
|
TopicManager::cleanupParticipant();
|
||||||
|
g_initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 注册发布者实现
|
||||||
|
// XNDDSErrorCode XN_RegisterPublisher(const char *topicName, void **dataWriter)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!topicName || !dataWriter) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// auto *writer =
|
||||||
|
// TopicManager::Instance()->registerPublisher<XNRuntimeData>(topicName);
|
||||||
|
// if (!writer) {
|
||||||
|
// SetLastError("Failed to create publisher");
|
||||||
|
// return XNDDSErrorCode::PUBLISHER_CREATE_FAILED;
|
||||||
|
// }
|
||||||
|
// *dataWriter = writer;
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// } catch (const std::exception &e) {
|
||||||
|
// SetLastError(e.what());
|
||||||
|
// return XNDDSErrorCode::PUBLISHER_CREATE_FAILED;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 注销发布者实现
|
||||||
|
// XNDDSErrorCode XN_UnregisterPublisher(const char *topicName)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!topicName) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// TopicManager::Instance()->unregisterPublisher(topicName);
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// } catch (const std::exception &e) {
|
||||||
|
// SetLastError(e.what());
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 数据写入实现
|
||||||
|
// XNDDSErrorCode XN_WriteData(void *dataWriter, const void *data, size_t dataSize)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!dataWriter || !data || dataSize != sizeof(XNRuntimeData)) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// auto *writer = static_cast<XNDataWriter *>(dataWriter);
|
||||||
|
// if (writer->write(data) != eprosima::fastdds::dds::RETCODE_OK) {
|
||||||
|
// SetLastError("Failed to write data");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// } catch (const std::exception &e) {
|
||||||
|
// SetLastError(e.what());
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 订阅回调包装器
|
||||||
|
// class SubscriberCallbackWrapper
|
||||||
|
// {
|
||||||
|
// public:
|
||||||
|
// SubscriberCallbackWrapper(XNDataCallback callback, void *userData)
|
||||||
|
// : callback_(callback), userData_(userData)
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void operator()(const XNRuntimeData &data)
|
||||||
|
// {
|
||||||
|
// if (callback_) {
|
||||||
|
// callback_(&data, sizeof(XNRuntimeData), userData_);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// private:
|
||||||
|
// XNDataCallback callback_;
|
||||||
|
// void *userData_;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // 注册订阅者实现
|
||||||
|
// XNDDSErrorCode XN_RegisterSubscriber(const char *topicName, XNDataCallback callback, void *userData)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!topicName || !callback) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// // auto wrapper = std::make_shared<SubscriberCallbackWrapper>(callback, userData);
|
||||||
|
// // TopicManager::Instance()->registerSubscriber<XNRuntimeData>(
|
||||||
|
// // topicName, [wrapper](const XNRuntimeData &data) { (*wrapper)(data); });
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// } catch (const std::exception &e) {
|
||||||
|
// SetLastError(e.what());
|
||||||
|
// return XNDDSErrorCode::SUBSCRIBER_CREATE_FAILED;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 注销订阅者实现
|
||||||
|
// XNDDSErrorCode XN_UnregisterSubscriber(const char *topicName)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!topicName) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// TopicManager::Instance()->unregisterSubscriber(topicName);
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// } catch (const std::exception &e) {
|
||||||
|
// SetLastError(e.what());
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 获取运行时数据实现
|
||||||
|
// XNDDSErrorCode XN_GetRuntimeData(const char *name, XNRuntimeData *data)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!name || !data) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // TODO: 实现从数据存储中获取运行时数据
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 设置运行时数据实现
|
||||||
|
// XNDDSErrorCode XN_SetRuntimeData(const char *name, const XNRuntimeData *data)
|
||||||
|
// {
|
||||||
|
// if (!g_initialized) {
|
||||||
|
// SetLastError("Not initialized");
|
||||||
|
// return XNDDSErrorCode::NOT_INITIALIZED;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!name || !data) {
|
||||||
|
// SetLastError("Invalid parameters");
|
||||||
|
// return XNDDSErrorCode::INVALID_PARAM;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // TODO: 实现将运行时数据保存到数据存储中
|
||||||
|
// return XNDDSErrorCode::SUCCESS;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 获取最后错误信息实现
|
||||||
|
// const char *XN_GetLastError()
|
||||||
|
// {
|
||||||
|
// std::lock_guard<std::mutex> lock(g_errorMutex);
|
||||||
|
// return g_lastError.c_str();
|
||||||
|
// }
|
57
XNMonitorServer/XNMonitorInterface.h
Normal file
57
XNMonitorServer/XNMonitorInterface.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @file XNMonitorInterface.h
|
||||||
|
* @brief 监控服务器接口定义
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TypeDefine.h"
|
||||||
|
#include "XNMonitorServer_global.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
// 初始化函数
|
||||||
|
int XNMONITORSERVER_EXPORT XN_Initialize(const char *domainId, int domainIdLen, char *errorMsg,
|
||||||
|
int errorMsgSize);
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
void XNMONITORSERVER_EXPORT XN_Cleanup();
|
||||||
|
|
||||||
|
// 启动监控系统信息
|
||||||
|
int XNMONITORSERVER_EXPORT XN_StartMonitorSystemInfo(char *errorMsg, int errorMsgSize);
|
||||||
|
|
||||||
|
// 获取系统信息
|
||||||
|
int XNMONITORSERVER_EXPORT XN_GetSystemInfo(char *infoMsg, int infoMsgSize);
|
||||||
|
|
||||||
|
// 获取所有线程信息
|
||||||
|
int XNMONITORSERVER_EXPORT XN_GetAllThreadInfo(char *infoMsg, int infoMsgSize);
|
||||||
|
|
||||||
|
// 停止监控系统信息
|
||||||
|
void XNMONITORSERVER_EXPORT XN_StopMonitorSystemInfo();
|
||||||
|
// // 主题管理接口
|
||||||
|
// XNDDSErrorCode XN_RegisterPublisher(const char *topicName, void **dataWriter);
|
||||||
|
// XNDDSErrorCode XN_UnregisterPublisher(const char *topicName);
|
||||||
|
|
||||||
|
// // 数据写入接口
|
||||||
|
// XNDDSErrorCode XN_WriteData(void *dataWriter, const void *data, size_t dataSize);
|
||||||
|
|
||||||
|
// // 订阅回调函数类型
|
||||||
|
// typedef void (*XNDataCallback)(const void *data, size_t dataSize, void *userData);
|
||||||
|
|
||||||
|
// // 订阅接口
|
||||||
|
// XNDDSErrorCode XN_RegisterSubscriber(const char *topicName, XNDataCallback callback,
|
||||||
|
// void *userData);
|
||||||
|
// XNDDSErrorCode XN_UnregisterSubscriber(const char *topicName);
|
||||||
|
|
||||||
|
// // 运行时数据接口
|
||||||
|
// XNDDSErrorCode XN_GetRuntimeData(const char *name, XNRuntimeData *data);
|
||||||
|
// XNDDSErrorCode XN_SetRuntimeData(const char *name, const XNRuntimeData *data);
|
||||||
|
|
||||||
|
// 错误信息获取
|
||||||
|
//const char *MONITOR_EXPORT XN_GetLastError();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
7
XNMonitorServer/XNMonitorServer_global.h
Normal file
7
XNMonitorServer/XNMonitorServer_global.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef XNMonitorServer_EXPORTS
|
||||||
|
# define XNMONITORSERVER_EXPORT __attribute__((visibility("default")))
|
||||||
|
#else
|
||||||
|
# define XNMONITORSERVER_EXPORT __attribute__((visibility("default")))
|
||||||
|
#endif
|
@ -2,10 +2,255 @@ class SimulationMonitor extends HTMLElement {
|
|||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.attachShadow({ mode: 'open' });
|
this.attachShadow({ mode: 'open' });
|
||||||
|
this.monitorStatus = {
|
||||||
|
isMonitoring: false,
|
||||||
|
lastError: null
|
||||||
|
};
|
||||||
|
this.systemInfo = null;
|
||||||
|
this.threadInfo = null;
|
||||||
|
this.statusCheckInterval = null;
|
||||||
|
this.chart = null;
|
||||||
|
this.chartInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.render();
|
this.render();
|
||||||
|
// 等待组件完全加载
|
||||||
|
setTimeout(() => {
|
||||||
|
this.initializeComponent();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeComponent() {
|
||||||
|
if (this.chartInitialized) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.initChart();
|
||||||
|
this.startStatusCheck();
|
||||||
|
this.chartInitialized = true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化组件失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
this.stopStatusCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
startStatusCheck() {
|
||||||
|
this.checkMonitorStatus();
|
||||||
|
this.statusCheckInterval = setInterval(() => {
|
||||||
|
this.checkMonitorStatus();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopStatusCheck() {
|
||||||
|
if (this.statusCheckInterval) {
|
||||||
|
clearInterval(this.statusCheckInterval);
|
||||||
|
this.statusCheckInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkMonitorStatus() {
|
||||||
|
try {
|
||||||
|
// 获取监控状态
|
||||||
|
const statusResponse = await fetch('/api/system-monitor/status');
|
||||||
|
const statusData = await statusResponse.json();
|
||||||
|
this.monitorStatus = statusData;
|
||||||
|
|
||||||
|
if (this.monitorStatus.isMonitoring) {
|
||||||
|
// 获取系统信息
|
||||||
|
const systemResponse = await fetch('/api/system-monitor/system-info');
|
||||||
|
const systemData = await systemResponse.json();
|
||||||
|
this.systemInfo = systemData.data;
|
||||||
|
console.log('系统信息:', this.systemInfo);
|
||||||
|
|
||||||
|
// 获取线程信息
|
||||||
|
const threadResponse = await fetch('/api/system-monitor/thread-info');
|
||||||
|
const threadData = await threadResponse.json();
|
||||||
|
this.threadInfo = threadData.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateUI();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取监控状态失败:', error);
|
||||||
|
this.monitorStatus.lastError = error.message;
|
||||||
|
this.updateUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUI() {
|
||||||
|
const input = this.shadowRoot.querySelector('.domain-input');
|
||||||
|
const startButton = this.shadowRoot.querySelector('.start-button');
|
||||||
|
const stopButton = this.shadowRoot.querySelector('.stop-button');
|
||||||
|
const statusDisplay = this.shadowRoot.querySelector('.status-display');
|
||||||
|
const engineInfo = this.shadowRoot.querySelector('#engine-info');
|
||||||
|
const coreStatus = this.shadowRoot.querySelector('#core-status');
|
||||||
|
const threadTableBody = this.shadowRoot.querySelector('#thread-table-body');
|
||||||
|
|
||||||
|
if (this.monitorStatus.isMonitoring) {
|
||||||
|
input.disabled = true;
|
||||||
|
startButton.disabled = true;
|
||||||
|
stopButton.disabled = false;
|
||||||
|
} else {
|
||||||
|
input.disabled = false;
|
||||||
|
startButton.disabled = false;
|
||||||
|
stopButton.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态显示
|
||||||
|
statusDisplay.textContent = `监控状态: ${this.monitorStatus.isMonitoring ? '运行中' : '已停止'}`;
|
||||||
|
if (this.monitorStatus.lastError) {
|
||||||
|
statusDisplay.textContent += ` (错误: ${this.monitorStatus.lastError})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新引擎信息
|
||||||
|
const engineInfoFields = [
|
||||||
|
{ label: '引擎名称', key: 'name' },
|
||||||
|
{ label: '引擎ID', key: 'id' },
|
||||||
|
{ label: '引擎状态', key: 'status' },
|
||||||
|
{ label: '引擎亲和性', key: 'affinity' },
|
||||||
|
{ label: '线程数', key: 'threadCount' }
|
||||||
|
];
|
||||||
|
|
||||||
|
engineInfo.innerHTML = `
|
||||||
|
<div class="status-grid">
|
||||||
|
${engineInfoFields.map(field => `
|
||||||
|
<div class="status-item">
|
||||||
|
<div class="status-label">${field.label}</div>
|
||||||
|
<div class="status-value">${this.monitorStatus.isMonitoring && this.systemInfo?.engineInfo ?
|
||||||
|
(this.systemInfo.engineInfo[field.key] || '未知') :
|
||||||
|
(field.key === 'status' ? '未运行' : '未知')}</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 更新核心状态
|
||||||
|
const coreStatusFields = [
|
||||||
|
{ label: '主框架状态', key: 'fwStatus' },
|
||||||
|
{ label: '时间管理器状态', key: 'tmStatus' },
|
||||||
|
{ label: '事件管理器状态', key: 'emStatus' },
|
||||||
|
{ label: '环境管理器状态', key: 'sdStatus' },
|
||||||
|
{ label: '线程管理器状态', key: 'thmStatus' },
|
||||||
|
{ label: '模型管理器状态', key: 'mmStatus' },
|
||||||
|
{ label: '服务管理器状态', key: 'smStatus' },
|
||||||
|
{ label: 'DDS管理器状态', key: 'dmStatus' }
|
||||||
|
];
|
||||||
|
|
||||||
|
// 确保coreStatus元素存在
|
||||||
|
if (!coreStatus) {
|
||||||
|
console.error('找不到核心状态元素');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 无论是否有数据,都显示状态项
|
||||||
|
coreStatus.innerHTML = `
|
||||||
|
<div class="status-grid">
|
||||||
|
${coreStatusFields.map(field => `
|
||||||
|
<div class="status-item">
|
||||||
|
<div class="status-label">${field.label}</div>
|
||||||
|
<div class="status-value">${this.monitorStatus.isMonitoring && this.systemInfo?.coreStatus ?
|
||||||
|
(this.systemInfo.coreStatus[field.key] || '未知') : '未知'}</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 更新线程表格
|
||||||
|
if (this.monitorStatus.isMonitoring && this.threadInfo && Array.isArray(this.threadInfo)) {
|
||||||
|
threadTableBody.innerHTML = this.threadInfo.map(thread => `
|
||||||
|
<tr>
|
||||||
|
<td>${thread.name || '未知'}</td>
|
||||||
|
<td>${thread.id || '未知'}</td>
|
||||||
|
<td>${thread.status || '未知'}</td>
|
||||||
|
<td>${thread.priority || '未知'}</td>
|
||||||
|
<td>${thread.runCount || '0'}</td>
|
||||||
|
<td>${(thread.currentFrequency || 0).toFixed(2)}</td>
|
||||||
|
<td>${(thread.setFrequency || 0).toFixed(2)}</td>
|
||||||
|
<td>${(thread.currentPeriod || 0).toFixed(2)}</td>
|
||||||
|
</tr>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
// 更新图表数据
|
||||||
|
this.updateChartData();
|
||||||
|
} else {
|
||||||
|
threadTableBody.innerHTML = '<tr><td colspan="8" style="text-align: center;">暂无线程信息</td></tr>';
|
||||||
|
// 清空图表数据
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.data.labels = [];
|
||||||
|
this.chart.data.datasets = [];
|
||||||
|
this.chart.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startMonitoring() {
|
||||||
|
const domainId = this.shadowRoot.querySelector('.domain-input').value.trim();
|
||||||
|
|
||||||
|
// 验证域ID是否为有效的数字字符串
|
||||||
|
if (!/^\d+$/.test(domainId)) {
|
||||||
|
console.error('域ID必须是有效的数字');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 首先检查DDS监控状态
|
||||||
|
const ddsStatusResponse = await fetch('/api/dds-monitor/status');
|
||||||
|
const ddsStatusData = await ddsStatusResponse.json();
|
||||||
|
|
||||||
|
// 如果DDS监控未初始化,先初始化DDS监控
|
||||||
|
if (!ddsStatusData.isInitialized) {
|
||||||
|
const ddsInitResponse = await fetch('/api/dds-monitor/initialize', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ domainId })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!ddsInitResponse.ok) {
|
||||||
|
const errorData = await ddsInitResponse.json();
|
||||||
|
console.error('DDS监控初始化失败:', errorData.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动系统监控
|
||||||
|
const response = await fetch('/api/system-monitor/start', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ domainId })
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
this.monitorStatus = data.status;
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
console.error('启动监控失败:', data.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动监控失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async stopMonitoring() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/system-monitor/stop', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
this.monitorStatus = data.status;
|
||||||
|
this.updateUI();
|
||||||
|
} else {
|
||||||
|
console.error('停止监控失败:', data.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停止监控失败:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@ -20,37 +265,464 @@ class SimulationMonitor extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.monitor-container {
|
.monitor-container {
|
||||||
background-color: white;
|
display: flex;
|
||||||
border-radius: 8px;
|
flex-direction: column;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
gap: 16px;
|
||||||
padding: 16px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitor-header {
|
.toolbar-section {
|
||||||
display: flex;
|
background-color: white;
|
||||||
justify-content: space-between;
|
border-radius: 8px;
|
||||||
align-items: center;
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
margin-bottom: 20px;
|
padding: 16px;
|
||||||
padding-bottom: 10px;
|
|
||||||
border-bottom: 1px solid #e0e0e0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.monitor-title {
|
.content-container {
|
||||||
font-size: 18px;
|
display: grid;
|
||||||
font-weight: bold;
|
grid-template-columns: 300px 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-section {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-label {
|
||||||
|
font-size: 14px;
|
||||||
color: #333;
|
color: #333;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-input {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.domain-input:disabled {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button {
|
||||||
|
background-color: #4CAF50;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.start-button:hover:not(:disabled) {
|
||||||
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-button {
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stop-button:hover:not(:disabled) {
|
||||||
|
background-color: #da190b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-display {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item .label {
|
||||||
|
color: #666;
|
||||||
|
font-size: 13px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item .value {
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0 0 12px 0;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-table th,
|
||||||
|
.thread-table td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-table th {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thread-table tr:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 300px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#thread-chart {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: 60px;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: #ccc;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider:before {
|
||||||
|
position: absolute;
|
||||||
|
content: "";
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
left: 4px;
|
||||||
|
bottom: 4px;
|
||||||
|
background-color: white;
|
||||||
|
transition: .4s;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider {
|
||||||
|
background-color: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:checked + .slider:before {
|
||||||
|
transform: translateX(26px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-label {
|
||||||
|
flex: 1;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-value {
|
||||||
|
flex: 1;
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="monitor-container">
|
<div class="monitor-container">
|
||||||
<div class="monitor-header">
|
<div class="toolbar-section">
|
||||||
<div class="monitor-title">仿真监控</div>
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-left">
|
||||||
|
<div class="input-group">
|
||||||
|
<label class="input-label">DDS通信域ID:</label>
|
||||||
|
<input type="text" class="domain-input" value="10">
|
||||||
|
</div>
|
||||||
|
<button class="control-button start-button" onclick="this.getRootNode().host.startMonitoring()">开始监控</button>
|
||||||
|
<button class="control-button stop-button" onclick="this.getRootNode().host.stopMonitoring()">停止监控</button>
|
||||||
|
</div>
|
||||||
|
<div class="status-display">监控状态: 未启动</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-container">
|
||||||
|
<div class="left-panel">
|
||||||
|
<div class="panel-section">
|
||||||
|
<h3>引擎信息</h3>
|
||||||
|
<div class="info-grid" id="engine-info"></div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-section">
|
||||||
|
<h3>核心状态</h3>
|
||||||
|
<div class="info-grid" id="core-status"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-panel">
|
||||||
|
<div class="panel-section">
|
||||||
|
<h3>线程信息</h3>
|
||||||
|
<table class="thread-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>线程名称</th>
|
||||||
|
<th>线程ID</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>优先级</th>
|
||||||
|
<th>运行次数</th>
|
||||||
|
<th>当前频率(Hz)</th>
|
||||||
|
<th>设置频率(Hz)</th>
|
||||||
|
<th>当前周期(ms)</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="thread-table-body"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="panel-section">
|
||||||
|
<h3>线程监控</h3>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="thread-chart"></canvas>
|
||||||
|
</div>
|
||||||
|
<div class="chart-controls">
|
||||||
|
<span>显示类型:</span>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" id="display-type-switch">
|
||||||
|
<span class="slider"></span>
|
||||||
|
</label>
|
||||||
|
<span id="display-type-label">频率</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>仿真监控组件内容(待实现)</div>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initChart() {
|
||||||
|
const chartElement = this.shadowRoot.querySelector('#thread-chart');
|
||||||
|
if (!chartElement) {
|
||||||
|
console.error('找不到图表元素');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 Chart.js 已加载
|
||||||
|
if (typeof Chart === 'undefined') {
|
||||||
|
console.error('Chart.js 未加载');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建图表实例
|
||||||
|
const ctx = chartElement.getContext('2d');
|
||||||
|
if (!ctx) {
|
||||||
|
console.error('无法获取 canvas 上下文');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 销毁已存在的图表实例
|
||||||
|
if (this.chart) {
|
||||||
|
this.chart.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新的图表实例
|
||||||
|
this.chart = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [],
|
||||||
|
datasets: []
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
animation: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: true
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听显示类型切换
|
||||||
|
const switchInput = this.shadowRoot.querySelector('#display-type-switch');
|
||||||
|
const displayTypeLabel = this.shadowRoot.querySelector('#display-type-label');
|
||||||
|
|
||||||
|
if (switchInput && displayTypeLabel) {
|
||||||
|
switchInput.addEventListener('change', (e) => {
|
||||||
|
const isFrequency = e.target.checked;
|
||||||
|
displayTypeLabel.textContent = isFrequency ? '频率' : '周期';
|
||||||
|
this.updateChartDisplayType(isFrequency);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化图表失败:', error);
|
||||||
|
this.chart = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartData() {
|
||||||
|
if (!this.chart || !this.threadInfo || !Array.isArray(this.threadInfo)) return;
|
||||||
|
|
||||||
|
const isFrequency = this.shadowRoot.querySelector('#display-type-switch').checked;
|
||||||
|
const currentTime = new Date().toLocaleTimeString();
|
||||||
|
|
||||||
|
// 确保图表数据集存在
|
||||||
|
if (!this.chart.data.datasets || this.chart.data.datasets.length === 0) {
|
||||||
|
this.updateChartDisplayType(isFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新数据
|
||||||
|
const newData = {
|
||||||
|
labels: [...this.chart.data.labels, currentTime].slice(-30),
|
||||||
|
datasets: this.threadInfo.map((thread, index) => {
|
||||||
|
const dataset = this.chart.data.datasets[index] || {
|
||||||
|
label: thread.name,
|
||||||
|
data: [],
|
||||||
|
borderColor: this.getRandomColor(),
|
||||||
|
fill: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const value = isFrequency ? (thread.currentFrequency || 0) : (thread.currentPeriod || 0);
|
||||||
|
dataset.data = [...dataset.data, value].slice(-30);
|
||||||
|
|
||||||
|
return dataset;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用新数据更新图表
|
||||||
|
this.chart.data = newData;
|
||||||
|
this.chart.update('none'); // 使用 'none' 模式更新,避免触发动画和事件
|
||||||
|
}
|
||||||
|
|
||||||
|
updateChartDisplayType(isFrequency) {
|
||||||
|
if (!this.threadInfo) return;
|
||||||
|
|
||||||
|
const datasets = this.threadInfo.map(thread => ({
|
||||||
|
label: thread.name,
|
||||||
|
data: [],
|
||||||
|
borderColor: this.getRandomColor(),
|
||||||
|
fill: false
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.chart.data.datasets = datasets;
|
||||||
|
this.chart.update('none'); // 使用 'none' 模式更新
|
||||||
|
}
|
||||||
|
|
||||||
|
getRandomColor() {
|
||||||
|
const letters = '0123456789ABCDEF';
|
||||||
|
let color = '#';
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
color += letters[Math.floor(Math.random() * 16)];
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('simulation-monitor', SimulationMonitor);
|
customElements.define('simulation-monitor', SimulationMonitor);
|
@ -6,6 +6,7 @@
|
|||||||
<title>XNSim - 主页面</title>
|
<title>XNSim - 主页面</title>
|
||||||
<link rel="icon" type="image/png" href="assets/icons/XNSim.png">
|
<link rel="icon" type="image/png" href="assets/icons/XNSim.png">
|
||||||
<link rel="shortcut icon" type="image/png" href="assets/icons/XNSim.png">
|
<link rel="shortcut icon" type="image/png" href="assets/icons/XNSim.png">
|
||||||
|
<script src="chart.min.js"></script>
|
||||||
<script src="components/auth-component.js"></script>
|
<script src="components/auth-component.js"></script>
|
||||||
<script src="components/main-toolbar.js"></script>
|
<script src="components/main-toolbar.js"></script>
|
||||||
<script src="components/sub-toolbar.js"></script>
|
<script src="components/sub-toolbar.js"></script>
|
||||||
|
@ -10,24 +10,46 @@ const xnCorePath = process.env.XNCore || '';
|
|||||||
const isWindows = os.platform() === 'win32';
|
const isWindows = os.platform() === 'win32';
|
||||||
const libExtension = isWindows ? '.dll' : '.so';
|
const libExtension = isWindows ? '.dll' : '.so';
|
||||||
const libPrefix = isWindows ? '' : 'lib';
|
const libPrefix = isWindows ? '' : 'lib';
|
||||||
const libName = `${libPrefix}Login${libExtension}`;
|
|
||||||
const libPath = path.join(xnCorePath, 'lib', libName);
|
// Login库配置
|
||||||
|
const loginLibName = `${libPrefix}Login${libExtension}`;
|
||||||
|
const loginLibPath = path.join(xnCorePath, 'lib', loginLibName);
|
||||||
|
|
||||||
|
// MonitorServer库配置
|
||||||
|
const monitorLibName = `${libPrefix}XNMonitorServer${libExtension}`;
|
||||||
|
const monitorLibPath = path.join(xnCorePath, 'lib', monitorLibName);
|
||||||
|
|
||||||
// 定义Buffer类型
|
// 定义Buffer类型
|
||||||
const BufferType = ref.refType(ref.types.void);
|
const BufferType = ref.refType(ref.types.void);
|
||||||
const StringType = ref.types.CString;
|
const StringType = ref.types.CString;
|
||||||
|
const IntType = ref.types.int;
|
||||||
|
|
||||||
// 定义动态库函数接口
|
// 定义动态库函数接口
|
||||||
let loginLib;
|
let loginLib;
|
||||||
|
let monitorLib;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loginLib = ffi.Library(libPath, {
|
loginLib = ffi.Library(loginLibPath, {
|
||||||
'validateUser': ['int', [BufferType, 'size_t', BufferType, 'size_t']],
|
'validateUser': ['int', [BufferType, 'size_t', BufferType, 'size_t']],
|
||||||
'getUserInfo': [StringType, ['int']],
|
'getUserInfo': [StringType, ['int']],
|
||||||
'cleanup': ['void', []],
|
'cleanup': ['void', []],
|
||||||
'registerUser': ['int', [BufferType, 'size_t', BufferType, 'size_t', BufferType, 'size_t']]
|
'registerUser': ['int', [BufferType, 'size_t', BufferType, 'size_t', BufferType, 'size_t']]
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`加载 ${libName} 失败:`, error);
|
console.error(`加载 ${loginLibName} 失败:`, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
monitorLib = ffi.Library(monitorLibPath, {
|
||||||
|
'XN_Initialize': ['int', [StringType, 'int', StringType, 'int']],
|
||||||
|
'XN_Cleanup': ['void', []],
|
||||||
|
'XN_StartMonitorSystemInfo': ['int', [StringType, 'int']],
|
||||||
|
'XN_GetSystemInfo': ['int', [StringType, 'int']],
|
||||||
|
'XN_GetAllThreadInfo': ['int', [StringType, 'int']],
|
||||||
|
'XN_StopMonitorSystemInfo': ['void', []]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`加载 ${monitorLibName} 失败:`, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册进程退出时的清理函数
|
// 注册进程退出时的清理函数
|
||||||
@ -57,8 +79,111 @@ function stringToBuffer(str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化监控服务器
|
||||||
|
function initializeMonitor(domainId) {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 创建错误消息缓冲区
|
||||||
|
const errorMsg = Buffer.alloc(1024);
|
||||||
|
const result = monitorLib.XN_Initialize(domainId, domainId.length, errorMsg, errorMsg.length);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
return `初始化失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return '初始化成功';
|
||||||
|
} catch (error) {
|
||||||
|
return `初始化失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理监控服务器资源
|
||||||
|
function cleanupMonitor() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
monitorLib.XN_Cleanup();
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动监控系统信息
|
||||||
|
function startMonitorSystemInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const errorMsg = Buffer.alloc(1024);
|
||||||
|
const result = monitorLib.XN_StartMonitorSystemInfo(errorMsg, errorMsg.length);
|
||||||
|
if (result !== 0) {
|
||||||
|
return `启动失败: ${errorMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return '启动成功';
|
||||||
|
} catch (error) {
|
||||||
|
return `启动失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取系统信息
|
||||||
|
function getSystemInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const infoMsg = Buffer.alloc(4096);
|
||||||
|
const result = monitorLib.XN_GetSystemInfo(infoMsg, infoMsg.length);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
return `获取失败: ${infoMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return infoMsg.toString('utf8').replace(/\0/g, '');
|
||||||
|
} catch (error) {
|
||||||
|
return `获取失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有线程信息
|
||||||
|
function getAllThreadInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return '监控服务器库未加载';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const infoMsg = Buffer.alloc(4096);
|
||||||
|
const result = monitorLib.XN_GetAllThreadInfo(infoMsg, infoMsg.length);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
return `获取失败: ${infoMsg.toString('utf8').replace(/\0/g, '')}`;
|
||||||
|
}
|
||||||
|
return infoMsg.toString('utf8').replace(/\0/g, '');
|
||||||
|
} catch (error) {
|
||||||
|
return `获取失败: ${error.message}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止监控系统信息
|
||||||
|
function stopMonitorSystemInfo() {
|
||||||
|
if (!monitorLib) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
monitorLib.XN_StopMonitorSystemInfo();
|
||||||
|
} catch (error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
loginLib,
|
loginLib,
|
||||||
|
monitorLib,
|
||||||
performCleanup,
|
performCleanup,
|
||||||
stringToBuffer
|
stringToBuffer,
|
||||||
|
initializeMonitor,
|
||||||
|
cleanupMonitor,
|
||||||
|
startMonitorSystemInfo,
|
||||||
|
getSystemInfo,
|
||||||
|
getAllThreadInfo,
|
||||||
|
stopMonitorSystemInfo
|
||||||
};
|
};
|
76
XNSimHtml/routes/DDSMonitor.js
Normal file
76
XNSimHtml/routes/DDSMonitor.js
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { initializeMonitor, cleanupMonitor } = require('../modules/cleanup');
|
||||||
|
|
||||||
|
// 存储监控服务的状态
|
||||||
|
let monitorStatus = {
|
||||||
|
isInitialized: false,
|
||||||
|
domainId: null,
|
||||||
|
lastError: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化监控服务
|
||||||
|
router.post('/initialize', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { domainId } = req.body;
|
||||||
|
|
||||||
|
if (!domainId) {
|
||||||
|
return res.status(400).json({ error: '缺少必要的domainId参数' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitorStatus.isInitialized) {
|
||||||
|
return res.status(400).json({ error: '监控服务已经初始化' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = initializeMonitor(domainId);
|
||||||
|
if (result && result.includes('失败')) {
|
||||||
|
monitorStatus.lastError = result;
|
||||||
|
return res.status(500).json({ error: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
monitorStatus.isInitialized = true;
|
||||||
|
monitorStatus.domainId = domainId;
|
||||||
|
monitorStatus.lastError = null;
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: '监控服务初始化成功',
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化监控服务失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '初始化监控服务失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 清理监控服务
|
||||||
|
router.post('/cleanup', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!monitorStatus.isInitialized) {
|
||||||
|
return res.status(400).json({ error: '监控服务未初始化' });
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupMonitor();
|
||||||
|
monitorStatus = {
|
||||||
|
isInitialized: false,
|
||||||
|
domainId: null,
|
||||||
|
lastError: null
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: '监控服务清理成功',
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('清理监控服务失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '清理监控服务失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取监控服务状态
|
||||||
|
router.get('/status', (req, res) => {
|
||||||
|
res.json(monitorStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
249
XNSimHtml/routes/SystemMonitor.js
Normal file
249
XNSimHtml/routes/SystemMonitor.js
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { startMonitorSystemInfo, stopMonitorSystemInfo, getSystemInfo, getAllThreadInfo } = require('../modules/cleanup');
|
||||||
|
|
||||||
|
// 存储监控服务的状态
|
||||||
|
let monitorStatus = {
|
||||||
|
isMonitoring: false,
|
||||||
|
lastError: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// 存储线程频率历史数据
|
||||||
|
const threadFrequencyHistory = new Map(); // Map<threadId, number[]>
|
||||||
|
const MAX_HISTORY_SIZE = 100;
|
||||||
|
|
||||||
|
// 存储线程频率统计
|
||||||
|
const threadFrequencyStats = new Map(); // Map<threadId, {max: number, min: number, avg: number}>
|
||||||
|
|
||||||
|
// 频率转周期(单位:ms)
|
||||||
|
function frequencyToPeriod(frequency) {
|
||||||
|
return frequency === 0 ? 0 : (1000 / frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新线程频率统计
|
||||||
|
function updateThreadFrequencyStats(threadId, currentFrequency, setFrequency) {
|
||||||
|
// 初始化历史数据
|
||||||
|
if (!threadFrequencyHistory.has(threadId)) {
|
||||||
|
threadFrequencyHistory.set(threadId, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化统计数据
|
||||||
|
if (!threadFrequencyStats.has(threadId)) {
|
||||||
|
threadFrequencyStats.set(threadId, {
|
||||||
|
max: setFrequency,
|
||||||
|
min: setFrequency,
|
||||||
|
avg: setFrequency
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = threadFrequencyHistory.get(threadId);
|
||||||
|
const stats = threadFrequencyStats.get(threadId);
|
||||||
|
|
||||||
|
// 更新最大最小值
|
||||||
|
stats.max = Math.max(stats.max, currentFrequency);
|
||||||
|
stats.min = Math.min(stats.min, currentFrequency);
|
||||||
|
|
||||||
|
// 更新历史数据
|
||||||
|
history.push(currentFrequency);
|
||||||
|
if (history.length > MAX_HISTORY_SIZE) {
|
||||||
|
history.shift(); // 移除最旧的数据
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算平均值
|
||||||
|
stats.avg = history.reduce((sum, freq) => sum + freq, 0) / history.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动系统监控
|
||||||
|
router.post('/start', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '系统监控已经在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = startMonitorSystemInfo();
|
||||||
|
if (result && result.includes('失败')) {
|
||||||
|
monitorStatus.lastError = result;
|
||||||
|
return res.status(500).json({ error: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空历史数据和统计数据
|
||||||
|
threadFrequencyHistory.clear();
|
||||||
|
threadFrequencyStats.clear();
|
||||||
|
|
||||||
|
monitorStatus.isMonitoring = true;
|
||||||
|
monitorStatus.lastError = null;
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: '系统监控启动成功',
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('启动系统监控失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '启动系统监控失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 停止系统监控
|
||||||
|
router.post('/stop', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '系统监控未在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
stopMonitorSystemInfo();
|
||||||
|
monitorStatus.isMonitoring = false;
|
||||||
|
monitorStatus.lastError = null;
|
||||||
|
|
||||||
|
// 清空历史数据和统计数据
|
||||||
|
threadFrequencyHistory.clear();
|
||||||
|
threadFrequencyStats.clear();
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
message: '系统监控停止成功',
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('停止系统监控失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '停止系统监控失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取系统信息
|
||||||
|
router.get('/system-info', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '系统监控未在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = getSystemInfo();
|
||||||
|
if (result && result.includes('失败')) {
|
||||||
|
monitorStatus.lastError = result;
|
||||||
|
return res.status(500).json({ error: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析JSON字符串
|
||||||
|
const data = JSON.parse(result);
|
||||||
|
const engineStatus = data.engineStatus;
|
||||||
|
|
||||||
|
// 构造响应数据
|
||||||
|
const responseData = {
|
||||||
|
engineInfo: {
|
||||||
|
name: engineStatus.engineName,
|
||||||
|
id: engineStatus.engineID,
|
||||||
|
status: engineStatus.engineStatus,
|
||||||
|
affinity: engineStatus.engineAffinity,
|
||||||
|
threadCount: engineStatus.threadCount
|
||||||
|
},
|
||||||
|
coreStatus: {
|
||||||
|
fw: engineStatus.coreStatus.fwStatus,
|
||||||
|
tm: engineStatus.coreStatus.tmStatus,
|
||||||
|
em: engineStatus.coreStatus.emStatus,
|
||||||
|
sd: engineStatus.coreStatus.sdStatus,
|
||||||
|
thm: engineStatus.coreStatus.thmStatus,
|
||||||
|
mm: engineStatus.coreStatus.mmStatus,
|
||||||
|
sm: engineStatus.coreStatus.smStatus,
|
||||||
|
dm: engineStatus.coreStatus.dmStatus
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
data: responseData,
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取系统信息失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '获取系统信息失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取所有线程信息
|
||||||
|
router.get('/thread-info', async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!monitorStatus.isMonitoring) {
|
||||||
|
return res.status(400).json({ error: '系统监控未在运行' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = getAllThreadInfo();
|
||||||
|
if (result && result.includes('失败')) {
|
||||||
|
monitorStatus.lastError = result;
|
||||||
|
return res.status(500).json({ error: result });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析JSON字符串
|
||||||
|
const data = JSON.parse(result);
|
||||||
|
if (!data.threadStatus) {
|
||||||
|
return res.status(500).json({ error: '线程状态数据缺失' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const threadStatus = data.threadStatus;
|
||||||
|
|
||||||
|
// 构造响应数据
|
||||||
|
const responseData = Object.entries(threadStatus).map(([threadId, thread]) => {
|
||||||
|
if (!thread.threadCurrentFrequency || !thread.threadSetFrequency ||
|
||||||
|
!thread.threadName || !thread.threadAffinity ||
|
||||||
|
!thread.threadPro || !thread.threadRunCount ||
|
||||||
|
!thread.threadStatus) {
|
||||||
|
throw new Error('线程数据不完整');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新频率统计
|
||||||
|
updateThreadFrequencyStats(
|
||||||
|
parseInt(threadId),
|
||||||
|
thread.threadCurrentFrequency,
|
||||||
|
thread.threadSetFrequency
|
||||||
|
);
|
||||||
|
|
||||||
|
const stats = threadFrequencyStats.get(parseInt(threadId));
|
||||||
|
if (!stats) {
|
||||||
|
throw new Error('线程频率统计数据缺失');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算周期值
|
||||||
|
const currentPeriod = frequencyToPeriod(thread.threadCurrentFrequency);
|
||||||
|
const setPeriod = frequencyToPeriod(thread.threadSetFrequency);
|
||||||
|
const maxPeriod = frequencyToPeriod(stats.min);
|
||||||
|
const minPeriod = frequencyToPeriod(stats.max);
|
||||||
|
const avgPeriod = frequencyToPeriod(stats.avg);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: parseInt(threadId),
|
||||||
|
name: thread.threadName,
|
||||||
|
affinity: thread.threadAffinity,
|
||||||
|
priority: thread.threadPro,
|
||||||
|
runCount: thread.threadRunCount,
|
||||||
|
status: thread.threadStatus,
|
||||||
|
// 频率值
|
||||||
|
currentFrequency: thread.threadCurrentFrequency,
|
||||||
|
setFrequency: thread.threadSetFrequency,
|
||||||
|
maxFrequency: stats.max,
|
||||||
|
minFrequency: stats.min,
|
||||||
|
avgFrequency: stats.avg,
|
||||||
|
// 周期值(单位:ms)
|
||||||
|
currentPeriod: currentPeriod,
|
||||||
|
setPeriod: setPeriod,
|
||||||
|
maxPeriod: maxPeriod,
|
||||||
|
minPeriod: minPeriod,
|
||||||
|
avgPeriod: avgPeriod
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
data: responseData,
|
||||||
|
status: monitorStatus
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取线程信息失败:', error);
|
||||||
|
monitorStatus.lastError = error.message;
|
||||||
|
res.status(500).json({ error: '获取线程信息失败', message: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取监控状态
|
||||||
|
router.get('/status', (req, res) => {
|
||||||
|
res.json(monitorStatus);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
@ -27,6 +27,8 @@ const qaRoutes = require('./routes/qa');
|
|||||||
const todoRoutes = require('./routes/todos');
|
const todoRoutes = require('./routes/todos');
|
||||||
const userRoutes = require('./routes/users');
|
const userRoutes = require('./routes/users');
|
||||||
const systemLogRoutes = require('./routes/system-log');
|
const systemLogRoutes = require('./routes/system-log');
|
||||||
|
const ddsMonitorRoutes = require('./routes/DDSMonitor');
|
||||||
|
const systemMonitorRoutes = require('./routes/SystemMonitor');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
@ -95,6 +97,8 @@ app.use('/api/qa', qaRoutes);
|
|||||||
app.use('/api/todos', todoRoutes);
|
app.use('/api/todos', todoRoutes);
|
||||||
app.use('/api', userRoutes);
|
app.use('/api', userRoutes);
|
||||||
app.use('/api/system-log', systemLogRoutes);
|
app.use('/api/system-log', systemLogRoutes);
|
||||||
|
app.use('/api/dds-monitor', ddsMonitorRoutes);
|
||||||
|
app.use('/api/system-monitor', systemMonitorRoutes);
|
||||||
|
|
||||||
// 接口配置页面路由
|
// 接口配置页面路由
|
||||||
app.get('/interface-config', (req, res) => {
|
app.get('/interface-config', (req, res) => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user