Compare commits
5 Commits
Win-x86支持分
...
main
Author | SHA1 | Date | |
---|---|---|---|
d53274288a | |||
4743606222 | |||
b80f86f197 | |||
527e455e4b | |||
8c8f3a09f9 |
Binary file not shown.
@ -5,62 +5,96 @@
|
||||
#include "DDSInterfaceGen.h"
|
||||
#include "CMakeListsGen.h"
|
||||
|
||||
int XNInterfaceGen(const char *tableName, const int tableNameSize, const char *configName,
|
||||
const int configNameSize, const char *errorMsg, const int errorMsgSize)
|
||||
// 全局变量定义
|
||||
std::string g_tableName;
|
||||
std::string g_configName;
|
||||
char *g_errorMsg;
|
||||
int g_errorMsgSize;
|
||||
AllInterfaceData g_interfaceData;
|
||||
|
||||
bool XNInterfaceGen_Step1_InitParams(const char *tableName, const int tableNameSize,
|
||||
const char *configName, const int configNameSize,
|
||||
const char *errorMsg, const int errorMsgSize)
|
||||
{
|
||||
g_tableName = std::string(tableName, tableNameSize);
|
||||
g_configName = std::string(configName, configNameSize);
|
||||
g_errorMsg = const_cast<char *>(errorMsg);
|
||||
g_errorMsgSize = errorMsgSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XNInterfaceGen_Step2_GetInterfaceData()
|
||||
{
|
||||
std::string errorMsgStr;
|
||||
// 1. 从数据库获取接口数据
|
||||
AllInterfaceData interfaceData = GetInterfaceData::getInterfaceData(tableName, errorMsgStr);
|
||||
g_interfaceData = GetInterfaceData::getInterfaceData(g_tableName, errorMsgStr);
|
||||
if (!errorMsgStr.empty()) {
|
||||
memcpy((void *)errorMsg, errorMsgStr.c_str(), errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, errorMsgStr.c_str(), g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 创建构型目录
|
||||
bool ret = GenIDL::createConfigDirectory(configName);
|
||||
bool XNInterfaceGen_Step3_CreateConfigDir()
|
||||
{
|
||||
bool ret = GenIDL::createConfigDirectory(g_configName);
|
||||
if (!ret) {
|
||||
memcpy((void *)errorMsg, "Create config directory failed", errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, "Create config directory failed", g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. 生成IDL文件
|
||||
ret = GenIDL::generateIDL(interfaceData);
|
||||
bool XNInterfaceGen_Step4_GenerateIDL()
|
||||
{
|
||||
bool ret = GenIDL::generateIDL(g_interfaceData);
|
||||
if (!ret) {
|
||||
memcpy((void *)errorMsg, "Generate IDL failed", errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, "Generate IDL failed", g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 4. 生成FastDDS代码
|
||||
ret = FastDDSGen::generateFastDDSCode(GenIDL::getIDLFilePath());
|
||||
bool XNInterfaceGen_Step5_GenerateFastDDS()
|
||||
{
|
||||
bool ret = FastDDSGen::generateFastDDSCode(GenIDL::getIDLFilePath());
|
||||
if (!ret) {
|
||||
memcpy((void *)errorMsg, "Generate FastDDS code failed", errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, "Generate FastDDS code failed", g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5. 生成DDS接口
|
||||
bool XNInterfaceGen_Step6_GenerateDDSInterface()
|
||||
{
|
||||
DDSInterfaceGen ddsInterfaceGen(GenIDL::getIDLFilePath());
|
||||
ret = ddsInterfaceGen.generateDDSInterface(interfaceData);
|
||||
bool ret = ddsInterfaceGen.generateDDSInterface(g_interfaceData);
|
||||
if (!ret) {
|
||||
memcpy((void *)errorMsg, "Generate DDS interface failed", errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, "Generate DDS interface failed", g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 6. 生成CMakeLists.txt文件
|
||||
ret = CMakeListsGen::generateCMakeLists(interfaceData, GenIDL::getIDLFilePath(), configName);
|
||||
bool XNInterfaceGen_Step7_GenerateCMakeLists()
|
||||
{
|
||||
bool ret =
|
||||
CMakeListsGen::generateCMakeLists(g_interfaceData, GenIDL::getIDLFilePath(), g_configName);
|
||||
if (!ret) {
|
||||
memcpy((void *)errorMsg, "Generate CMakeLists.txt failed", errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, "Generate CMakeLists.txt failed", g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 7. 执行CMake编译和安装
|
||||
bool XNInterfaceGen_Step8_BuildAndInstall()
|
||||
{
|
||||
std::string idlDirPath = fs::path(GenIDL::getIDLFilePath()).parent_path().string();
|
||||
std::string buildCmd =
|
||||
"cd " + idlDirPath + " && mkdir -p build && cd build && cmake .. && make && make install";
|
||||
int buildRet = system(buildCmd.c_str());
|
||||
if (buildRet != 0) {
|
||||
memcpy((void *)errorMsg, "CMake build or install failed", errorMsgSize);
|
||||
return -1;
|
||||
memcpy(g_errorMsg, "CMake build or install failed", g_errorMsgSize);
|
||||
return false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
@ -2,9 +2,26 @@
|
||||
#define XN_INTERFACE_GEN_SERVER_H
|
||||
|
||||
#include "XNInterfaceGenServer_global.h"
|
||||
#include "PublicDefine.h"
|
||||
|
||||
extern "C" XNIGS_EXPORT int XNInterfaceGen(const char *tableName, const int tableNameSize,
|
||||
const char *configName, const int configNameSize,
|
||||
const char *errorMsg, const int errorMsgSize);
|
||||
// 全局变量声明
|
||||
extern std::string g_tableName;
|
||||
extern std::string g_configName;
|
||||
extern char *g_errorMsg;
|
||||
extern int g_errorMsgSize;
|
||||
extern AllInterfaceData g_interfaceData;
|
||||
|
||||
// 步骤函数声明
|
||||
extern "C" XNIGS_EXPORT bool
|
||||
XNInterfaceGen_Step1_InitParams(const char *tableName, const int tableNameSize,
|
||||
const char *configName, const int configNameSize,
|
||||
const char *errorMsg, const int errorMsgSize);
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step2_GetInterfaceData();
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step3_CreateConfigDir();
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step4_GenerateIDL();
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step5_GenerateFastDDS();
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step6_GenerateDDSInterface();
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step7_GenerateCMakeLists();
|
||||
extern "C" XNIGS_EXPORT bool XNInterfaceGen_Step8_BuildAndInstall();
|
||||
|
||||
#endif // XN_INTERFACE_GEN_SERVER_H
|
||||
|
@ -145,6 +145,15 @@ public:
|
||||
void unregisterPublisher(const std::string &topicName)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||
unregisterPublisherWithoutLock(topicName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销发布者, 不带锁
|
||||
* @param topicName: 主题名称
|
||||
*/
|
||||
void unregisterPublisherWithoutLock(const std::string &topicName)
|
||||
{
|
||||
auto it = topics_.find(topicName);
|
||||
if (it != topics_.end()) {
|
||||
MonitorTopicInfo &topicInfo = it->second; // 获取主题信息
|
||||
@ -221,12 +230,21 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销订阅者
|
||||
* @brief 注销订阅者, 带锁
|
||||
* @param topicName: 主题名称
|
||||
*/
|
||||
void unregisterSubscriber(const std::string &topicName)
|
||||
{
|
||||
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||
unregisterSubscriberWithoutLock(topicName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销订阅者, 不带锁
|
||||
* @param topicName: 主题名称
|
||||
*/
|
||||
void unregisterSubscriberWithoutLock(const std::string &topicName)
|
||||
{
|
||||
auto it = topics_.find(topicName);
|
||||
if (it != topics_.end()) {
|
||||
MonitorTopicInfo &topicInfo = it->second; // 获取主题信息
|
||||
@ -260,8 +278,8 @@ private:
|
||||
std::lock_guard<std::mutex> locker(m_Mutex);
|
||||
if (m_Participant != nullptr) {
|
||||
while (!topics_.empty()) {
|
||||
unregisterPublisher(topics_.begin()->first); // 注销发布者
|
||||
unregisterSubscriber(topics_.begin()->first); // 注销订阅者
|
||||
unregisterPublisherWithoutLock(topics_.begin()->first); // 注销发布者
|
||||
unregisterSubscriberWithoutLock(topics_.begin()->first); // 注销订阅者
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,9 +28,11 @@ class DataMonitor extends HTMLElement {
|
||||
this._resizeEventBinded = false;
|
||||
this.monitorId = `data_monitor_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // 添加监控器ID
|
||||
this.dataUpdateTimer = null; // 数据更新定时器
|
||||
this.isActive = false; // 组件是否激活
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.isActive = true; // 设置初始状态为激活
|
||||
this.loadInterfaces();
|
||||
// 延迟多次尝试,直到拿到有效宽度
|
||||
const tryInitColWidth = (tryCount = 0) => {
|
||||
@ -71,6 +73,57 @@ class DataMonitor extends HTMLElement {
|
||||
});
|
||||
this._resizeEventBinded = true;
|
||||
}
|
||||
|
||||
// 等待组件完全加载后初始化
|
||||
setTimeout(() => {
|
||||
this.initializeComponent();
|
||||
// 初始化完成后再启动定时器,给服务器一些准备时间
|
||||
setTimeout(() => {
|
||||
this.startDataUpdateTimer();
|
||||
}, 1000); // 延迟1秒启动定时器
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 重新激活组件
|
||||
reactivate() {
|
||||
if (this.isActive) return; // 如果已经激活,直接返回
|
||||
this.isActive = true;
|
||||
this.initializeComponent();
|
||||
// 重新启动定时器
|
||||
this.startDataUpdateTimer();
|
||||
}
|
||||
|
||||
async initializeComponent() {
|
||||
try {
|
||||
// 更新所有行的监控状态
|
||||
this.tableData.forEach(row => {
|
||||
row.isMonitoring = true;
|
||||
});
|
||||
|
||||
// 初始状态设置为未监控
|
||||
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
|
||||
const statusText = this.shadowRoot.getElementById('statusText');
|
||||
if (statusIndicator) {
|
||||
statusIndicator.classList.remove('active', 'error');
|
||||
statusIndicator.classList.add('inactive');
|
||||
}
|
||||
if (statusText) {
|
||||
statusText.textContent = '未监控';
|
||||
}
|
||||
|
||||
this.renderTable();
|
||||
} catch (error) {
|
||||
console.error('初始化组件失败:', error);
|
||||
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
|
||||
const statusText = this.shadowRoot.getElementById('statusText');
|
||||
if (statusIndicator) {
|
||||
statusIndicator.classList.remove('active', 'inactive');
|
||||
statusIndicator.classList.add('error');
|
||||
}
|
||||
if (statusText) {
|
||||
statusText.textContent = '监控错误';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,51 +232,6 @@ class DataMonitor extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 初始化DDS监控服务
|
||||
* @returns {Promise<boolean>} 初始化是否成功
|
||||
*/
|
||||
async initializeDDSMonitor() {
|
||||
try {
|
||||
// 获取当前选择的配置
|
||||
const selection = this.getCurrentSelection();
|
||||
const { domainId } = selection;
|
||||
if (!domainId) {
|
||||
throw new Error('未找到有效的域ID,请确保已选择构型并等待构型加载完成');
|
||||
}
|
||||
|
||||
// 检查DDS监控状态
|
||||
const statusResponse = await fetch('/api/dds-monitor/status');
|
||||
if (!statusResponse.ok) {
|
||||
throw new Error(`获取DDS监控状态失败: ${statusResponse.status} ${statusResponse.statusText}`);
|
||||
}
|
||||
const statusData = await statusResponse.json();
|
||||
|
||||
// 如果未初始化,则初始化
|
||||
if (!statusData.isInitialized) {
|
||||
const initResponse = await fetch('/api/dds-monitor/initialize', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
domainId,
|
||||
monitorId: this.monitorId
|
||||
})
|
||||
});
|
||||
|
||||
if (!initResponse.ok) {
|
||||
const errorData = await initResponse.json();
|
||||
throw new Error(`初始化DDS监控失败: ${errorData.error || initResponse.statusText}`);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 按结构体名称分组获取接口数据
|
||||
* @returns {Object} 按结构体名称分组的接口数据
|
||||
@ -279,6 +287,43 @@ class DataMonitor extends HTMLElement {
|
||||
|
||||
this.dataUpdateTimer = setInterval(async () => {
|
||||
try {
|
||||
// 检查DDS监控状态
|
||||
const statusResponse = await fetch('/api/dds-monitor/status');
|
||||
if (!statusResponse.ok) {
|
||||
throw new Error(`获取DDS监控状态失败: ${statusResponse.status} ${statusResponse.statusText}`);
|
||||
}
|
||||
const statusData = await statusResponse.json();
|
||||
|
||||
// 更新状态显示
|
||||
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
|
||||
const statusText = this.shadowRoot.getElementById('statusText');
|
||||
|
||||
if (!statusData.isInitialized) {
|
||||
if (statusIndicator) {
|
||||
statusIndicator.classList.remove('active');
|
||||
statusIndicator.classList.add('inactive');
|
||||
}
|
||||
if (statusText) {
|
||||
statusText.textContent = '未监控';
|
||||
}
|
||||
// 更新表格中所有数据的监控状态
|
||||
this.tableData.forEach(row => {
|
||||
row.isMonitoring = false;
|
||||
row.monitorData = '';
|
||||
});
|
||||
this.renderTable();
|
||||
return; // 未初始化时等待下一次循环
|
||||
}
|
||||
|
||||
// DDS已初始化,更新状态显示
|
||||
if (statusIndicator) {
|
||||
statusIndicator.classList.remove('inactive', 'error');
|
||||
statusIndicator.classList.add('active');
|
||||
}
|
||||
if (statusText) {
|
||||
statusText.textContent = '监控中';
|
||||
}
|
||||
|
||||
const groupedInterfaces = this.getGroupedInterfaces();
|
||||
|
||||
// 对每个结构体启动监控并获取数据
|
||||
@ -610,6 +655,10 @@ class DataMonitor extends HTMLElement {
|
||||
background: #ff4d4f;
|
||||
}
|
||||
|
||||
.status-indicator.inactive {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
|
||||
.global-monitor-btn {
|
||||
padding: 6px 16px;
|
||||
border: none;
|
||||
@ -926,9 +975,6 @@ class DataMonitor extends HTMLElement {
|
||||
</style>
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<button class="global-monitor-btn" id="globalMonitorBtn">开始监控</button>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<div class="monitor-status">
|
||||
<div class="status-indicator" id="statusIndicator"></div>
|
||||
<span id="statusText">未监控</span>
|
||||
@ -1086,62 +1132,10 @@ class DataMonitor extends HTMLElement {
|
||||
document.body.style.userSelect = '';
|
||||
}
|
||||
};
|
||||
|
||||
// 绑定全局监控按钮事件
|
||||
const globalMonitorBtn = this.shadowRoot.getElementById('globalMonitorBtn');
|
||||
const statusIndicator = this.shadowRoot.getElementById('statusIndicator');
|
||||
const statusText = this.shadowRoot.getElementById('statusText');
|
||||
|
||||
globalMonitorBtn.addEventListener('click', async () => {
|
||||
try {
|
||||
if (!globalMonitorBtn.classList.contains('monitoring')) {
|
||||
// 开始监控
|
||||
const ddsInitialized = await this.initializeDDSMonitor();
|
||||
if (!ddsInitialized) {
|
||||
throw new Error('DDS监控初始化失败');
|
||||
}
|
||||
|
||||
// 更新所有行的监控状态
|
||||
this.tableData.forEach(row => {
|
||||
row.isMonitoring = true;
|
||||
});
|
||||
|
||||
// 启动数据更新定时器
|
||||
this.startDataUpdateTimer();
|
||||
|
||||
// 更新状态
|
||||
globalMonitorBtn.textContent = '停止监控';
|
||||
globalMonitorBtn.classList.add('monitoring');
|
||||
statusIndicator.classList.add('active');
|
||||
statusText.textContent = '监控中';
|
||||
this.renderTable();
|
||||
} else {
|
||||
// 停止监控
|
||||
// 停止数据更新定时器
|
||||
await this.stopDataUpdateTimer();
|
||||
|
||||
// 更新所有行的监控状态
|
||||
this.tableData.forEach(row => {
|
||||
row.isMonitoring = false;
|
||||
row.monitorData = ''; // 清空监控数据
|
||||
});
|
||||
|
||||
// 更新状态
|
||||
globalMonitorBtn.textContent = '开始监控';
|
||||
globalMonitorBtn.classList.remove('monitoring');
|
||||
statusIndicator.classList.remove('active');
|
||||
statusText.textContent = '未监控';
|
||||
this.renderTable();
|
||||
}
|
||||
} catch (error) {
|
||||
statusIndicator.classList.add('error');
|
||||
statusText.textContent = '监控错误';
|
||||
alert(error.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.isActive = false;
|
||||
// 组件销毁时清理定时器
|
||||
this.stopDataUpdateTimer();
|
||||
}
|
||||
|
@ -1394,12 +1394,56 @@ class ModelDevelopment extends HTMLElement {
|
||||
const selectedYear = parseInt(calendarYear.value);
|
||||
const selectedMonth = parseInt(calendarMonth.value);
|
||||
|
||||
// 关闭当前对话框
|
||||
closeModal();
|
||||
|
||||
// 更新日期参数并重新显示对话框
|
||||
// 更新当前日期
|
||||
currentDate = new Date(selectedYear, selectedMonth, 1);
|
||||
this.showDateTimeDialog(inputElement);
|
||||
|
||||
// 重新生成日历内容
|
||||
const daysInMonth = new Date(selectedYear, selectedMonth + 1, 0).getDate();
|
||||
const firstDay = new Date(selectedYear, selectedMonth, 1).getDay();
|
||||
|
||||
let calendarRows = '';
|
||||
let dayCount = 1;
|
||||
|
||||
// 添加表头
|
||||
calendarRows += '<tr>';
|
||||
['日', '一', '二', '三', '四', '五', '六'].forEach(dayName => {
|
||||
calendarRows += `<th>${dayName}</th>`;
|
||||
});
|
||||
calendarRows += '</tr>';
|
||||
|
||||
// 计算行数
|
||||
const totalCells = firstDay + daysInMonth;
|
||||
const rowCount = Math.ceil(totalCells / 7);
|
||||
|
||||
// 添加日期行
|
||||
for (let i = 0; i < rowCount; i++) {
|
||||
calendarRows += '<tr>';
|
||||
for (let j = 0; j < 7; j++) {
|
||||
if ((i === 0 && j < firstDay) || dayCount > daysInMonth) {
|
||||
calendarRows += '<td></td>';
|
||||
} else {
|
||||
const isToday = dayCount === currentDate.getDate();
|
||||
calendarRows += `<td class="calendar-day ${isToday ? 'selected' : ''}" data-day="${dayCount}">${dayCount}</td>`;
|
||||
dayCount++;
|
||||
}
|
||||
}
|
||||
calendarRows += '</tr>';
|
||||
}
|
||||
|
||||
// 更新日历表格内容
|
||||
const calendarTable = modal.querySelector('.calendar-table');
|
||||
calendarTable.innerHTML = calendarRows;
|
||||
|
||||
// 重新绑定日期选择事件
|
||||
const calendarDays = modal.querySelectorAll('.calendar-day');
|
||||
calendarDays.forEach(cell => {
|
||||
cell.addEventListener('click', (e) => {
|
||||
// 移除所有选中状态
|
||||
calendarDays.forEach(day => day.classList.remove('selected'));
|
||||
// 添加新选中状态
|
||||
e.target.classList.add('selected');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
calendarMonth.addEventListener('change', updateCalendar);
|
||||
|
@ -13,18 +13,27 @@ class ModelMonitor extends HTMLElement {
|
||||
this.chartInitialized = false;
|
||||
this.domainId = '10'; // 默认值
|
||||
this.isActive = false;
|
||||
this.initializing = false;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
this.isActive = true; // 设置初始状态为激活
|
||||
// 等待组件完全加载
|
||||
|
||||
// 等待组件完全加载后初始化
|
||||
setTimeout(() => {
|
||||
this.initializeComponent();
|
||||
// 初始化完成后再启动定时器,给服务器一些准备时间
|
||||
setTimeout(() => {
|
||||
this.startStatusCheck();
|
||||
}, 1000); // 延迟1秒启动定时器
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async initializeComponent() {
|
||||
if (this.initializing) return; // 防止重复初始化
|
||||
this.initializing = true;
|
||||
|
||||
try {
|
||||
// 确保图表被正确初始化
|
||||
if (this.chart) {
|
||||
@ -40,12 +49,9 @@ class ModelMonitor extends HTMLElement {
|
||||
const statusData = await statusResponse.json();
|
||||
this.monitorStatus = statusData;
|
||||
|
||||
// 如果已经在监控中,立即开始获取数据
|
||||
if (this.monitorStatus.isMonitoring) {
|
||||
// 先获取一次数据
|
||||
await this.checkMonitorStatus();
|
||||
// 然后开始定时检查
|
||||
this.startStatusCheck();
|
||||
// 如果监控未运行,尝试启动监控
|
||||
if (!this.monitorStatus.isMonitoring) {
|
||||
this.startMonitoring();
|
||||
}
|
||||
|
||||
this.chartInitialized = true;
|
||||
@ -53,6 +59,8 @@ class ModelMonitor extends HTMLElement {
|
||||
console.error('初始化组件失败:', error);
|
||||
this.monitorStatus.lastError = error.message;
|
||||
this.updateUI();
|
||||
} finally {
|
||||
this.initializing = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +104,7 @@ class ModelMonitor extends HTMLElement {
|
||||
};
|
||||
|
||||
// 立即执行一次
|
||||
checkStatus();
|
||||
//checkStatus();
|
||||
|
||||
// 设置定时器,每秒执行一次
|
||||
this.statusCheckInterval = setInterval(checkStatus, 1000);
|
||||
@ -116,59 +124,35 @@ class ModelMonitor extends HTMLElement {
|
||||
const selection = savedSelection ? JSON.parse(savedSelection) : {};
|
||||
const confID = selection.configurationId;
|
||||
|
||||
// 如果已经在监控中,直接返回
|
||||
if (this.monitorStatus.isMonitoring) {
|
||||
console.log('监控已经在运行中');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取构型参数
|
||||
const configResponse = await fetch(`/api/configurations/${confID}`);
|
||||
if (!configResponse.ok) {
|
||||
throw new Error('获取构型参数失败');
|
||||
}
|
||||
const configData = await configResponse.json();
|
||||
|
||||
// 从构型参数中提取域ID
|
||||
const domainId = configData.DomainID;
|
||||
if (!domainId) {
|
||||
throw new Error('构型参数中未找到有效的域ID');
|
||||
}
|
||||
this.domainId = domainId;
|
||||
|
||||
// 首先检查DDS监控状态
|
||||
const ddsStatusResponse = await fetch('/api/dds-monitor/status');
|
||||
const ddsStatusData = await ddsStatusResponse.json();
|
||||
|
||||
// 如果DDS监控未初始化,先初始化DDS监控
|
||||
// 如果DDS监控未初始化,直接返回,等待下次定时器运行时再检查
|
||||
if (!ddsStatusData.isInitialized) {
|
||||
const ddsInitResponse = await fetch('/api/dds-monitor/initialize', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
domainId,
|
||||
monitorId: this.monitorId
|
||||
})
|
||||
});
|
||||
|
||||
if (!ddsInitResponse.ok) {
|
||||
const errorData = await ddsInitResponse.json();
|
||||
console.error('DDS监控初始化失败:', errorData.error);
|
||||
return;
|
||||
}
|
||||
//console.log('DDS监控未初始化,等待下次检查');
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动模型监控
|
||||
const response = await fetch('/api/model-monitor/start', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ domainId })
|
||||
method: 'POST'
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
this.monitorStatus = data.status;
|
||||
this.updateUI();
|
||||
this.startStatusCheck(); // 直接启动状态检查,不需要检查 isActive
|
||||
// 只有在没有运行中的状态检查时才启动新的检查
|
||||
if (!this.statusCheckInterval) {
|
||||
this.startStatusCheck();
|
||||
}
|
||||
} else {
|
||||
console.error('启动监控失败:', data.error);
|
||||
}
|
||||
@ -180,17 +164,8 @@ class ModelMonitor extends HTMLElement {
|
||||
disconnectedCallback() {
|
||||
this.isActive = false;
|
||||
this.stopStatusCheck();
|
||||
// 注销监控器
|
||||
fetch('/api/dds-monitor/unregister', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ monitorId: this.monitorId })
|
||||
}).catch(error => {
|
||||
console.error('注销监控器失败:', error);
|
||||
});
|
||||
// 不要在这里销毁图表实例,让它在重新激活时重新创建
|
||||
// 自动停止监控
|
||||
this.stopMonitoring();
|
||||
}
|
||||
|
||||
async checkMonitorStatus() {
|
||||
@ -200,13 +175,11 @@ class ModelMonitor extends HTMLElement {
|
||||
const statusData = await statusResponse.json();
|
||||
this.monitorStatus = statusData;
|
||||
|
||||
// 只有在监控状态为true时才获取详细信息
|
||||
if (this.monitorStatus.isMonitoring) {
|
||||
try {
|
||||
// 获取模型信息
|
||||
const modelResponse = await fetch('/api/model-monitor/model-info');
|
||||
if (!modelResponse.ok) {
|
||||
throw new Error(`模型信息获取失败: ${modelResponse.status}`);
|
||||
}
|
||||
const modelData = await modelResponse.json();
|
||||
|
||||
if (!modelResponse.ok) {
|
||||
@ -231,12 +204,8 @@ class ModelMonitor extends HTMLElement {
|
||||
// 保持原有数据不变
|
||||
}
|
||||
} else {
|
||||
// 只有在状态发生变化时才更新
|
||||
if (this.modelInfo !== null) {
|
||||
this.modelInfo = null;
|
||||
this.monitorStatus.lastError = null;
|
||||
this.updateUI();
|
||||
}
|
||||
// 如果监控未运行,尝试启动监控
|
||||
this.startMonitoring();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取监控状态失败:', error);
|
||||
@ -261,23 +230,24 @@ class ModelMonitor extends HTMLElement {
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
const startButton = this.shadowRoot.querySelector('.start-button');
|
||||
const stopButton = this.shadowRoot.querySelector('.stop-button');
|
||||
const statusDisplay = this.shadowRoot.querySelector('.status-display');
|
||||
const statusIndicator = this.shadowRoot.querySelector('#statusIndicator');
|
||||
const statusText = this.shadowRoot.querySelector('#statusText');
|
||||
const modelTableBody = this.shadowRoot.querySelector('#model-table-body');
|
||||
|
||||
if (this.monitorStatus.isMonitoring) {
|
||||
startButton.disabled = true;
|
||||
stopButton.disabled = false;
|
||||
} else {
|
||||
startButton.disabled = false;
|
||||
stopButton.disabled = true;
|
||||
}
|
||||
|
||||
// 更新状态显示
|
||||
statusDisplay.textContent = `监控状态: ${this.monitorStatus.isMonitoring ? '运行中' : '已停止'}`;
|
||||
if (this.monitorStatus.lastError) {
|
||||
statusDisplay.textContent += ` (错误: ${this.monitorStatus.lastError})`;
|
||||
if (statusIndicator && statusText) {
|
||||
if (this.monitorStatus.isMonitoring) {
|
||||
statusIndicator.classList.add('active');
|
||||
statusIndicator.classList.remove('error');
|
||||
statusText.textContent = '监控中';
|
||||
} else if (this.monitorStatus.lastError) {
|
||||
statusIndicator.classList.remove('active');
|
||||
statusIndicator.classList.add('error');
|
||||
statusText.textContent = `监控错误: ${this.monitorStatus.lastError}`;
|
||||
} else {
|
||||
statusIndicator.classList.remove('active', 'error');
|
||||
statusText.textContent = '未监控';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新模型表格
|
||||
@ -320,12 +290,28 @@ class ModelMonitor extends HTMLElement {
|
||||
|
||||
async stopMonitoring() {
|
||||
try {
|
||||
// 首先检查监控状态
|
||||
const statusResponse = await fetch('/api/model-monitor/status');
|
||||
const statusData = await statusResponse.json();
|
||||
|
||||
// 如果监控未运行,直接返回
|
||||
if (!statusData.isMonitoring) {
|
||||
//console.log('监控未运行,无需关闭');
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行关闭操作
|
||||
const response = await fetch('/api/model-monitor/stop', {
|
||||
method: 'POST'
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
this.monitorStatus = data.status;
|
||||
// 立即停止状态检查
|
||||
this.stopStatusCheck();
|
||||
// 清空数据
|
||||
this.modelInfo = null;
|
||||
// 更新UI
|
||||
this.updateUI();
|
||||
} else {
|
||||
console.error('停止监控失败:', data.error);
|
||||
@ -354,11 +340,44 @@ class ModelMonitor extends HTMLElement {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar-section {
|
||||
background-color: white;
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.monitor-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.status-indicator.active {
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.status-indicator.error {
|
||||
background: #ff4d4f;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
@ -375,63 +394,6 @@ class ModelMonitor extends HTMLElement {
|
||||
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;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.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;
|
||||
@ -536,13 +498,12 @@ class ModelMonitor extends HTMLElement {
|
||||
}
|
||||
</style>
|
||||
<div class="monitor-container">
|
||||
<div class="toolbar-section">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<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 class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<div class="monitor-status">
|
||||
<div class="status-indicator" id="statusIndicator"></div>
|
||||
<span id="statusText">未监控</span>
|
||||
</div>
|
||||
<div class="status-display">监控状态: 未启动</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-container">
|
||||
@ -582,7 +543,7 @@ class ModelMonitor extends HTMLElement {
|
||||
<input type="checkbox" id="display-type-switch">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span id="display-type-label">频率</span>
|
||||
<span id="display-type-label">周期</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -43,7 +43,7 @@ class OverviewPage extends HTMLElement {
|
||||
const verNum = version.verNum;
|
||||
const title = version.title;
|
||||
|
||||
return `【${date} ${time}】v${verNum} ${title}`;
|
||||
return `<span class="version-date">【${date} ${time}】</span><span class="version-number">v${verNum}</span><span class="version-title">${title}</span>`;
|
||||
}
|
||||
|
||||
startClock() {
|
||||
@ -519,6 +519,28 @@ class OverviewPage extends HTMLElement {
|
||||
line-height: 1;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.update-list li {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
color: #2c3e50;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.version-date {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version-number {
|
||||
min-width: 60px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version-title {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
<div class="overview-container">
|
||||
<div class="welcome-card">
|
||||
|
@ -315,6 +315,45 @@ class RunSim extends HTMLElement {
|
||||
|
||||
// 使用进程ID作为仿真ID
|
||||
this.currentSimulationId = data.pid.toString();
|
||||
|
||||
// 获取构型ID
|
||||
const savedSelection = localStorage.getItem('xnsim-selection');
|
||||
const selection = savedSelection ? JSON.parse(savedSelection) : {};
|
||||
const confID = selection.configurationId;
|
||||
|
||||
// 获取构型参数
|
||||
const configResponse = await fetch(`/api/configurations/${confID}`);
|
||||
if (!configResponse.ok) {
|
||||
throw new Error('获取构型参数失败');
|
||||
}
|
||||
const configData = await configResponse.json();
|
||||
|
||||
// 从构型参数中提取域ID
|
||||
const domainId = configData.DomainID;
|
||||
if (!domainId) {
|
||||
throw new Error('构型参数中未找到有效的域ID');
|
||||
}
|
||||
|
||||
// 初始化DDS监控
|
||||
const ddsInitResponse = await fetch('/api/dds-monitor/initialize', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
domainId: domainId,
|
||||
})
|
||||
});
|
||||
|
||||
if (!ddsInitResponse.ok) {
|
||||
console.warn('初始化DDS监控失败,但继续连接仿真');
|
||||
} else {
|
||||
const ddsInitResult = await ddsInitResponse.json();
|
||||
if (ddsInitResult.error) {
|
||||
console.warn('初始化DDS监控出错:', ddsInitResult.error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 清空并初始化输出框
|
||||
const outputContent = this.shadowRoot.querySelector('#output-content');
|
||||
@ -524,16 +563,13 @@ class RunSim extends HTMLElement {
|
||||
throw new Error('构型参数中未找到有效的域ID');
|
||||
}
|
||||
|
||||
// 生成唯一的监控器ID
|
||||
const monitorId = `sim_${Date.now()}`;
|
||||
|
||||
// 初始化DDS监控
|
||||
const ddsInitResponse = await fetch('/api/dds-monitor/initialize', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ domainId, monitorId })
|
||||
body: JSON.stringify({ domainId })
|
||||
});
|
||||
|
||||
if (!ddsInitResponse.ok) {
|
||||
@ -624,16 +660,42 @@ class RunSim extends HTMLElement {
|
||||
|
||||
// 在组件销毁时清理资源
|
||||
disconnectedCallback() {
|
||||
// 清理日志文件轮询
|
||||
if (this.logFilePollingInterval) {
|
||||
clearInterval(this.logFilePollingInterval);
|
||||
this.logFilePollingInterval = null;
|
||||
}
|
||||
|
||||
// 清理 SSE 连接
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
this.eventSource = null;
|
||||
}
|
||||
|
||||
// 清理其他资源
|
||||
this.currentSimulationId = null;
|
||||
this.reconnectAttempts = 0;
|
||||
this.isPaused = false;
|
||||
this.modelGroups = [];
|
||||
this.services = [];
|
||||
|
||||
// 清理 DOM 事件监听器
|
||||
const runButton = this.shadowRoot.querySelector('#run-button');
|
||||
const runSimulationButton = this.shadowRoot.querySelector('#run-simulation-button');
|
||||
const pauseSimulationButton = this.shadowRoot.querySelector('#pause-simulation-button');
|
||||
const stopSimulationButton = this.shadowRoot.querySelector('#stop-simulation-button');
|
||||
|
||||
if (runButton) runButton.removeEventListener('click', () => this.runTest());
|
||||
if (runSimulationButton) runSimulationButton.removeEventListener('click', () => this.runSimulation());
|
||||
if (pauseSimulationButton) pauseSimulationButton.removeEventListener('click', () => this.pauseSimulation());
|
||||
if (stopSimulationButton) stopSimulationButton.removeEventListener('click', () => this.stopSimulation());
|
||||
|
||||
// 清理 Shadow DOM
|
||||
if (this.shadowRoot) {
|
||||
this.shadowRoot.innerHTML = '';
|
||||
}
|
||||
|
||||
// 注意:不在这里关闭 DDS 监控,因为其他组件可能正在使用它
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
@ -1136,23 +1198,8 @@ class RunSim extends HTMLElement {
|
||||
throw new Error(result.message || '停止引擎失败');
|
||||
}
|
||||
|
||||
// 等待5秒
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
// 调用老接口确保完全停止
|
||||
const fallbackResponse = await fetch('/api/stop-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: simulationId
|
||||
})
|
||||
});
|
||||
|
||||
if (!fallbackResponse.ok) {
|
||||
console.warn('调用老接口停止仿真失败,但引擎已停止');
|
||||
}
|
||||
// 等待1秒确保引擎完全停止
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 关闭SSE连接
|
||||
if (this.eventSource) {
|
||||
@ -1160,13 +1207,44 @@ class RunSim extends HTMLElement {
|
||||
this.eventSource = null;
|
||||
}
|
||||
|
||||
// 调用停止仿真接口清理数据库记录和发送终止事件
|
||||
const stopResponse = await fetch('/api/stop-simulation', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ id: simulationId })
|
||||
});
|
||||
|
||||
if (!stopResponse.ok) {
|
||||
console.warn('调用停止仿真接口失败,但引擎已停止');
|
||||
}
|
||||
|
||||
// 重置按钮状态
|
||||
this.resetSimulationButtons();
|
||||
this.currentSimulationId = null;
|
||||
this.showSuccess('仿真已停止');
|
||||
|
||||
// 关闭DDS监控
|
||||
try {
|
||||
await fetch('/api/dds-monitor/unregister', {
|
||||
method: 'POST'
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('关闭DDS监控失败,但这不影响仿真停止');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('停止仿真失败:', error);
|
||||
this.showError('停止仿真失败: ' + error.message);
|
||||
|
||||
// 即使出错也尝试清理资源
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
this.eventSource = null;
|
||||
}
|
||||
this.resetSimulationButtons();
|
||||
this.currentSimulationId = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1466,12 +1466,56 @@ class ServiceDevelopment extends HTMLElement {
|
||||
const selectedYear = parseInt(calendarYear.value);
|
||||
const selectedMonth = parseInt(calendarMonth.value);
|
||||
|
||||
// 关闭当前对话框
|
||||
closeModal();
|
||||
|
||||
// 更新日期参数并重新显示对话框
|
||||
// 更新当前日期
|
||||
currentDate = new Date(selectedYear, selectedMonth, 1);
|
||||
this.showDateTimeDialog(inputElement);
|
||||
|
||||
// 重新生成日历内容
|
||||
const daysInMonth = new Date(selectedYear, selectedMonth + 1, 0).getDate();
|
||||
const firstDay = new Date(selectedYear, selectedMonth, 1).getDay();
|
||||
|
||||
let calendarRows = '';
|
||||
let dayCount = 1;
|
||||
|
||||
// 添加表头
|
||||
calendarRows += '<tr>';
|
||||
['日', '一', '二', '三', '四', '五', '六'].forEach(dayName => {
|
||||
calendarRows += `<th>${dayName}</th>`;
|
||||
});
|
||||
calendarRows += '</tr>';
|
||||
|
||||
// 计算行数
|
||||
const totalCells = firstDay + daysInMonth;
|
||||
const rowCount = Math.ceil(totalCells / 7);
|
||||
|
||||
// 添加日期行
|
||||
for (let i = 0; i < rowCount; i++) {
|
||||
calendarRows += '<tr>';
|
||||
for (let j = 0; j < 7; j++) {
|
||||
if ((i === 0 && j < firstDay) || dayCount > daysInMonth) {
|
||||
calendarRows += '<td></td>';
|
||||
} else {
|
||||
const isToday = dayCount === currentDate.getDate();
|
||||
calendarRows += `<td class="calendar-day ${isToday ? 'selected' : ''}" data-day="${dayCount}">${dayCount}</td>`;
|
||||
dayCount++;
|
||||
}
|
||||
}
|
||||
calendarRows += '</tr>';
|
||||
}
|
||||
|
||||
// 更新日历表格内容
|
||||
const calendarTable = modal.querySelector('.calendar-table');
|
||||
calendarTable.innerHTML = calendarRows;
|
||||
|
||||
// 重新绑定日期选择事件
|
||||
const calendarDays = modal.querySelectorAll('.calendar-day');
|
||||
calendarDays.forEach(cell => {
|
||||
cell.addEventListener('click', (e) => {
|
||||
// 移除所有选中状态
|
||||
calendarDays.forEach(day => day.classList.remove('selected'));
|
||||
// 添加新选中状态
|
||||
e.target.classList.add('selected');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
calendarMonth.addEventListener('change', updateCalendar);
|
||||
|
@ -20,9 +20,14 @@ class SimulationMonitor extends HTMLElement {
|
||||
connectedCallback() {
|
||||
this.render();
|
||||
this.isActive = true; // 设置初始状态为激活
|
||||
// 等待组件完全加载
|
||||
|
||||
// 等待组件完全加载后初始化
|
||||
setTimeout(() => {
|
||||
this.initializeComponent();
|
||||
// 初始化完成后再启动定时器,给服务器一些准备时间
|
||||
setTimeout(() => {
|
||||
this.startStatusCheck();
|
||||
}, 1000); // 延迟1秒启动定时器
|
||||
}, 100);
|
||||
}
|
||||
|
||||
@ -45,10 +50,10 @@ class SimulationMonitor extends HTMLElement {
|
||||
|
||||
// 设置定时器,每秒执行一次
|
||||
this.statusCheckInterval = setInterval(() => {
|
||||
if (this.isActive && this.monitorStatus.isMonitoring) {
|
||||
if (this.isActive) {
|
||||
this.checkMonitorStatus();
|
||||
} else {
|
||||
// 如果监控已停止,清除定时器
|
||||
// 如果组件不再激活,清除定时器
|
||||
this.stopStatusCheck();
|
||||
}
|
||||
}, 1000);
|
||||
@ -56,8 +61,6 @@ class SimulationMonitor extends HTMLElement {
|
||||
|
||||
// 修改 checkMonitorStatus 方法
|
||||
async checkMonitorStatus() {
|
||||
if (!this.monitorStatus.isMonitoring) return;
|
||||
|
||||
try {
|
||||
// 获取监控状态
|
||||
const statusResponse = await fetch('/api/system-monitor/status');
|
||||
@ -89,20 +92,13 @@ class SimulationMonitor extends HTMLElement {
|
||||
// 只在成功获取数据后更新UI
|
||||
this.updateUI();
|
||||
} else {
|
||||
// 如果监控已停止,清空数据
|
||||
this.systemInfo = null;
|
||||
this.threadInfo = null;
|
||||
// 停止状态检查
|
||||
this.stopStatusCheck();
|
||||
// 更新UI显示停止状态
|
||||
this.updateUI();
|
||||
// 如果监控已停止,尝试启动监控
|
||||
this.startMonitoring();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取监控状态失败:', error);
|
||||
this.monitorStatus.lastError = error.message;
|
||||
this.updateUI();
|
||||
// 发生错误时也停止状态检查
|
||||
this.stopStatusCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,51 +123,19 @@ class SimulationMonitor extends HTMLElement {
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取构型参数
|
||||
const configResponse = await fetch(`/api/configurations/${confID}`);
|
||||
if (!configResponse.ok) {
|
||||
throw new Error('获取构型参数失败');
|
||||
}
|
||||
const configData = await configResponse.json();
|
||||
|
||||
// 从构型参数中提取域ID
|
||||
const domainId = configData.DomainID;
|
||||
if (!domainId) {
|
||||
throw new Error('构型参数中未找到有效的域ID');
|
||||
}
|
||||
this.domainId = domainId;
|
||||
|
||||
// 首先检查DDS监控状态
|
||||
const ddsStatusResponse = await fetch('/api/dds-monitor/status');
|
||||
const ddsStatusData = await ddsStatusResponse.json();
|
||||
|
||||
// 如果DDS监控未初始化,先初始化DDS监控
|
||||
// 如果DDS监控未初始化,直接返回,等待下次定时器运行时再检查
|
||||
if (!ddsStatusData.isInitialized) {
|
||||
const ddsInitResponse = await fetch('/api/dds-monitor/initialize', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
domainId,
|
||||
monitorId: this.monitorId
|
||||
})
|
||||
});
|
||||
|
||||
if (!ddsInitResponse.ok) {
|
||||
const errorData = await ddsInitResponse.json();
|
||||
console.error('DDS监控初始化失败:', errorData.error);
|
||||
return;
|
||||
}
|
||||
//console.log('DDS监控未初始化,等待下次检查');
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动系统监控
|
||||
const response = await fetch('/api/system-monitor/start', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ domainId })
|
||||
method: 'POST'
|
||||
});
|
||||
const data = await response.json();
|
||||
if (response.ok) {
|
||||
@ -192,17 +156,9 @@ class SimulationMonitor extends HTMLElement {
|
||||
disconnectedCallback() {
|
||||
this.isActive = false;
|
||||
this.stopStatusCheck();
|
||||
// 注销监控器
|
||||
fetch('/api/dds-monitor/unregister', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ monitorId: this.monitorId })
|
||||
}).catch(error => {
|
||||
console.error('注销监控器失败:', error);
|
||||
});
|
||||
// 不要在这里销毁图表实例,让它在重新激活时重新创建
|
||||
// 自动停止监控
|
||||
this.stopMonitoring();
|
||||
// 注意:不再在这里注销 DDS 监控,因为其他组件可能正在使用它
|
||||
}
|
||||
|
||||
async initializeComponent() {
|
||||
@ -224,9 +180,9 @@ class SimulationMonitor extends HTMLElement {
|
||||
const statusData = await statusResponse.json();
|
||||
this.monitorStatus = statusData;
|
||||
|
||||
// 如果已经在监控中,开始状态检查
|
||||
if (this.monitorStatus.isMonitoring && !this.statusCheckInterval) {
|
||||
this.startStatusCheck();
|
||||
// 如果监控未运行,尝试启动监控
|
||||
if (!this.monitorStatus.isMonitoring) {
|
||||
this.startMonitoring();
|
||||
}
|
||||
|
||||
this.chartInitialized = true;
|
||||
@ -285,25 +241,26 @@ class SimulationMonitor extends HTMLElement {
|
||||
}
|
||||
|
||||
updateUI() {
|
||||
const startButton = this.shadowRoot.querySelector('.start-button');
|
||||
const stopButton = this.shadowRoot.querySelector('.stop-button');
|
||||
const statusDisplay = this.shadowRoot.querySelector('.status-display');
|
||||
const statusIndicator = this.shadowRoot.querySelector('#statusIndicator');
|
||||
const statusText = this.shadowRoot.querySelector('#statusText');
|
||||
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) {
|
||||
startButton.disabled = true;
|
||||
stopButton.disabled = false;
|
||||
} else {
|
||||
startButton.disabled = false;
|
||||
stopButton.disabled = true;
|
||||
}
|
||||
|
||||
// 更新状态显示
|
||||
statusDisplay.textContent = `监控状态: ${this.monitorStatus.isMonitoring ? '运行中' : '已停止'}`;
|
||||
if (this.monitorStatus.lastError) {
|
||||
statusDisplay.textContent += ` (错误: ${this.monitorStatus.lastError})`;
|
||||
if (statusIndicator && statusText) {
|
||||
if (this.monitorStatus.isMonitoring) {
|
||||
statusIndicator.classList.add('active');
|
||||
statusIndicator.classList.remove('error');
|
||||
statusText.textContent = '监控中';
|
||||
} else if (this.monitorStatus.lastError) {
|
||||
statusIndicator.classList.remove('active');
|
||||
statusIndicator.classList.add('error');
|
||||
statusText.textContent = `监控错误: ${this.monitorStatus.lastError}`;
|
||||
} else {
|
||||
statusIndicator.classList.remove('active', 'error');
|
||||
statusText.textContent = '未监控';
|
||||
}
|
||||
}
|
||||
|
||||
// 更新引擎信息
|
||||
@ -404,6 +361,17 @@ class SimulationMonitor extends HTMLElement {
|
||||
|
||||
async stopMonitoring() {
|
||||
try {
|
||||
// 首先检查监控状态
|
||||
const statusResponse = await fetch('/api/system-monitor/status');
|
||||
const statusData = await statusResponse.json();
|
||||
|
||||
// 如果监控未运行,直接返回
|
||||
if (!statusData.isMonitoring) {
|
||||
//console.log('监控未运行,无需关闭');
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行关闭操作
|
||||
const response = await fetch('/api/system-monitor/stop', {
|
||||
method: 'POST'
|
||||
});
|
||||
@ -462,11 +430,44 @@ class SimulationMonitor extends HTMLElement {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.toolbar-section {
|
||||
background-color: white;
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 16px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.monitor-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.status-indicator.active {
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.status-indicator.error {
|
||||
background: #ff4d4f;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
@ -495,63 +496,6 @@ class SimulationMonitor extends HTMLElement {
|
||||
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;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.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;
|
||||
@ -711,13 +655,12 @@ class SimulationMonitor extends HTMLElement {
|
||||
}
|
||||
</style>
|
||||
<div class="monitor-container">
|
||||
<div class="toolbar-section">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<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 class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<div class="monitor-status">
|
||||
<div class="status-indicator" id="statusIndicator"></div>
|
||||
<span id="statusText">未监控</span>
|
||||
</div>
|
||||
<div class="status-display">监控状态: 未启动</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content-container">
|
||||
@ -824,7 +767,7 @@ class SimulationMonitor extends HTMLElement {
|
||||
<input type="checkbox" id="display-type-switch">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<span id="display-type-label">频率</span>
|
||||
<span id="display-type-label">周期</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -284,13 +284,15 @@ class UpdateHistory extends HTMLElement {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.version-number {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
margin-right: 15px;
|
||||
min-width: 80px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -299,7 +301,7 @@ class UpdateHistory extends HTMLElement {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #2d3748;
|
||||
margin-right: 15px;
|
||||
min-width: 200px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -307,6 +309,7 @@ class UpdateHistory extends HTMLElement {
|
||||
.version-date {
|
||||
color: #718096;
|
||||
font-size: 14px;
|
||||
min-width: 150px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
@ -316,7 +319,6 @@ class UpdateHistory extends HTMLElement {
|
||||
font-size: 14px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.expand-btn {
|
||||
|
@ -6,27 +6,25 @@ const { initializeMonitor, cleanupMonitor } = require('../utils/systemMonitor');
|
||||
let monitorStatus = {
|
||||
isInitialized: false,
|
||||
domainId: null,
|
||||
lastError: null,
|
||||
activeMonitors: new Set() // 添加活跃监控器集合
|
||||
lastError: null
|
||||
};
|
||||
|
||||
// 初始化监控服务
|
||||
router.post('/initialize', async (req, res) => {
|
||||
try {
|
||||
const { domainId, monitorId } = req.body;
|
||||
const { domainId } = req.body;
|
||||
|
||||
if (!domainId || !monitorId) {
|
||||
if (!domainId) {
|
||||
return res.status(400).json({ error: '缺少必要的参数' });
|
||||
}
|
||||
|
||||
// 如果已经初始化,检查是否是新的监控器
|
||||
// 如果已经初始化,检查域ID是否匹配
|
||||
if (monitorStatus.isInitialized) {
|
||||
if (monitorStatus.domainId !== domainId) {
|
||||
return res.status(400).json({ error: 'DDS域ID不匹配' });
|
||||
}
|
||||
monitorStatus.activeMonitors.add(monitorId);
|
||||
return res.json({
|
||||
message: '监控器已注册',
|
||||
message: '监控服务已初始化',
|
||||
status: monitorStatus
|
||||
});
|
||||
}
|
||||
@ -41,7 +39,6 @@ router.post('/initialize', async (req, res) => {
|
||||
monitorStatus.isInitialized = true;
|
||||
monitorStatus.domainId = domainId;
|
||||
monitorStatus.lastError = null;
|
||||
monitorStatus.activeMonitors.add(monitorId);
|
||||
|
||||
res.json({
|
||||
message: '监控服务初始化成功',
|
||||
@ -57,32 +54,21 @@ router.post('/initialize', async (req, res) => {
|
||||
// 注销监控器
|
||||
router.post('/unregister', async (req, res) => {
|
||||
try {
|
||||
const { monitorId } = req.body;
|
||||
|
||||
if (!monitorId) {
|
||||
return res.status(400).json({ error: '缺少必要的monitorId参数' });
|
||||
}
|
||||
|
||||
monitorStatus.activeMonitors.delete(monitorId);
|
||||
|
||||
// 如果没有活跃的监控器了,清理资源
|
||||
if (monitorStatus.activeMonitors.size === 0) {
|
||||
cleanupMonitor();
|
||||
monitorStatus = {
|
||||
isInitialized: false,
|
||||
domainId: null,
|
||||
lastError: null,
|
||||
activeMonitors: new Set()
|
||||
};
|
||||
}
|
||||
// 清理资源
|
||||
await cleanupMonitor();
|
||||
monitorStatus = {
|
||||
isInitialized: false,
|
||||
domainId: null,
|
||||
lastError: null
|
||||
};
|
||||
|
||||
res.json({
|
||||
message: '监控器注销成功',
|
||||
message: '监控服务注销成功',
|
||||
status: monitorStatus
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('注销监控器失败:', error);
|
||||
res.status(500).json({ error: '注销监控器失败', message: error.message });
|
||||
console.error('注销监控服务失败:', error);
|
||||
res.status(500).json({ error: '注销监控服务失败', message: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -83,7 +83,7 @@ router.post('/resume', async (req, res) => {
|
||||
*/
|
||||
router.post('/stop', async (req, res) => {
|
||||
try {
|
||||
const result = stopEngine();
|
||||
const result = await stopEngine();
|
||||
res.json({
|
||||
success: !result.includes('失败'),
|
||||
message: result
|
||||
|
@ -385,16 +385,36 @@ router.post('/stop-simulation', async (req, res) => {
|
||||
if (isRunning && isXNEngine) {
|
||||
// 终止进程
|
||||
try {
|
||||
// 使用 SIGTERM 信号终止进程
|
||||
process.kill(processInfo.pid, 'SIGTERM');
|
||||
// 等待进程终止
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// 检查进程是否真的终止了
|
||||
const stillRunning = await isProcessRunning(processInfo.pid);
|
||||
if (stillRunning) {
|
||||
// 如果还在运行,强制终止
|
||||
process.kill(processInfo.pid, 'SIGKILL');
|
||||
}
|
||||
// 使用 Promise.race 添加超时机制
|
||||
const killPromise = new Promise(async (resolve) => {
|
||||
let attempts = 0;
|
||||
const maxAttempts = 5;
|
||||
|
||||
while (attempts < maxAttempts) {
|
||||
const stillRunning = await isProcessRunning(processInfo.pid);
|
||||
if (!stillRunning) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
// 如果进程还在运行,使用 SIGKILL 强制终止
|
||||
if (await isProcessRunning(processInfo.pid)) {
|
||||
process.kill(processInfo.pid, 'SIGKILL');
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('终止进程超时')), 3000)
|
||||
);
|
||||
|
||||
await Promise.race([killPromise, timeoutPromise]);
|
||||
|
||||
// 删除数据库中的进程记录
|
||||
await deleteXNEngineProcess(id);
|
||||
|
@ -112,14 +112,16 @@ function initializeMonitor(domainId) {
|
||||
}
|
||||
|
||||
// 清理监控服务器资源
|
||||
function cleanupMonitor() {
|
||||
async function cleanupMonitor() {
|
||||
if (!monitorLib) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
monitorLib.XN_Cleanup();
|
||||
} catch (error) {
|
||||
return;
|
||||
console.error('清理监控服务器资源失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,7 +285,7 @@ function resumeEngine() {
|
||||
}
|
||||
|
||||
// 停止引擎
|
||||
function stopEngine() {
|
||||
async function stopEngine() {
|
||||
if (!monitorLib) {
|
||||
return '监控服务器库未加载';
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user