实现设备连接状态管理和实时统计更新功能

核心功能:
• 设备卡片连接/断开按钮:动态显示"连接"/"断开",一键切换设备状态
• 数据库状态同步:实时更新Client数据库devices表中的state字段(0=离线,1=在线)
• 实时统计更新:设备管理中心顶部统计信息随状态变化实时更新
• 视觉反馈增强:状态变化动画、颜色指示器、按钮文本自动更新

技术实现:
• DeviceCard新增连接状态切换逻辑和数据库更新方法
• 新增deviceStatusChanged信号实现组件间状态同步
• DeviceListPanel监听状态变化并更新统计显示
• 唯一数据库连接名称避免重复连接警告

用户体验提升:
• 简化设备管理操作,点击即可控制设备连接状态
• 界面状态与数据库完全同步,重启后保持状态
• 实时反馈让用户清楚了解当前系统状态
main
123 3 days ago
parent 96f7f667fa
commit 4e5d2b45e2

@ -28,6 +28,9 @@
#include <QGridLayout>
#include <QTimer>
#include <QDateTime>
#include <QPropertyAnimation>
#include <QGraphicsEffect>
#include <QGraphicsDropShadowEffect>
/**
* @brief
@ -212,6 +215,13 @@ signals:
* @param deviceId ID
*/
void deviceDetailsRequested(const QString &deviceId);
/**
* @brief
* @param deviceId ID
* @param status
*/
void deviceStatusChanged(const QString &deviceId, DeviceStatus status);
public slots:
/**
@ -278,6 +288,18 @@ private:
*/
void setupStyle();
/**
* @brief
*/
void setupAnimations();
/**
* @brief
* @param oldStatus
* @param newStatus
*/
void animateStatusChange(DeviceStatus oldStatus, DeviceStatus newStatus);
/**
* @brief
*/
@ -309,6 +331,25 @@ private:
* @return
*/
QString formatCoordinates(double longitude, double latitude) const;
/**
* @brief
* @return "连接""断开"
*/
QString getConnectionButtonText() const;
/**
* @brief
* @return
*/
QString getConnectionButtonTooltip() const;
/**
* @brief
* @param status
* @return
*/
bool updateDeviceStatusInDatabase(DeviceStatus status);
private:
// 设备信息
@ -338,6 +379,11 @@ private:
QGridLayout *m_infoLayout; ///< 信息区域布局
QVBoxLayout *m_buttonLayout; ///< 按钮区域布局(垂直排列)
// 动画效果组件
QPropertyAnimation *m_hoverAnimation; ///< 悬停动画
QPropertyAnimation *m_scaleAnimation; ///< 缩放动画
QGraphicsDropShadowEffect *m_shadowEffect; ///< 阴影效果
// 样式和配置 - 优化的卡片尺寸
static const int CARD_WIDTH = 320; ///< 卡片宽度 (再次增加)
static const int CARD_HEIGHT = 240; ///< 卡片高度 (增加以显示完整信息)

@ -265,6 +265,13 @@ private slots:
*/
void onDeviceCardDetailsRequested(const QString &deviceId);
/**
* @brief
* @param deviceId ID
* @param status
*/
void onDeviceStatusChanged(const QString &deviceId, DeviceStatus status);
/**
* @brief
*/

@ -98,6 +98,17 @@ public:
*/
void setupMapDisplay();
/**
* @brief
* @param name
* @param type ("uav" "dog")
* @param ip IP
* @param port
* @param state (0)
* @return
*/
bool addDeviceToDatabase(const QString &name, const QString &type, const QString &ip, int port, int state = 0);
/**
* @brief
* @param button

@ -0,0 +1,12 @@
#!/bin/bash
# 战场探索系统启动脚本
# 清理snap环境变量避免库冲突
# 清理可能导致冲突的snap环境变量
unset GTK_PATH
unset LOCPATH
unset GIO_MODULE_DIR
unset GSETTINGS_SCHEMA_DIR
# 启动程序
exec ./BattlefieldExplorationSystem "$@"

@ -0,0 +1,55 @@
-- BattlefieldExplorationSystem 数据库设置脚本
-- 创建数据库
CREATE DATABASE IF NOT EXISTS fly_land_database;
USE fly_land_database;
-- 创建无人机表
CREATE TABLE IF NOT EXISTS uavdatabase (
id VARCHAR(50) PRIMARY KEY,
state INT DEFAULT 0,
ip VARCHAR(15) NOT NULL,
port INT DEFAULT 8080,
lon DOUBLE DEFAULT 0.0,
lat DOUBLE DEFAULT 0.0
);
-- 创建机器狗表
CREATE TABLE IF NOT EXISTS dogdatabase (
id VARCHAR(50) PRIMARY KEY,
state INT DEFAULT 0,
ip VARCHAR(15) NOT NULL,
port INT DEFAULT 9090,
lon DOUBLE DEFAULT 0.0,
lat DOUBLE DEFAULT 0.0
);
-- 插入测试数据 - 无人机
INSERT INTO uavdatabase (id, state, ip, port, lon, lat) VALUES
('UAV001', 1, '192.168.1.101', 8080, 116.40, 39.90),
('UAV002', 2, '192.168.1.102', 8080, 116.42, 39.92),
('UAV003', 0, '192.168.1.103', 8080, 116.44, 39.94)
ON DUPLICATE KEY UPDATE
state = VALUES(state),
ip = VALUES(ip),
port = VALUES(port),
lon = VALUES(lon),
lat = VALUES(lat);
-- 插入测试数据 - 机器狗
INSERT INTO dogdatabase (id, state, ip, port, lon, lat) VALUES
('DOG001', 1, '192.168.1.201', 9090, 116.38, 39.88),
('DOG002', 0, '192.168.1.202', 9090, 116.46, 39.86)
ON DUPLICATE KEY UPDATE
state = VALUES(state),
ip = VALUES(ip),
port = VALUES(port),
lon = VALUES(lon),
lat = VALUES(lat);
-- 验证数据
SELECT 'UAV Devices:' AS info;
SELECT * FROM uavdatabase;
SELECT 'Dog Devices:' AS info;
SELECT * FROM dogdatabase;

@ -18,6 +18,13 @@
#include <QFontMetrics>
#include <QApplication>
#include <QDebug>
#include <QPropertyAnimation>
#include <QGraphicsDropShadowEffect>
#include <QEasingCurve>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDateTime>
DeviceCard::DeviceCard(const DeviceInfo &device, QWidget *parent)
: QWidget(parent)
@ -38,9 +45,13 @@ DeviceCard::DeviceCard(const DeviceInfo &device, QWidget *parent)
, m_headerLayout(nullptr)
, m_infoLayout(nullptr)
, m_buttonLayout(nullptr)
, m_hoverAnimation(nullptr)
, m_scaleAnimation(nullptr)
, m_shadowEffect(nullptr)
{
setupUI();
setupStyle();
setupAnimations(); // 添加动画初始化
connectSignals();
// 设置卡片基本属性
@ -68,10 +79,11 @@ void DeviceCard::setupUI()
m_headerLayout->setSpacing(8); // 减少间距,让名称更靠左
m_headerLayout->setContentsMargins(0, 0, 0, 0); // 移除边距
// 设备图标 - 适当调整尺寸
// 设备图标 - 增大尺寸提高区分度
m_deviceIconLabel = new QLabel();
m_deviceIconLabel->setFixedSize(32, 32); // 稍微减小图标
m_deviceIconLabel->setFixedSize(40, 40); // 增大图标尺寸
m_deviceIconLabel->setScaledContents(true);
m_deviceIconLabel->setAlignment(Qt::AlignCenter);
updateDeviceIcon();
// 设备名称 - 调整字体和位置
@ -84,11 +96,11 @@ void DeviceCard::setupUI()
m_deviceNameLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // 左对齐,垂直居中
m_deviceNameLabel->setContentsMargins(0, 0, 0, 0); // 恢复正常边距
// 状态指示器(圆点)- 适当调整尺寸
// 状态指示器(圆点)- 增大尺寸提高可见性
m_statusIndicator = new QLabel("");
m_statusIndicator->setFixedSize(20, 20);
m_statusIndicator->setFixedSize(28, 28); // 增大状态指示器
m_statusIndicator->setAlignment(Qt::AlignCenter);
m_statusIndicator->setFont(QFont("Arial", 16));
m_statusIndicator->setFont(QFont("Arial", 22)); // 增大字体
// 状态文本 - 适当调整字体
m_statusLabel = new QLabel(getStatusText(m_currentStatus));
@ -143,9 +155,9 @@ void DeviceCard::setupUI()
m_detailsButton->setToolTip("设备详情");
m_detailsButton->setFont(QFont("Arial", 12, QFont::Bold));
m_controlButton = new QPushButton("控制");
m_controlButton = new QPushButton(getConnectionButtonText());
m_controlButton->setFixedSize(60, 32);
m_controlButton->setToolTip("设备控制");
m_controlButton->setToolTip(getConnectionButtonTooltip());
m_controlButton->setFont(QFont("Arial", 12, QFont::Bold));
m_locationButton = new QPushButton("定位");
@ -181,7 +193,7 @@ void DeviceCard::setupUI()
void DeviceCard::setupStyle()
{
// 基础卡片样式
// 基础卡片样式移除CSS悬停样式使用动画实现
QString cardStyle = QString(
"DeviceCard {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
@ -190,12 +202,6 @@ void DeviceCard::setupStyle()
" border: 2px solid rgba(82, 194, 242, 0.4);"
" border-radius: %1px;"
"}"
"DeviceCard:hover {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
" stop:0 rgba(82, 194, 242, 0.3),"
" stop:1 rgba(45, 120, 180, 0.3));"
" border-color: rgba(82, 194, 242, 0.8);"
"}"
).arg(BORDER_RADIUS);
setStyleSheet(cardStyle);
@ -266,6 +272,95 @@ void DeviceCard::setupStyle()
updateStatusColor();
}
void DeviceCard::setupAnimations()
{
// 创建阴影效果
m_shadowEffect = new QGraphicsDropShadowEffect(this);
m_shadowEffect->setBlurRadius(15);
m_shadowEffect->setColor(QColor(82, 194, 242, 100));
m_shadowEffect->setOffset(0, 3);
setGraphicsEffect(m_shadowEffect);
// 创建悬停动画(阴影模糊半径)
m_hoverAnimation = new QPropertyAnimation(m_shadowEffect, "blurRadius", this);
m_hoverAnimation->setDuration(200);
m_hoverAnimation->setEasingCurve(QEasingCurve::OutCubic);
// 创建缩放动画(会在需要时手动创建)
m_scaleAnimation = nullptr;
}
void DeviceCard::animateStatusChange(DeviceStatus oldStatus, DeviceStatus newStatus)
{
// 状态指示器闪烁动画
QPropertyAnimation *flashAnimation = new QPropertyAnimation(m_statusIndicator, "windowOpacity", this);
flashAnimation->setDuration(300);
flashAnimation->setKeyValueAt(0, 1.0);
flashAnimation->setKeyValueAt(0.5, 0.3);
flashAnimation->setKeyValueAt(1, 1.0);
flashAnimation->setEasingCurve(QEasingCurve::InOutQuad);
// 卡片边框闪烁效果
QPropertyAnimation *borderAnimation = new QPropertyAnimation(this);
borderAnimation->setDuration(500);
// 根据状态变化类型选择不同的动画效果
QString notificationColor;
if (newStatus == DeviceStatus::Online && oldStatus != DeviceStatus::Online) {
// 设备上线 - 绿色闪烁
notificationColor = "rgba(0, 255, 127, 0.8)";
} else if (newStatus == DeviceStatus::Offline) {
// 设备离线 - 红色闪烁
notificationColor = "rgba(255, 68, 68, 0.8)";
} else if (newStatus == DeviceStatus::Warning) {
// 设备警告 - 黄色闪烁
notificationColor = "rgba(255, 215, 0, 0.8)";
} else {
// 其他状态变化 - 蓝色闪烁
notificationColor = "rgba(82, 194, 242, 0.8)";
}
// 设置临时的闪烁样式
connect(flashAnimation, &QPropertyAnimation::valueChanged, this, [this, notificationColor](const QVariant &value) {
QString flashStyle = QString(
"DeviceCard {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
" stop:0 rgba(45, 65, 95, 0.9),"
" stop:1 rgba(25, 40, 65, 0.9));"
" border: 3px solid %1;"
" border-radius: %2px;"
"}"
).arg(notificationColor).arg(BORDER_RADIUS);
setStyleSheet(flashStyle);
});
// 动画结束后恢复正常样式并更新状态颜色
connect(flashAnimation, &QPropertyAnimation::finished, this, [this, flashAnimation]() {
// 恢复默认样式
QString defaultStyle = QString(
"DeviceCard {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
" stop:0 rgba(45, 65, 95, 0.9),"
" stop:1 rgba(25, 40, 65, 0.9));"
" border: 2px solid rgba(82, 194, 242, 0.4);"
" border-radius: %1px;"
"}"
).arg(BORDER_RADIUS);
setStyleSheet(defaultStyle);
// 更新状态颜色
updateStatusColor();
// 清理动画对象
flashAnimation->deleteLater();
});
// 启动动画
flashAnimation->start();
}
void DeviceCard::connectSignals()
{
connect(m_detailsButton, &QPushButton::clicked, this, &DeviceCard::onDetailsClicked);
@ -290,11 +385,21 @@ void DeviceCard::updateDeviceInfo(const DeviceInfo &device)
void DeviceCard::updateDeviceStatus(DeviceStatus status)
{
m_currentStatus = status;
m_statusLabel->setText(getStatusText(status));
updateStatusColor();
qDebug() << "Device status updated:" << m_deviceInfo.name << "status:" << static_cast<int>(status);
if (m_currentStatus != status) {
DeviceStatus oldStatus = m_currentStatus;
m_currentStatus = status;
m_statusLabel->setText(getStatusText(status));
// 更新连接按钮文本和提示
m_controlButton->setText(getConnectionButtonText());
m_controlButton->setToolTip(getConnectionButtonTooltip());
// 添加状态变化动画效果
animateStatusChange(oldStatus, status);
qDebug() << "Device status updated with animation:" << m_deviceInfo.name
<< "from" << static_cast<int>(oldStatus) << "to" << static_cast<int>(status);
}
}
@ -340,6 +445,30 @@ void DeviceCard::mousePressEvent(QMouseEvent *event)
void DeviceCard::enterEvent(QEvent *event)
{
m_isHovered = true;
// 启动悬停动画效果
if (m_hoverAnimation && m_shadowEffect) {
m_hoverAnimation->setStartValue(15);
m_hoverAnimation->setEndValue(25);
m_hoverAnimation->start();
// 更新阴影颜色为更亮的蓝色
m_shadowEffect->setColor(QColor(82, 194, 242, 150));
}
// 更新卡片背景以实现更明显的悬停效果
QString enhancedHoverStyle = QString(
"DeviceCard {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
" stop:0 rgba(82, 194, 242, 0.4),"
" stop:1 rgba(45, 120, 180, 0.4));"
" border: 2px solid rgba(82, 194, 242, 1.0);"
" border-radius: %1px;"
"}"
).arg(BORDER_RADIUS);
setStyleSheet(enhancedHoverStyle);
update();
QWidget::enterEvent(event);
}
@ -347,6 +476,30 @@ void DeviceCard::enterEvent(QEvent *event)
void DeviceCard::leaveEvent(QEvent *event)
{
m_isHovered = false;
// 停止悬停动画效果
if (m_hoverAnimation && m_shadowEffect) {
m_hoverAnimation->setStartValue(25);
m_hoverAnimation->setEndValue(15);
m_hoverAnimation->start();
// 恢复阴影颜色
m_shadowEffect->setColor(QColor(82, 194, 242, 100));
}
// 恢复卡片默认背景
QString defaultStyle = QString(
"DeviceCard {"
" background: qlineargradient(x1:0, y1:0, x2:0, y2:1,"
" stop:0 rgba(45, 65, 95, 0.9),"
" stop:1 rgba(25, 40, 65, 0.9));"
" border: 2px solid rgba(82, 194, 242, 0.4);"
" border-radius: %1px;"
"}"
).arg(BORDER_RADIUS);
setStyleSheet(defaultStyle);
update();
QWidget::leaveEvent(event);
}
@ -375,8 +528,39 @@ void DeviceCard::onDetailsClicked()
void DeviceCard::onControlClicked()
{
qDebug() << "Control clicked for device:" << m_deviceInfo.name;
emit deviceControlRequested(m_deviceInfo.id);
qDebug() << "Connection toggle clicked for device:" << m_deviceInfo.name;
// 切换设备连接状态
DeviceStatus newStatus;
if (m_currentStatus == DeviceStatus::Online) {
// 当前在线,切换为离线
newStatus = DeviceStatus::Offline;
qDebug() << "Disconnecting device:" << m_deviceInfo.name;
} else {
// 当前离线,切换为在线
newStatus = DeviceStatus::Online;
qDebug() << "Connecting device:" << m_deviceInfo.name;
}
// 更新数据库中的状态
if (updateDeviceStatusInDatabase(newStatus)) {
// 数据库更新成功更新UI
updateDeviceStatus(newStatus);
m_deviceInfo.status = newStatus;
// 更新按钮文本和提示
m_controlButton->setText(getConnectionButtonText());
m_controlButton->setToolTip(getConnectionButtonTooltip());
// 发送状态变化信号,通知父组件更新统计信息
emit deviceStatusChanged(m_deviceInfo.id, newStatus);
qDebug() << "Device status successfully updated:" << m_deviceInfo.name
<< "to" << (newStatus == DeviceStatus::Online ? "Online" : "Offline");
} else {
qWarning() << "Failed to update device status in database for:" << m_deviceInfo.name;
// 可以显示错误提示给用户
}
}
void DeviceCard::onLocationClicked()
@ -388,7 +572,34 @@ void DeviceCard::onLocationClicked()
void DeviceCard::updateStatusColor()
{
QString color = getStatusColor(m_currentStatus);
m_statusIndicator->setStyleSheet(QString("QLabel { color: %1; background: transparent; border: none; }").arg(color));
// 为状态指示器添加发光效果和阴影
QString shadowColor;
switch (m_currentStatus) {
case DeviceStatus::Online:
shadowColor = "rgba(0, 255, 127, 0.6)"; // 绿色发光
break;
case DeviceStatus::Warning:
shadowColor = "rgba(255, 215, 0, 0.6)"; // 黄色发光
break;
case DeviceStatus::Offline:
shadowColor = "rgba(255, 68, 68, 0.6)"; // 红色发光
break;
default:
shadowColor = "rgba(136, 136, 136, 0.6)"; // 灰色发光
break;
}
m_statusIndicator->setStyleSheet(QString(
"QLabel {"
" color: %1;"
" background: qradialgradient(cx:0.5, cy:0.5, radius:0.8,"
" stop:0 %2, stop:1 transparent);"
" border-radius: 14px;"
" border: 2px solid %1;"
" font-weight: bold;"
"}"
).arg(color).arg(shadowColor));
// 更新卡片边框颜色
QString borderColor;
@ -415,10 +626,42 @@ void DeviceCard::updateDeviceIcon()
QString iconPath = m_deviceInfo.getTypeIconPath();
QPixmap pixmap(iconPath);
if (!pixmap.isNull()) {
m_deviceIconLabel->setPixmap(pixmap.scaled(24, 24, Qt::KeepAspectRatio, Qt::SmoothTransformation));
m_deviceIconLabel->setPixmap(pixmap.scaled(36, 36, Qt::KeepAspectRatio, Qt::SmoothTransformation));
// 添加图标背景效果
m_deviceIconLabel->setStyleSheet(
"QLabel {"
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
" stop:0 rgba(82, 194, 242, 0.15),"
" stop:1 rgba(45, 120, 180, 0.15));"
" border: 2px solid rgba(82, 194, 242, 0.3);"
" border-radius: 20px;"
" padding: 2px;"
"}"
);
} else {
qWarning() << "Failed to load device icon:" << iconPath;
m_deviceIconLabel->setText("📱"); // 备用图标
// 设置默认设备图标,根据设备类型选择
if (m_deviceInfo.type == "uav") {
m_deviceIconLabel->setText("🚁"); // 无人机图标
m_deviceIconLabel->setFont(QFont("Arial", 20));
} else if (m_deviceInfo.type == "dog") {
m_deviceIconLabel->setText("🐕"); // 机器狗图标
m_deviceIconLabel->setFont(QFont("Arial", 20));
} else {
m_deviceIconLabel->setText("📱"); // 备用图标
m_deviceIconLabel->setFont(QFont("Arial", 18));
}
// 添加图标背景效果
m_deviceIconLabel->setStyleSheet(
"QLabel {"
" background: qlineargradient(x1:0, y1:0, x2:1, y2:1,"
" stop:0 rgba(82, 194, 242, 0.15),"
" stop:1 rgba(45, 120, 180, 0.15));"
" border: 2px solid rgba(82, 194, 242, 0.3);"
" border-radius: 20px;"
" padding: 2px;"
"}"
);
}
}
@ -435,10 +678,10 @@ QString DeviceCard::getStatusText(DeviceStatus status) const
QString DeviceCard::getStatusColor(DeviceStatus status) const
{
switch (status) {
case DeviceStatus::Online: return "#00FF7F"; // 绿色
case DeviceStatus::Warning: return "#FFD700"; // 黄色
case DeviceStatus::Offline: return "#FF4444"; // 红色
default: return "#888888"; // 灰色
case DeviceStatus::Online: return "#00FF7F"; // 明亮绿色
case DeviceStatus::Warning: return "#FFD700"; // 明亮黄色
case DeviceStatus::Offline: return "#FF4444"; // 明亮红色
default: return "#A0A0A0"; // 明亮灰色
}
}
@ -447,4 +690,61 @@ QString DeviceCard::formatCoordinates(double longitude, double latitude) const
return QString("N%1, E%2")
.arg(latitude, 0, 'f', 2)
.arg(longitude, 0, 'f', 2);
}
QString DeviceCard::getConnectionButtonText() const
{
if (m_currentStatus == DeviceStatus::Online) {
return "断开";
} else {
return "连接";
}
}
QString DeviceCard::getConnectionButtonTooltip() const
{
if (m_currentStatus == DeviceStatus::Online) {
return "断开设备连接";
} else {
return "连接到设备";
}
}
bool DeviceCard::updateDeviceStatusInDatabase(DeviceStatus status)
{
// 创建唯一的数据库连接名称(包含时间戳避免重复)
QString connectionName = QString("DeviceCard_%1_%2").arg(m_deviceInfo.id).arg(QDateTime::currentMSecsSinceEpoch());
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", connectionName);
db.setHostName("localhost");
db.setPort(3306);
db.setDatabaseName("Client");
db.setUserName("root");
db.setPassword("hzk200407140238");
if (!db.open()) {
qWarning() << "Failed to connect to database for device status update:" << db.lastError().text();
return false;
}
QSqlQuery query(db);
// 将DeviceStatus转换为数据库中的state值 (0=离线, 1=在线)
int stateValue = (status == DeviceStatus::Online) ? 1 : 0;
// 更新设备状态
QString sql = "UPDATE devices SET state = ? WHERE id = ?";
query.prepare(sql);
query.addBindValue(stateValue);
query.addBindValue(m_deviceInfo.id);
bool success = query.exec();
if (!success) {
qWarning() << "Failed to update device status in database:" << query.lastError().text();
} else {
qDebug() << "Successfully updated device status in database:" << m_deviceInfo.id
<< "state:" << stateValue;
}
db.close();
return success;
}

@ -14,6 +14,11 @@
#include <QApplication>
#include <QSplitter>
// Qt SQL头文件
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
DeviceListPanel::DeviceListPanel(QWidget *parent)
: QWidget(parent)
, m_titleLabel(nullptr)
@ -72,7 +77,7 @@ void DeviceListPanel::setupUI()
m_headerLayout = new QHBoxLayout();
// 面板标题
m_titleLabel = new QLabel("🤖 设备管理");
m_titleLabel = new QLabel("🎯 设备管理中心");
m_titleLabel->setFont(QFont("Arial", 18, QFont::Bold));
// 设备数量标签
@ -88,11 +93,11 @@ void DeviceListPanel::setupUI()
// === 操作按钮区域 ===
m_buttonLayout = new QHBoxLayout();
m_addUAVButton = new QPushButton("+ 无人机");
m_addUAVButton = new QPushButton("🚁 + 无人机");
m_addUAVButton->setMaximumHeight(50);
m_addUAVButton->setMaximumWidth(120);
m_addDogButton = new QPushButton("+ 机器狗");
m_addDogButton = new QPushButton("🐕 + 机器狗");
m_addDogButton->setMaximumHeight(50);
m_addDogButton->setMaximumWidth(120);
@ -413,6 +418,7 @@ DeviceCard* DeviceListPanel::createDeviceCard(const DeviceInfo &device)
connect(card, &DeviceCard::deviceControlRequested, this, &DeviceListPanel::onDeviceCardControlRequested);
connect(card, &DeviceCard::deviceLocationRequested, this, &DeviceListPanel::onDeviceCardLocationRequested);
connect(card, &DeviceCard::deviceDetailsRequested, this, &DeviceListPanel::onDeviceCardDetailsRequested);
connect(card, &DeviceCard::deviceStatusChanged, this, &DeviceListPanel::onDeviceStatusChanged);
return card;
}
@ -423,95 +429,82 @@ QList<DeviceInfo> DeviceListPanel::loadDevicesFromDatabase()
qDebug() << "Loading devices from database...";
// TODO: 尝试从真实数据库加载数据
// 尝试从真实数据库加载数据
bool databaseAvailable = false;
try {
// 检查数据库连接
if (m_uavDatabase && m_dogDatabase) {
qDebug() << "Database connections available, attempting to load real data...";
// 这里将来会实现真实的数据库查询
// auto uavList = m_uavDatabase->getAllDevices();
// auto dogList = m_dogDatabase->getAllDevices();
databaseAvailable = false; // 暂时设为false直到实现查询方法
qDebug() << "Attempting to connect to unified devices database...";
// 创建数据库连接
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "DeviceListPanel_Connection");
db.setHostName("localhost");
db.setPort(3306);
db.setDatabaseName("Client");
db.setUserName("root");
db.setPassword("hzk200407140238");
if (db.open()) {
qDebug() << "Successfully connected to Client database";
QSqlQuery query(db);
QString sql = "SELECT id, name, device_type, state, ip, port, longitude, latitude, signal_strength, battery_level FROM devices";
if (query.exec(sql)) {
qDebug() << "Successfully executed query on devices table";
while (query.next()) {
DeviceInfo device;
device.id = query.value("id").toString();
device.name = query.value("name").toString();
device.type = query.value("device_type").toString();
device.ipAddress = query.value("ip").toString();
device.port = query.value("port").toInt();
device.longitude = query.value("longitude").toDouble();
device.latitude = query.value("latitude").toDouble();
device.signalStrength = query.value("signal_strength").toInt();
device.batteryLevel = query.value("battery_level").toInt();
// 状态转换0=离线, 1=在线
int state = query.value("state").toInt();
switch (state) {
case 1: device.status = DeviceStatus::Online; break;
case 0:
default: device.status = DeviceStatus::Offline; break;
}
device.lastHeartbeat = QDateTime::currentDateTime();
device.createdAt = QDateTime::currentDateTime();
device.updatedAt = QDateTime::currentDateTime();
devices.append(device);
qDebug() << "Loaded device from database:" << device.name << "(" << device.type << ")";
}
databaseAvailable = true;
qDebug() << "Successfully loaded" << devices.size() << "devices from unified database";
} else {
qWarning() << "Failed to execute query:" << query.lastError().text();
}
db.close();
} else {
qWarning() << "Failed to connect to Client database:" << db.lastError().text();
}
} catch (const std::exception& e) {
qWarning() << "Database connection exception:" << e.what();
databaseAvailable = false;
} catch (...) {
qWarning() << "Database connection failed, using test data";
qWarning() << "Unknown database connection error, using test data";
databaseAvailable = false;
}
if (!databaseAvailable) {
qDebug() << "Using test data for demonstration...";
}
qWarning() << "Database not available, no devices loaded.";
qDebug() << "Please ensure MySQL is running and Client database exists with devices table.";
// 数据库不可用时不加载任何设备
// 添加测试数据以便查看效果(当真实数据库不可用时)
DeviceInfo uav1;
uav1.id = "UAV001";
uav1.name = "侦察机-01";
uav1.type = "uav";
uav1.ipAddress = "192.168.1.101";
uav1.port = 8080;
uav1.longitude = 116.40;
uav1.latitude = 39.90;
uav1.signalStrength = 85;
uav1.batteryLevel = 90;
uav1.status = DeviceStatus::Online;
uav1.lastHeartbeat = QDateTime::currentDateTime();
uav1.createdAt = QDateTime::currentDateTime().addDays(-7);
uav1.updatedAt = QDateTime::currentDateTime();
devices.append(uav1);
DeviceInfo uav2;
uav2.id = "UAV002";
uav2.name = "侦察机-02";
uav2.type = "uav";
uav2.ipAddress = "192.168.1.102";
uav2.port = 8080;
uav2.longitude = 116.42;
uav2.latitude = 39.92;
uav2.signalStrength = 72;
uav2.batteryLevel = 65;
uav2.status = DeviceStatus::Warning;
uav2.lastHeartbeat = QDateTime::currentDateTime().addSecs(-30);
uav2.createdAt = QDateTime::currentDateTime().addDays(-5);
uav2.updatedAt = QDateTime::currentDateTime();
devices.append(uav2);
DeviceInfo dog1;
dog1.id = "DOG001";
dog1.name = "巡逻犬-Alpha";
dog1.type = "dog";
dog1.ipAddress = "192.168.1.201";
dog1.port = 9090;
dog1.longitude = 116.38;
dog1.latitude = 39.88;
dog1.signalStrength = 95;
dog1.batteryLevel = 80;
dog1.status = DeviceStatus::Online;
dog1.lastHeartbeat = QDateTime::currentDateTime();
dog1.createdAt = QDateTime::currentDateTime().addDays(-3);
dog1.updatedAt = QDateTime::currentDateTime();
devices.append(dog1);
DeviceInfo dog2;
dog2.id = "DOG002";
dog2.name = "巡逻犬-Beta";
dog2.type = "dog";
dog2.ipAddress = "192.168.1.202";
dog2.port = 9090;
dog2.longitude = 116.44;
dog2.latitude = 39.86;
dog2.signalStrength = 0;
dog2.batteryLevel = 25;
dog2.status = DeviceStatus::Offline;
dog2.lastHeartbeat = QDateTime::currentDateTime().addSecs(-300);
dog2.createdAt = QDateTime::currentDateTime().addDays(-1);
dog2.updatedAt = QDateTime::currentDateTime().addSecs(-120);
devices.append(dog2);
// TODO: 实现真实的数据库查询
// auto uavList = m_uavDatabase->getAllDevices();
// auto dogList = m_dogDatabase->getAllDevices();
}
return devices;
}
@ -640,6 +633,23 @@ void DeviceListPanel::onDeviceCardDetailsRequested(const QString &deviceId)
emit deviceDetailsRequested(deviceId);
}
void DeviceListPanel::onDeviceStatusChanged(const QString &deviceId, DeviceStatus status)
{
qDebug() << "Device status changed signal received:" << deviceId << "status:" << static_cast<int>(status);
// 更新内部设备列表中的状态
for (auto &device : m_allDevices) {
if (device.id == deviceId) {
device.status = status;
qDebug() << "Updated device status in internal list:" << device.name;
break;
}
}
// 更新统计信息显示
updateDeviceCountStats();
}
void DeviceListPanel::setSearchKeyword(const QString &keyword)
{
m_currentSearchKeyword = keyword.trimmed();

@ -28,6 +28,11 @@
#include <QColor>
#include <QApplication>
#include <QDesktopWidget>
// Qt SQL头文件
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QLineEdit>
#include <QTableWidget>
#include <QTableWidgetItem>
@ -321,9 +326,20 @@ void MainWindow::onAddRobotClicked()
QString ip = ipEdit->text().trimmed();
if (!name.isEmpty() && !ip.isEmpty()) {
m_robotList.append(qMakePair(name, ip));
QMessageBox::information(this, "成功", "机器人添加成功!");
dialog->accept();
// 保存到数据库
if (addDeviceToDatabase(name, "dog", ip, 9090, 0)) {
m_robotList.append(qMakePair(name, ip));
QMessageBox::information(this, "成功", "机器人添加成功!");
// 刷新设备列表
if (m_deviceListPanel) {
m_deviceListPanel->refreshDeviceList();
}
dialog->accept();
} else {
QMessageBox::warning(this, "错误", "保存到数据库失败!");
}
} else {
QMessageBox::warning(this, "错误", "请填写完整信息!");
}
@ -407,9 +423,20 @@ void MainWindow::onAddUAVClicked()
QString ip = ipEdit->text().trimmed();
if (!name.isEmpty() && !ip.isEmpty()) {
m_uavList.append(qMakePair(name, ip));
QMessageBox::information(this, "成功", "无人机添加成功!");
dialog->accept();
// 保存到数据库
if (addDeviceToDatabase(name, "uav", ip, 8080, 0)) {
m_uavList.append(qMakePair(name, ip));
QMessageBox::information(this, "成功", "无人机添加成功!");
// 刷新设备列表
if (m_deviceListPanel) {
m_deviceListPanel->refreshDeviceList();
}
dialog->accept();
} else {
QMessageBox::warning(this, "错误", "保存到数据库失败!");
}
} else {
QMessageBox::warning(this, "错误", "请填写完整信息!");
}
@ -684,4 +711,63 @@ void MainWindow::initializeDeviceMarkersOnMap()
break;
}
}
}
bool MainWindow::addDeviceToDatabase(const QString &name, const QString &type, const QString &ip, int port, int state)
{
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "MainWindow_AddDevice_Connection");
db.setHostName("localhost");
db.setPort(3306);
db.setDatabaseName("Client");
db.setUserName("root");
db.setPassword("hzk200407140238");
if (!db.open()) {
qWarning() << "Failed to connect to database for adding device:" << db.lastError().text();
return false;
}
QSqlQuery query(db);
// 生成唯一ID
QString deviceId;
if (type == "uav") {
// 查找现有UAV数量来生成ID
query.exec("SELECT COUNT(*) FROM devices WHERE device_type = 'uav'");
int count = 0;
if (query.next()) {
count = query.value(0).toInt();
}
deviceId = QString("UAV%1").arg(count + 1, 3, 10, QChar('0')); // UAV001, UAV002...
} else if (type == "dog") {
// 查找现有DOG数量来生成ID
query.exec("SELECT COUNT(*) FROM devices WHERE device_type = 'dog'");
int count = 0;
if (query.next()) {
count = query.value(0).toInt();
}
deviceId = QString("DOG%1").arg(count + 1, 3, 10, QChar('0')); // DOG001, DOG002...
}
// 插入新设备
QString sql = "INSERT INTO devices (id, name, device_type, state, ip, port, longitude, latitude, signal_strength, battery_level) "
"VALUES (?, ?, ?, ?, ?, ?, 0.0, 0.0, 0, 100)";
query.prepare(sql);
query.addBindValue(deviceId);
query.addBindValue(name);
query.addBindValue(type);
query.addBindValue(state); // 默认状态为0离线
query.addBindValue(ip);
query.addBindValue(port);
bool success = query.exec();
if (!success) {
qWarning() << "Failed to insert device into database:" << query.lastError().text();
} else {
qDebug() << "Successfully added device to database:" << deviceId << name;
}
db.close();
return success;
}
Loading…
Cancel
Save