关键词:MQTT、物联网、QT、网络连接、远程控制

一、系统概述

本系统是一套完整的智能大棚监控解决方案,由两部分构成:

  1. 基于Hi3861的嵌入式硬件系统(负责环境数据采集和设备控制)
  2. 基于Qt开发的跨平台控制软件(提供可视化界面和远程控制)

该系统实现对大棚环境的全方位监控:

  • ​环境监测系统​​:实时检测温度、湿度、光照强度、CO₂浓度
  • ​安防系统​​:人体检测报警(PIR传感器)
  • ​执行设备​​:LED补光灯控制、通风风扇控制
  • ​双模控制​​:手动控制/自动智能控制模式
  • ​远程监控​​:实时数据面板 + 三态环境状态指示
  • ​异常处理​​:临界值提醒与自动执行保护策略

二、Qt控制界面设计亮点

1. 布局结构(用户登录界面和系统控制界面)

登录界面

2. 动态数据可视化功能

​环境数据卡片设计:​

QWidget* Widget::createDataCard(const QString& title, const QString& unit, const QColor& color)
{// 创建带有彩色边框的数据卡片// 包含标题、实时数值、单位和趋势指示器
}

​趋势变化可视化:​

由于博主的QT版本过低,部分模块及代码无法兼容,所以无法通过绘制温湿度、光照强度的折线图观察环境数据的具体趋势,采用箭头指示​的方式作为系统数据趋势的指示器,当检测到此次数据较上一次采集数据高时箭头指向↑,以此类推。

// 更新趋势指示器(上升↑、下降↓、稳定→)
if (value > prevValue) {trendLabel->setText("↑");trendLabel->setStyleSheet("color: red;");
} else if (value < prevValue) {trendLabel->setText("↓");trendLabel->setStyleSheet("color: blue;");
} else {trendLabel->setText("→");
}
​​​3. 三态预警系统​

我们创新性地采用三色指示灯直观展示环境状态(正常/临界/异常),避免单一阈值判断的局限性:

void Widget::updateStatusIndicator(QWidget* indicator, double value, double min, double max) {QLabel *light = indicator->findChild<QLabel*>("statusLight");if (!light) return;// 正常范围:显示绿色if (value >= min && value <= max) {light->setStyleSheet("border-radius: 15px; background-color: #4CAF50;");} // 临界范围:显示黄色(预留±5缓冲区间)else if ((value >= min-5 && value < min) || (value > max && value <= max+5)) {light->setStyleSheet("border-radius: 15px; background-color: #FFC107;");} // 危险范围:显示红色else {light->setStyleSheet("border-radius: 15px; background-color: #F44336;");}
}

应用场景示例

  • 温度预警:20-28℃正常(绿)|15-19℃或29-33℃警告(黄)|<15℃或>33℃危险(红)
  • 该设计解决了传统二元警报频繁误报问题,大幅提升监控准确性
​4. 人体检测可视化​

采用动态指示灯+状态文字双重提示:

// 提取人体监测数据bool humanDetected = jsonObj.contains("HumanSensor") ? jsonObj["HumanSensor"].toBool() : false;// 更新人体监测指示灯if (humanDetected) {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: green;");humanMonitorStatus->setText("有人");} else {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: red;");humanMonitorStatus->setText("无人");}

安防联动设计

  • 检测到人员时触发警报(可以尝试添加人脸识别,制作检测抓拍记录功能
  • 夜间模式下自动开启补光灯辅助监控

​​​三、ThingsCloud云平台

​1.设备详情

新建设备智能大棚,添加大棚灯、风机、温湿度、人体监测等部分,需要注意的是由QT控制的部分(灯、风机、警报)需要将属性设置为云端下发属性,QT接收的温湿度、光照强度、CO2等设置为设备上报属性

2.设备看板

3.手机端云平台

在ThingsCloud云平台创建自己的UI界面,注意添加对应组件的属性,保存后在扫描用户应用中的微信小程序二维码,登录的手机端云平台界面就是电脑端创建的UI界面,该界面与电脑端界面操作同步。

4.MQTT通信效果

我们在MQTTX创建设备连接,连接云平台设备,在发送如下字符(设备上报)后发现看板组件确实安装发送的温湿度数据配置,同时在看板中打开灯,云端下发了如下字符,证明MQTT通信成功,可以连接。

四、双模式控制机制

​1. 手动模式

用户可直接点击控件按钮,系统实时反馈设备状态:

void Widget::toggleLight1() {if (!autoMode) { // 仅在手动模式响应light1On = !light1On;light1Button->setText(light1On ? "开" : "关");sendData(light1On ? "{\"light0\":true}" : "{\"light0\":false}");}
}

点击按键后,QT根据按键向开发板发送固定字符,开发板通过接收的字符进行判断,控制部分外设进行特定功能。

​2. 自动模式

动态响应环境变化,用户在自动模式下无法直接操作按键,由系统自行控制,同时系统日志显示切换到自动模式。

void Widget::parseEnvironmentData(const QByteArray &data) {if (autoMode) {// 温度>26°C开启风扇,<25°C关闭(滞回控制防抖动)if (temp > 26.0 && !fanOn) sendControlCommand(FAN_ON); else if (temp <= 25.0 && fanOn) sendControlCommand(FAN_OFF);// 光照<80Lux开灯,>90Lux关灯if (lightIntensity < 80 && !light1On) sendControlCommand(LIGHT_ON);else if (lightIntensity >= 90 && light1On) sendControlCommand(LIGHT_OFF);// 人员检测联动报警if (humanDetected) sendControlCommand(ALARM_ON);else if (alarmOn) sendControlCommand(ALARM_OFF);}
}

策略优势

  • 滞回区间设计:防止温度波动导致风扇频繁启停
  • 分级响应:不同设备采用独立阈值策略

​五、通信与算法优化实践​

​1. 高效UDP通信协议​

初始化udp套接字,以及开发板IP地址。注:确保电脑和开发板要连在同一WiFi热点下,通过串口查找开发板设备IP地址,在QT代码中设为连接目标,确保端口号与开发板程序设定的端口号一致(此处设置为9000,设备IP地址为192.168.34.8).​

// 初始化UDPudpSocket = new QUdpSocket(this);if (!udpSocket->bind(QHostAddress::Any, 9000)) {QMessageBox::critical(this, "初始化错误", "UDP套接字绑定失败,请检查端口是否被占用");} else {connect(udpSocket, SIGNAL(readyRead()), this, SLOT(recvData()));}
}
​2. 核心算法优化实例​

解析JSON数据:温湿度,光照,CO2,人体监测状态,判断是否为自动模式,根据解析的数据进行面板显示和状态指示灯显示。​

void Widget::parseEnvironmentData(const QByteArray &data)
{QJsonParseError jsonError;QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);if (jsonError.error == QJsonParseError::NoError && jsonDoc.isObject()) {QJsonObject jsonObj = jsonDoc.object();// 提取温度数据(使用0作为默认值)double temperature = jsonObj.contains("Temp") ? jsonObj["Temp"].toDouble() : 0.0;// 更新温度数据卡片if (tempCard) {QLabel *tempValueLabel = tempCard->findChild<QLabel*>("valueLabel");if (tempValueLabel) {tempValueLabel->setText(QString::number(temperature, 'f', 1));// 更新趋势指示器QLabel *tempTrendLabel = tempCard->findChild<QLabel*>("trendLabel");if (tempTrendLabel) {if (temperature > prevTemp) {tempTrendLabel->setText("↑");tempTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (temperature < prevTemp) {tempTrendLabel->setText("↓");tempTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {tempTrendLabel->setText("→");tempTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevTemp = temperature;}}}// 提取湿度数据double humidity = jsonObj.contains("Humi") ? jsonObj["Humi"].toDouble() : 0.0;if (humCard) {QLabel *humValueLabel = humCard->findChild<QLabel*>("valueLabel");if (humValueLabel) {humValueLabel->setText(QString::number(humidity, 'f', 0));// 更新趋势指示器QLabel *humTrendLabel = humCard->findChild<QLabel*>("trendLabel");if (humTrendLabel) {if (humidity > prevHum) {humTrendLabel->setText("↑");humTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (humidity < prevHum) {humTrendLabel->setText("↓");humTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {humTrendLabel->setText("→");humTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevHum = humidity;}}}// 提取CO₂数据int co2 = jsonObj.contains("CO2") ? jsonObj["CO2"].toInt() : 0;if (co2Card) {QLabel *co2ValueLabel = co2Card->findChild<QLabel*>("valueLabel");if (co2ValueLabel) {co2ValueLabel->setText(QString::number(co2));// 更新趋势指示器QLabel *co2TrendLabel = co2Card->findChild<QLabel*>("trendLabel");if (co2TrendLabel) {if (co2 > prevCO2) {co2TrendLabel->setText("↑");co2TrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (co2 < prevCO2) {co2TrendLabel->setText("↓");co2TrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {co2TrendLabel->setText("→");co2TrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevCO2 = co2;}}}// 提取光照强度数据int lightIntensity = jsonObj.contains("lumen") ? jsonObj["lumen"].toInt() : 0;if (lightCard) {QLabel *lightValueLabel = lightCard->findChild<QLabel*>("valueLabel");if (lightValueLabel) {lightValueLabel->setText(QString::number(lightIntensity));// 更新趋势指示器QLabel *lightTrendLabel = lightCard->findChild<QLabel*>("trendLabel");if (lightTrendLabel) {if (lightIntensity > prevLight) {lightTrendLabel->setText("↑");lightTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (lightIntensity < prevLight) {lightTrendLabel->setText("↓");lightTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {lightTrendLabel->setText("→");lightTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevLight = lightIntensity;}}}// 更新状态指示器updateStatusIndicator(tempStatus, temperature, 20, 28);updateStatusIndicator(humStatus, humidity, 40, 60);updateStatusIndicator(co2Status, co2, 0, 800);updateStatusIndicator(lightStatus, lightIntensity, 80, 100);// 提取人体监测数据bool humanDetected = jsonObj.contains("HumanSensor") ? jsonObj["HumanSensor"].toBool() : false;// 更新人体监测指示灯if (humanDetected) {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: green;");humanMonitorStatus->setText("有人");} else {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: red;");humanMonitorStatus->setText("无人");}// 更新温度数据用于存储if (!temperatureData.isEmpty()) {temperatureData.pop_front();temperatureData.append(temperature);}// 如果是自动模式,执行自动控制逻辑if (autoMode) {// 温度高于26度时开启风扇if (temperature > 26.0 && !fanOn) {sendData("{\"Fan0\":true}");fanOn = true;fanButton->setText("开");fanButton->setChecked(true);}// 温度低于25度时关闭风扇else if (temperature <= 25.0 && fanOn) {sendData("{\"Fan0\":false}");fanOn = false;fanButton->setText("关");fanButton->setChecked(false);}// 检测到有人时开启报警if (humanDetected && !alarmOn) {sendData("{\"Alarm\":true}");alarmOn = true;alarmButton->setText("开");alarmButton->setChecked(true);}// 无人时关闭报警else if (!humanDetected && alarmOn) {sendData("{\"Alarm\":false}");alarmOn = false;alarmButton->setText("关");alarmButton->setChecked(false);}// 光照强度小于100时开启灯1if (lightIntensity < 80 && !light1On) {sendData("{\"light0\":true}");light1On = true;light1Button->setText("开");light1Button->setChecked(true);}// 光照强度大于150时关闭灯1else if (lightIntensity >= 90 && light1On) {sendData("{\"light0\":false}");light1On = false;light1Button->setText("关");light1Button->setChecked(false);}}}
}

​六、代码管理

1. QT登录界面

​实现两种登录模式(用户名密码登录、其他账号登录),这里博主为了省事直接将其他方式写为点击后弹出窗口显示系统维护。。。具体实现方法和用户名密码登录大体一致,感兴趣的朋友可以自行尝试其他方法登录。

#include "loginwidget.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPainter>
#include <QPaintEvent>
#include <QStyleOption>
#include <QMessageBox>
#include <QTimer>
#include <QRadialGradient>
#include "widget.h" // 控制界面头文件
#include <QGroupBox>LoginWidget::LoginWidget(QWidget *parent) : QWidget(parent)
{setWindowTitle("智能大棚控制系统");setFixedSize(1000, 700);setupUI();// 填充示例用户名usernameEdit->setText("");passwordEdit->setText(""); // 实际使用时应该为空
}void LoginWidget::setupUI()
{// 主布局QHBoxLayout *mainLayout = new QHBoxLayout(this);mainLayout->setContentsMargins(0, 0, 0, 0);mainLayout->setSpacing(0);// 左侧装饰区(占位60%宽度)QWidget *leftDecoration = new QWidget(this);leftDecoration->setStyleSheet("background-color: #0f1120;");mainLayout->addWidget(leftDecoration, 6);// 替换左侧装饰区创建代码//leftDecoration->setStyleSheet("background-color: #0f1120;"); // 保留这个以防图片加载失败QVBoxLayout *leftLayout = new QVBoxLayout(leftDecoration);leftLayout->setContentsMargins(0, 0, 0, 0);// 创建图片标签QLabel *bgLabel = new QLabel(leftDecoration);bgLabel->setScaledContents(true); // 设置图片缩放以适应标签leftLayout->addWidget(bgLabel);bgLabel->setPixmap(QPixmap(":/2025.png"));// 右侧登录表单区(占位40%宽度)QWidget *rightForm = new QWidget(this);rightForm->setStyleSheet("background-color: #f9fafb;");QVBoxLayout *formLayout = new QVBoxLayout(rightForm);formLayout->setContentsMargins(70, 100, 70, 100);formLayout->setSpacing(0);mainLayout->addWidget(rightForm, 4);// 标题区域QVBoxLayout *titleLayout = new QVBoxLayout;titleLayout->setSpacing(5);// 欢迎语QLabel *welcomeLabel = new QLabel("你好,xxx");welcomeLabel->setStyleSheet("font-size: 32px; font-weight: bold; color: #2c3e50;");titleLayout->addWidget(welcomeLabel);// 英文标题QLabel *subtitle = new QLabel("Welcome, Wang");subtitle->setStyleSheet("font-size: 20px; color: #7b8a9b; line-height: 1.5;");titleLayout->addWidget(subtitle);titleLayout->addSpacing(10);formLayout->addLayout(titleLayout);// 表单区域QGroupBox *formGroup = new QGroupBox;formGroup->setStyleSheet("QGroupBox { border: none; }");QVBoxLayout *inputLayout = new QVBoxLayout(formGroup);inputLayout->setSpacing(15);// 用户名输入QLabel *usernameLabel = new QLabel("用户名");usernameLabel->setStyleSheet("font-size: 16px; color: #2c3e50;");inputLayout->addWidget(usernameLabel);usernameEdit = new QLineEdit;usernameEdit->setStyleSheet("QLineEdit {""   border: 1px solid #dfe6f0;""   border-radius: 6px;""   padding: 12px;""   font-size: 20px;""   background: white;""}""QLineEdit:focus { border: 1px solid #2196f3; }");inputLayout->addWidget(usernameEdit);// 密码输入QLabel *passwordLabel = new QLabel("密码");passwordLabel->setStyleSheet("font-size: 16px; color: #2c3e50;");inputLayout->addWidget(passwordLabel);passwordEdit = new QLineEdit;passwordEdit->setEchoMode(QLineEdit::Password);passwordEdit->setStyleSheet("QLineEdit {""   border: 1px solid #dfe6f0;""   border-radius: 6px;""   padding: 12px;""   font-size: 14px;""   background: white;""}""QLineEdit:focus { border: 1px solid #2196f3; }");inputLayout->addWidget(passwordEdit);// 密码管理区域QHBoxLayout *passwordOptions = new QHBoxLayout;QCheckBox *rememberCheckbox = new QCheckBox("记住密码");rememberCheckbox->setStyleSheet("color: #7b8a9b;");passwordOptions->addWidget(rememberCheckbox);passwordOptions->addStretch();QLabel *forgotPassword = new QLabel("忘记密码?");forgotPassword->setStyleSheet("font-size: 14px; color: #2196f3; text-decoration: underline;");passwordOptions->addWidget(forgotPassword);inputLayout->addLayout(passwordOptions);inputLayout->addSpacing(15);formLayout->addWidget(formGroup);// 登录按钮loginButton = new QPushButton("登录");loginButton->setFixedHeight(48);loginButton->setStyleSheet("QPushButton {""   background-color: #2196f3;""   color: white;""   border-radius: 6px;""   font-size: 15px;""   font-weight: bold;""}""QPushButton:hover { background-color: #1e88e5; }""QPushButton:pressed { background-color: #1976d2; }");formLayout->addWidget(loginButton);// 第三方登录区域 - 替换为单一按钮QLabel *otherMethods = new QLabel("其他登录方式");otherMethods->setAlignment(Qt::AlignCenter);otherMethods->setStyleSheet("font-size: 14px; color: #7b8a9b; margin: 30px 0 15px 0;");formLayout->addWidget(otherMethods);otherMethods->setAlignment(Qt::AlignCenter);otherMethods->setStyleSheet("font-size: 14px; color: #7b8a9b; margin: 30px 0 15px 0;");formLayout->addWidget(otherMethods);// 创建成都理工大学登录按钮QPushButton *cdutLoginButton = new QPushButton("使用xxxx账号登录");cdutLoginButton->setFixedHeight(48);cdutLoginButton->setStyleSheet("QPushButton {""   background-color: #2196f3;""   color: white;""   border: 1px solid #1d5ab4;""   border-radius: 6px;""   font-size: 14px;""   font-weight: bold;""}""QPushButton:hover {""   background-color: #e3f0ff;""}""QPushButton:pressed {""   background-color: #c7e1ff;""}");formLayout->addWidget(cdutLoginButton);// 连接登录按钮信号// 连接新按钮的信号connect(cdutLoginButton, SIGNAL(clicked()), this, SLOT(onCdutLoginClicked()));connect(loginButton, SIGNAL(clicked()), this, SLOT(onLoginClicked()));
}void LoginWidget::onCdutLoginClicked()
{QMessageBox::information(this, "系统维护", "xxxx统一认证服务正在维护中,请稍后再试");
}void LoginWidget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);
}void LoginWidget::onLoginClicked()
{QString username = usernameEdit->text();QString password = passwordEdit->text();// 禁用登录按钮防止重复点击loginButton->setEnabled(false);loginButton->setText("登录中...");// 添加简单的登录动画效果QTimer::singleShot(1000, this, [=]() {// 检查用户名和密码是否都是"1"if (username == "1" && password == "1") {// 登录成功openControlWidget();this->hide(); // 隐藏登录窗口} else {// 登录失败QMessageBox::warning(this, "登录失败", "用户名或密码错误");// 重置登录按钮状态loginButton->setEnabled(true);loginButton->setText("登录");}});
}void LoginWidget::openControlWidget()
{// 创建控制界面Widget *controlWidget = new Widget();controlWidget->show();// 当控制界面关闭时,重新显示登录窗口connect(controlWidget, &Widget::destroyed, this, [=]() {// 重新启用登录按钮(为下次登录准备)loginButton->setEnabled(true);loginButton->setText("登录");// 重新显示登录窗口this->show();});
}
2. QT用户控制界面
#include "widget.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPainter>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QSplitter>
#include <QTimer>
#include <cmath>
#include <QButtonGroup>
#include <QMessageBox>
#include <QDateTime> // 添加这行Widget::Widget(QWidget *parent) :QWidget(parent),light1On(false), light2On(false), fanOn(false), alarmOn(false),light1Button(nullptr), light2Button(nullptr), fanButton(nullptr), alarmButton(nullptr),udpInput(nullptr), udpTextEdit(nullptr),targetIp("192.168.34.8"),modeGroup(nullptr), manualButton(nullptr), autoButton(nullptr),autoMode(false), // 默认手动模式// 初始化卡片指针tempCard(nullptr), humCard(nullptr), co2Card(nullptr), lightCard(nullptr),// 初始化状态指示器指针tempStatus(nullptr), humStatus(nullptr), co2Status(nullptr), lightStatus(nullptr),// 初始化上一次的值prevTemp(0.0), prevHum(0.0), prevCO2(0), prevLight(0),// 人体监测相关humanMonitorLabel(nullptr), humanMonitorStatus(nullptr)
{setWindowTitle("智能大棚控制系统");resize(1200, 800);setStyleSheet("background-color: #f0f8ff;");// 初始化温度数据为0for(int i = 0; i < 60; i++) {temperatureData.append(0.0);}// 主布局QSplitter *mainSplitter = new QSplitter(Qt::Horizontal, this);// 左侧控制面板mainSplitter->addWidget(createLeftPanel());// 右侧面板(上下分割)QSplitter *rightSplitter = new QSplitter(Qt::Vertical);rightSplitter->addWidget(createRightTopPanel());rightSplitter->addWidget(createRightBottomPanel());mainSplitter->addWidget(rightSplitter);mainSplitter->setStretchFactor(0, 2);  // 左侧占40%mainSplitter->setStretchFactor(1, 3);  // 右侧占60%QHBoxLayout *mainLayout = new QHBoxLayout(this);mainLayout->setContentsMargins(10, 10, 10, 10);mainLayout->addWidget(mainSplitter);// 初始化UDPudpSocket = new QUdpSocket(this);if (!udpSocket->bind(QHostAddress::Any, 9000)) {QMessageBox::critical(this, "初始化错误", "UDP套接字绑定失败,请检查端口是否被占用");} else {connect(udpSocket, SIGNAL(readyRead()), this, SLOT(recvData()));}
}Widget::~Widget()
{if (udpSocket) {udpSocket->close();delete udpSocket;}
}// 创建数据卡片
QWidget* Widget::createDataCard(const QString& title, const QString& unit, const QColor& color)
{QWidget *card = new QWidget;card->setObjectName("dataCard");card->setStyleSheet("QWidget#dataCard {""background-color: white;""border-radius: 10px;""border: 2px solid " + color.name() + ";""}");// 添加阴影效果QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;shadow->setBlurRadius(10);shadow->setColor(QColor(0, 0, 0, 70));shadow->setOffset(0, 2);card->setGraphicsEffect(shadow);QVBoxLayout *layout = new QVBoxLayout(card);layout->setAlignment(Qt::AlignCenter);layout->setSpacing(10);// 标题QLabel *titleLabel = new QLabel(title);titleLabel->setStyleSheet("font-size: 18px; font-weight: bold; color: " + color.name() + ";");titleLabel->setAlignment(Qt::AlignCenter);layout->addWidget(titleLabel);// 值QLabel *valueLabel = new QLabel("0");valueLabel->setObjectName("valueLabel");valueLabel->setStyleSheet("font-size: 32px; font-weight: bold;");valueLabel->setAlignment(Qt::AlignCenter);layout->addWidget(valueLabel);// 单位QLabel *unitLabel = new QLabel(unit);unitLabel->setStyleSheet("font-size: 16px;");unitLabel->setAlignment(Qt::AlignCenter);layout->addWidget(unitLabel);// 趋势指示器(箭头)QLabel *trendLabel = new QLabel("→");trendLabel->setObjectName("trendLabel");trendLabel->setStyleSheet("font-size: 24px;");trendLabel->setAlignment(Qt::AlignCenter);layout->addWidget(trendLabel);return card;
}// 创建状态指示器
QWidget* Widget::createStatusIndicator(const QString& name, const QString& color)
{QWidget *indicator = new QWidget;indicator->setObjectName("statusIndicator");indicator->setStyleSheet("QWidget#statusIndicator {""background-color: white;""border: 1px solid " + color + ";""border-radius: 10px;""}");// 添加阴影效果QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;shadow->setBlurRadius(8);shadow->setColor(QColor(0, 0, 0, 50));shadow->setOffset(0, 2);indicator->setGraphicsEffect(shadow);QVBoxLayout *layout = new QVBoxLayout(indicator);layout->setAlignment(Qt::AlignCenter);layout->setSpacing(10);// 状态灯QLabel *light = new QLabel;light->setObjectName("statusLight");light->setFixedSize(30, 30);light->setStyleSheet("border-radius: 15px; background-color: gray;");layout->addWidget(light, 0, Qt::AlignHCenter);// 名称标签QLabel *nameLabel = new QLabel(name);nameLabel->setStyleSheet("font-size: 16px; color: " + color + "; font-weight: bold;");nameLabel->setAlignment(Qt::AlignCenter);layout->addWidget(nameLabel);return indicator;
}void Widget::recvData()
{while (udpSocket && udpSocket->hasPendingDatagrams()) {// 接收数据报QByteArray datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);// 记录接收信息QString message = QString("[%1] From %2:%3 - %4").arg(QDateTime::currentDateTime().toString("hh:mm:ss")).arg(sender.toString()).arg(senderPort).arg(QString::fromUtf8(datagram));// 在UI中显示if (udpTextEdit) {udpTextEdit->append(message);}// 解析环境数据parseEnvironmentData(datagram);}
}void Widget::parseEnvironmentData(const QByteArray &data)
{QJsonParseError jsonError;QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);if (jsonError.error == QJsonParseError::NoError && jsonDoc.isObject()) {QJsonObject jsonObj = jsonDoc.object();// 提取温度数据(使用0作为默认值)double temperature = jsonObj.contains("Temp") ? jsonObj["Temp"].toDouble() : 0.0;// 更新温度数据卡片if (tempCard) {QLabel *tempValueLabel = tempCard->findChild<QLabel*>("valueLabel");if (tempValueLabel) {tempValueLabel->setText(QString::number(temperature, 'f', 1));// 更新趋势指示器QLabel *tempTrendLabel = tempCard->findChild<QLabel*>("trendLabel");if (tempTrendLabel) {if (temperature > prevTemp) {tempTrendLabel->setText("↑");tempTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (temperature < prevTemp) {tempTrendLabel->setText("↓");tempTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {tempTrendLabel->setText("→");tempTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevTemp = temperature;}}}// 提取湿度数据double humidity = jsonObj.contains("Humi") ? jsonObj["Humi"].toDouble() : 0.0;if (humCard) {QLabel *humValueLabel = humCard->findChild<QLabel*>("valueLabel");if (humValueLabel) {humValueLabel->setText(QString::number(humidity, 'f', 0));// 更新趋势指示器QLabel *humTrendLabel = humCard->findChild<QLabel*>("trendLabel");if (humTrendLabel) {if (humidity > prevHum) {humTrendLabel->setText("↑");humTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (humidity < prevHum) {humTrendLabel->setText("↓");humTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {humTrendLabel->setText("→");humTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevHum = humidity;}}}// 提取CO₂数据int co2 = jsonObj.contains("CO2") ? jsonObj["CO2"].toInt() : 0;if (co2Card) {QLabel *co2ValueLabel = co2Card->findChild<QLabel*>("valueLabel");if (co2ValueLabel) {co2ValueLabel->setText(QString::number(co2));// 更新趋势指示器QLabel *co2TrendLabel = co2Card->findChild<QLabel*>("trendLabel");if (co2TrendLabel) {if (co2 > prevCO2) {co2TrendLabel->setText("↑");co2TrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (co2 < prevCO2) {co2TrendLabel->setText("↓");co2TrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {co2TrendLabel->setText("→");co2TrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevCO2 = co2;}}}// 提取光照强度数据int lightIntensity = jsonObj.contains("lumen") ? jsonObj["lumen"].toInt() : 0;if (lightCard) {QLabel *lightValueLabel = lightCard->findChild<QLabel*>("valueLabel");if (lightValueLabel) {lightValueLabel->setText(QString::number(lightIntensity));// 更新趋势指示器QLabel *lightTrendLabel = lightCard->findChild<QLabel*>("trendLabel");if (lightTrendLabel) {if (lightIntensity > prevLight) {lightTrendLabel->setText("↑");lightTrendLabel->setStyleSheet("color: red; font-size: 24px;");} else if (lightIntensity < prevLight) {lightTrendLabel->setText("↓");lightTrendLabel->setStyleSheet("color: blue; font-size: 24px;");} else {lightTrendLabel->setText("→");lightTrendLabel->setStyleSheet("color: gray; font-size: 24px;");}prevLight = lightIntensity;}}}// 更新状态指示器updateStatusIndicator(tempStatus, temperature, 20, 28);updateStatusIndicator(humStatus, humidity, 40, 60);updateStatusIndicator(co2Status, co2, 0, 800);updateStatusIndicator(lightStatus, lightIntensity, 80, 100);// 提取人体监测数据bool humanDetected = jsonObj.contains("HumanSensor") ? jsonObj["HumanSensor"].toBool() : false;// 更新人体监测指示灯if (humanDetected) {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: green;");humanMonitorStatus->setText("有人");} else {humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: red;");humanMonitorStatus->setText("无人");}// 更新温度数据用于存储if (!temperatureData.isEmpty()) {temperatureData.pop_front();temperatureData.append(temperature);}// 如果是自动模式,执行自动控制逻辑if (autoMode) {// 温度高于26度时开启风扇if (temperature > 26.0 && !fanOn) {sendData("{\"Fan0\":true}");fanOn = true;fanButton->setText("开");fanButton->setChecked(true);}// 温度低于25度时关闭风扇else if (temperature <= 25.0 && fanOn) {sendData("{\"Fan0\":false}");fanOn = false;fanButton->setText("关");fanButton->setChecked(false);}// 检测到有人时开启报警if (humanDetected && !alarmOn) {sendData("{\"Alarm\":true}");alarmOn = true;alarmButton->setText("开");alarmButton->setChecked(true);}// 无人时关闭报警else if (!humanDetected && alarmOn) {sendData("{\"Alarm\":false}");alarmOn = false;alarmButton->setText("关");alarmButton->setChecked(false);}// 光照强度小于100时开启灯1if (lightIntensity < 80 && !light1On) {sendData("{\"light0\":true}");light1On = true;light1Button->setText("开");light1Button->setChecked(true);}// 光照强度大于150时关闭灯1else if (lightIntensity >= 90 && light1On) {sendData("{\"light0\":false}");light1On = false;light1Button->setText("关");light1Button->setChecked(false);}}}
}void Widget::updateStatusIndicator(QWidget* indicator, double value, double min, double max)
{QLabel *light = indicator ? indicator->findChild<QLabel*>("statusLight") : nullptr;if (!light) return;// 定义警告范围(临界范围)的边界double warnLow = min - 5;  // 低于正常范围5个单位视为临界double warnHigh = max + 5; // 高于正常范围5个单位视为临界// 确定颜色(红-黄-绿)if (value >= min && value <= max) {// 在正常范围内,显示绿色light->setStyleSheet("border-radius: 15px; background-color: green;");} else if ((value >= warnLow && value < min) || (value > max && value <= warnHigh)) {// 在临界范围内,显示黄色light->setStyleSheet("border-radius: 15px; background-color: yellow;");} else {// 超出临界范围,显示红色light->setStyleSheet("border-radius: 15px; background-color: red;");}
}void Widget::sendData(const QString& data)
{if (udpSocket) {QByteArray byteArray = data.toUtf8();udpSocket->writeDatagram(byteArray, QHostAddress(targetIp), 9000);}
}void Widget::sendData(const char* data)
{if (udpSocket) {QByteArray byteArray(data);udpSocket->writeDatagram(byteArray, QHostAddress(targetIp), 9000);}
}void Widget::sendManualData()
{if (udpInput && !udpInput->text().isEmpty()) {sendData(udpInput->text());udpInput->clear();}
}void Widget::toggleLight1()
{if (!light1Button || !udpSocket) return;// 手动模式下才响应按钮点击if (!autoMode) {light1On = !light1On;// 更新按钮显示状态light1Button->setText(light1On ? "开" : "关");light1Button->setChecked(light1On);// 发送控制命令sendData(light1On ? "{\"light0\":true}" : "{\"light0\":false}");QMessageBox::information(this, "灯1控制", light1On ? "灯1已开启" : "灯1已关闭");}
}void Widget::toggleLight2()
{if (!light2Button || !udpSocket) return;// 手动模式下才响应按钮点击if (!autoMode) {light2On = !light2On;// 更新按钮显示状态light2Button->setText(light2On ? "开" : "关");light2Button->setChecked(light2On);sendData(light2On ? "{\"light1\":true}" : "{\"light1\":false}");QMessageBox::information(this, "灯2控制", light2On ? "灯2已开启" : "灯2已关闭");}
}void Widget::toggleFan()
{if (!fanButton || !udpSocket) return;// 手动模式下才响应按钮点击if (!autoMode) {fanOn = !fanOn;// 更新按钮显示状态fanButton->setText(fanOn ? "开" : "关");fanButton->setChecked(fanOn);sendData(fanOn ? "{\"Fan0\":true}" : "{\"Fan0\":false}");QMessageBox::information(this, "风扇控制", fanOn ? "风扇已启用" : "风扇已禁用");}
}void Widget::toggleAlarm()
{if (!alarmButton || !udpSocket) return;// 手动模式下才响应按钮点击if (!autoMode) {alarmOn = !alarmOn;// 更新按钮显示状态alarmButton->setText(alarmOn ? "开" : "关");alarmButton->setChecked(alarmOn);// 发送控制命令sendData(alarmOn ? "{\"Alarm\":true}" : "{\"Alarm\":false}");QMessageBox::information(this, "报警控制", alarmOn ? "报警已启用" : "报警已禁用");}
}// 切换到手动模式
void Widget::setManualMode()
{autoMode = false;manualButton->setStyleSheet("background-color: #42a5f5; color: white; border-radius: 5px; padding: 8px;");autoButton->setStyleSheet("background-color: #e0e0e0; color: #757575; border-radius: 5px; padding: 8px;");// 启用手动控制按钮light1Button->setEnabled(true);light2Button->setEnabled(true);fanButton->setEnabled(true);alarmButton->setEnabled(true);
}// 切换到自动模式
void Widget::setAutoMode()
{autoMode = true;manualButton->setStyleSheet("background-color: #e0e0e0; color: #757575; border-radius: 5px; padding: 8px;");autoButton->setStyleSheet("background-color: #42a5f5; color: white; border-radius: 5px; padding: 8px;");// 禁用手动控制按钮light1Button->setEnabled(false);light2Button->setEnabled(false);fanButton->setEnabled(false);alarmButton->setEnabled(false);// 在状态栏显示切换到自动模式udpTextEdit->append(QString("[%1] 系统切换到自动模式").arg(QDateTime::currentDateTime().toString("hh:mm:ss")));
}QWidget* Widget::createLeftPanel()
{QWidget *panel = new QWidget;panel->setStyleSheet("background-color: white; border-radius: 8px;");QVBoxLayout *layout = new QVBoxLayout(panel);layout->setContentsMargins(15, 15, 15, 15);layout->setSpacing(15);// 标题区域QLabel *title = new QLabel("设备控制");title->setAlignment(Qt::AlignCenter);title->setStyleSheet("font-size: 22px; font-weight: bold; color: #42a5f5; margin-bottom: 15px;");layout->addWidget(title);// 添加手动/自动模式切换按钮QWidget *modeWidget = new QWidget;QHBoxLayout *modeLayout = new QHBoxLayout(modeWidget);modeLayout->setContentsMargins(0, 0, 0, 0);modeGroup = new QButtonGroup(this);manualButton = new QPushButton("手动模式");manualButton->setCheckable(true);manualButton->setChecked(true); // 默认手动模式manualButton->setStyleSheet("background-color: #42a5f5; color: white; border-radius: 5px; padding: 8px;");connect(manualButton, SIGNAL(clicked()), this, SLOT(setManualMode()));autoButton = new QPushButton("自动模式");autoButton->setCheckable(true);autoButton->setStyleSheet("background-color: #e0e0e0; color: #757575; border-radius: 5px; padding: 8px;");connect(autoButton, SIGNAL(clicked()), this, SLOT(setAutoMode()));modeGroup->addButton(manualButton);modeGroup->addButton(autoButton);modeLayout->addWidget(manualButton);modeLayout->addWidget(autoButton);layout->addWidget(modeWidget);// ====== LED控制部分 ======QGroupBox *ledGroup = new QGroupBox("LED控制");ledGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *ledLayout = new QVBoxLayout(ledGroup);// LED开关选项QWidget *ledControl = new QWidget;QHBoxLayout *ledControlLayout = new QHBoxLayout(ledControl);ledControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *ledSwitchLabel = new QLabel("灯1开关");ledSwitchLabel->setStyleSheet("font-size: 30px;");// LED开关按钮light1Button = new QPushButton("关");light1Button->setCheckable(true);light1Button->setStyleSheet("QPushButton {""   background-color: #42a5f5;""   color: white;""   border-radius: 5px;""   padding: 6px;""   font-size: 30px;""   min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(light1Button, SIGNAL(clicked()), this, SLOT(toggleLight1()));ledControlLayout->addWidget(ledSwitchLabel);ledControlLayout->addStretch();ledControlLayout->addWidget(light1Button);ledLayout->addWidget(ledControl);// 灯2开关QWidget *led2Control = new QWidget;QHBoxLayout *led2ControlLayout = new QHBoxLayout(led2Control);led2ControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *led2SwitchLabel = new QLabel("灯2开关");led2SwitchLabel->setStyleSheet("font-size: 30px;");light2Button = new QPushButton("关");light2Button->setCheckable(true);light2Button->setStyleSheet("QPushButton {""   background-color: #42a5f5;""   color: white;""   border-radius: 5px;""   padding: 6px;""   font-size: 30px;""   min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(light2Button, SIGNAL(clicked()), this, SLOT(toggleLight2()));led2ControlLayout->addWidget(led2SwitchLabel);led2ControlLayout->addStretch();led2ControlLayout->addWidget(light2Button);ledLayout->addWidget(led2Control);layout->addWidget(ledGroup);// ====== 风扇控制 ======QGroupBox *fanGroup = new QGroupBox("风扇控制");fanGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *fanLayout = new QVBoxLayout(fanGroup);// 风扇开关QWidget *fanControl = new QWidget;QHBoxLayout *fanControlLayout = new QHBoxLayout(fanControl);fanControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *fanSwitchLabel = new QLabel("风扇开关");fanSwitchLabel->setStyleSheet("font-size: 30px;");fanButton = new QPushButton("关");fanButton->setCheckable(true);fanButton->setStyleSheet("QPushButton {""   background-color: #42a5f5;""   color: white;""   border-radius: 5px;""   padding: 6px;""   font-size: 30px;""   min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(fanButton, SIGNAL(clicked()), this, SLOT(toggleFan()));fanControlLayout->addWidget(fanSwitchLabel);fanControlLayout->addStretch();fanControlLayout->addWidget(fanButton);fanLayout->addWidget(fanControl);layout->addWidget(fanGroup);// ====== 报警控制 ======QGroupBox *alarmGroup = new QGroupBox("报警控制");alarmGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *alarmLayout = new QVBoxLayout(alarmGroup);QWidget *alarmControl = new QWidget;QHBoxLayout *alarmControlLayout = new QHBoxLayout(alarmControl);alarmControlLayout->setContentsMargins(0, 0, 0, 0);QLabel *alarmSwitchLabel = new QLabel("报警开关");alarmSwitchLabel->setStyleSheet("font-size: 30px;");alarmButton = new QPushButton("关");alarmButton->setCheckable(true);alarmButton->setStyleSheet("QPushButton {""   background-color: #42a5f5;""   color: white;""   border-radius: 5px;""   padding: 6px;""   font-size: 30px;""   min-width: 50px;""}""QPushButton:checked { background-color: #66bb6a; }");connect(alarmButton, SIGNAL(clicked()), this, SLOT(toggleAlarm()));alarmControlLayout->addWidget(alarmSwitchLabel);alarmControlLayout->addStretch();alarmControlLayout->addWidget(alarmButton);alarmLayout->addWidget(alarmControl);layout->addWidget(alarmGroup);// ====== UDP手动发送区域 ======QGroupBox *udpGroup = new QGroupBox("UDP手动发送");udpGroup->setStyleSheet("QGroupBox { border: 1px solid #a3d1f5; border-radius: 8px; padding: 10px; }""QGroupBox::title { color: #42a5f5; font-weight: bold; font-size: 16px; }");QVBoxLayout *udpLayout = new QVBoxLayout(udpGroup);QHBoxLayout *inputLayout = new QHBoxLayout;udpInput = new QLineEdit;udpInput->setPlaceholderText("输入要发送的数据");udpInput->setStyleSheet("font-size: 20px; padding: 8px;");QPushButton *sendButton = new QPushButton("发送");sendButton->setStyleSheet("QPushButton {""   background-color: #42a5f5;""   color: white;""   border-radius: 5px;""   padding: 8px;""   font-size: 20px;""}""QPushButton:hover { background-color: #1e88e5; }");connect(sendButton, SIGNAL(clicked()), this, SLOT(sendManualData()));inputLayout->addWidget(udpInput);inputLayout->addWidget(sendButton);udpLayout->addLayout(inputLayout);// UDP接收数据显示udpTextEdit = new QTextEdit;udpTextEdit->setReadOnly(true);udpTextEdit->setStyleSheet("font-size: 14px; background-color: #f8f8f8; border: 1px solid #ddd;");udpLayout->addWidget(udpTextEdit);layout->addWidget(udpGroup);layout->addStretch();return panel;
}QWidget* Widget::createRightTopPanel()
{// 环境监测面板QWidget *panel = new QWidget;panel->setStyleSheet("background-color: #f0f8ff; border-radius: 8px; padding: 10px;");QGridLayout *layout = new QGridLayout(panel);layout->setContentsMargins(10, 10, 10, 10);layout->setHorizontalSpacing(15);layout->setVerticalSpacing(15);// 数据卡片tempCard = createDataCard("温度", "°C", "#ff7043"); // 橙色layout->addWidget(tempCard, 0, 0);humCard = createDataCard("湿度", "%", "#42a5f5");   // 蓝色layout->addWidget(humCard, 0, 1);co2Card = createDataCard("CO₂", "ppm", "#66bb6a"); // 绿色layout->addWidget(co2Card, 0, 2);lightCard = createDataCard("光照", "lx", "#ffd700"); // 金色layout->addWidget(lightCard, 1, 0);// 人体监测部分QWidget *humanMonitorWidget = new QWidget;humanMonitorWidget->setObjectName("humanMonitorContainer");humanMonitorWidget->setStyleSheet("QWidget#humanMonitorContainer {""background-color: white;""border: 2px solid #9c27b0;""border-radius: 10px;""}");QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect;shadow->setBlurRadius(10);shadow->setColor(QColor(0, 0, 0, 70));shadow->setOffset(0, 2);humanMonitorWidget->setGraphicsEffect(shadow);QVBoxLayout *humanLayout = new QVBoxLayout(humanMonitorWidget);humanLayout->setAlignment(Qt::AlignCenter);humanLayout->setSpacing(8);// 指示灯humanMonitorLabel = new QLabel();humanMonitorLabel->setFixedSize(30, 30);humanMonitorLabel->setStyleSheet("border-radius: 15px; background-color: gray;");humanLayout->addWidget(humanMonitorLabel, 0, Qt::AlignHCenter);// 标题QLabel *humanTitle = new QLabel("人体监测");humanTitle->setStyleSheet("font-size: 18px; font-weight: bold; color: #9c27b0;");humanTitle->setAlignment(Qt::AlignCenter);humanLayout->addWidget(humanTitle);// 状态humanMonitorStatus = new QLabel("无人");humanMonitorStatus->setObjectName("statusText");humanMonitorStatus->setStyleSheet("font-size: 16px; font-weight: bold;");humanMonitorStatus->setAlignment(Qt::AlignCenter);humanLayout->addWidget(humanMonitorStatus);// 添加人体监测到网格(横跨两列)layout->addWidget(humanMonitorWidget, 1, 1, 1, 2);return panel;
}QWidget* Widget::createRightBottomPanel()
{// 状态监测面板QWidget *panel = new QWidget;panel->setStyleSheet("background-color: #f0f8ff; border-radius: 8px; padding: 15px;");QVBoxLayout *mainLayout = new QVBoxLayout(panel);mainLayout->setContentsMargins(10, 10, 10, 10);// 标题QLabel *title = new QLabel("环境状态监测");title->setAlignment(Qt::AlignCenter);title->setStyleSheet("font-size: 22px; font-weight: bold; color: #3a4a6b;");mainLayout->addWidget(title);// 状态灯网格布局QGridLayout *gridLayout = new QGridLayout;gridLayout->setHorizontalSpacing(20);gridLayout->setVerticalSpacing(20);gridLayout->setContentsMargins(20, 20, 20, 20);// 创建状态指示器tempStatus = createStatusIndicator("温度", "#ff7043"); // 橙色gridLayout->addWidget(tempStatus, 0, 0);humStatus = createStatusIndicator("湿度", "#42a5f5");   // 蓝色gridLayout->addWidget(humStatus, 0, 1);co2Status = createStatusIndicator("CO₂", "#66bb6a");   // 绿色gridLayout->addWidget(co2Status, 0, 2);lightStatus = createStatusIndicator("光照", "#ffd700"); // 金色gridLayout->addWidget(lightStatus, 1, 1);// 添加状态灯到主布局QWidget *statusContainer = new QWidget;statusContainer->setLayout(gridLayout);mainLayout->addWidget(statusContainer);// 添加图例说明QWidget *legendWidget = new QWidget;QHBoxLayout *legendLayout = new QHBoxLayout(legendWidget);legendLayout->setSpacing(15);QLabel *greenLegend = new QLabel;greenLegend->setFixedSize(20, 20);greenLegend->setStyleSheet("background-color: green; border-radius: 10px;");legendLayout->addWidget(greenLegend);legendLayout->addWidget(new QLabel("正常"));QLabel *yellowLegend = new QLabel;yellowLegend->setFixedSize(20, 20);yellowLegend->setStyleSheet("background-color: yellow; border-radius: 10px;");legendLayout->addWidget(yellowLegend);legendLayout->addWidget(new QLabel("临界"));QLabel *redLegend = new QLabel;redLegend->setFixedSize(20, 20);redLegend->setStyleSheet("background-color: red; border-radius: 10px;");legendLayout->addWidget(redLegend);legendLayout->addWidget(new QLabel("异常"));legendLayout->addStretch();mainLayout->addWidget(legendWidget, 0, Qt::AlignCenter);return panel;
}void Widget::paintEvent(QPaintEvent *event)
{Q_UNUSED(event);  // 不使用event参数// 在这里添加绘制代码QWidget::paintEvent(event);
}
3. HI3861主程序(部分)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_io.h"      // 海思 Pegasus SDK:IoT硬件设备操作接口-IO
#include "hi_time.h"
#include "hi_watchdog.h"
#include "hi_gpio.h"
// 海思 Pegasus SDK:IoT硬件设备操作接口-ADC
// Analog-to-digital conversion (ADC)
// 提供8个ADC通道,通道7为参考电压,不能adc转换
#include "hi_adc.h"
#include "hi_watchdog.h"
// 定义一个宏,用于标识ADC6通道,GPIO13管脚复用
#define LIGHT_SENSOR_CHAN_NAME HI_ADC_CHANNEL_3
#define LuMen_SENSOR_CHAN_NAME HI_ADC_CHANNEL_6#include "m_wifi.h"
#include "m_mqtt.h"#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "lwip/api_shell.h"#include "m_key.h"
#include "m_led.h"
#include "m_dht11.h"
#include "oled_ssd1306.h"   // OLED显示屏简化版驱动接口文件
#include "oled_fonts.h"//控制任务
#define WIFI_SSID "iQOO11"
#define WIFI_PAWD "88888888"#define SERVER_IP_ADDR "82.157.254.205"    //broker.emqx.io //54.244.173.190
#define SERVER_IP_PORT 1883
#define MQTT_TOPIC_SUB "attributes/push"
#define MQTT_TOPIC_PUB "attributes"
#define TASK_INIT_TIME 2 // s
#define MQTT_RECV_TASK_TIME (200 * 1000) // us#define MQTT_USERNAME  "tpqacbx5s052il1p"
#define MQTT_PASSWORD  "kqy3EoVZUZ"#define BEEP_PIN         HI_IO_NAME_GPIO_10 // for hispark_pegasus
#define BEEP_FUN_GPIO    HI_IO_FUNC_GPIO_10_GPIO
#define HUMANSENSOR_PIN  	   HI_IO_NAME_GPIO_14
#define HUMANSENSOR_FUN_GPIO  HI_IO_FUNC_GPIO_14_GPIO
#define DUOJI_PIN  	   HI_IO_NAME_GPIO_2
#define DUOJI_FUN_GPIO  HI_IO_FUNC_GPIO_2_GPIO
static volatile  hi_gpio_value g_duojiPinValue = 0;//-------------------------------------------------------------------------------------
int result;  
uint8_t temp;  	    
uint8_t humi;
uint8_t co2;
uint8_t Lum;
uint8_t renne;
char recvBuf[512];
int human_flag = 0;
hi_gpio_value human_sensor_val=HI_GPIO_VALUE0;int socket_fd = 0 ;           // 套接字文件描述符
int result;                  // 函数返回值
char sendBuf[512];           // 发送缓冲区
struct sockaddr_in client_addr;
socklen_t addr_length; osThreadId_t mqtt_send_task_id;   // mqtt订阅数据任务
osThreadId_t mqtt_recv_task_id;   // mqtt发布数据任务//人传感器初始话
void human_sensor_init(void)
{hi_gpio_init();//初始化gpio管脚hi_io_set_pull(HUMANSENSOR_PIN,HI_IO_PULL_DOWN);//设置管脚下拉hi_io_set_func(HUMANSENSOR_PIN,HUMANSENSOR_FUN_GPIO);//设置复用功能为通用输入输出功能hi_gpio_set_dir(HUMANSENSOR_PIN,HI_GPIO_DIR_IN);//设置管脚为输出
}void udp_judge()
{if(strcmp(recvBuf,"{\"light0\":true}")==0)//大棚灯0 开{printf("--- 大棚灯0 开 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE1);}else if(strcmp(recvBuf,"{\"light0\":false}")==0)//大棚灯0 关{printf("--- 大棚灯0 关 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE0);}if(strcmp(recvBuf,"{\"light1\":true}")==0)//大棚灯1 开{printf("--- 大棚灯1 开 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE1);}else if(strcmp(recvBuf,"{\"light1\":false}")==0)//大棚灯1 关{printf("--- 大棚灯1 关 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE0);}if(strcmp(recvBuf,"{\"Alarm\":true}")==0)//报警 开{printf("--- 报警 开 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE1);led_ctl(BEEP_PIN, HI_GPIO_VALUE0);//开蜂鸣器}else if(strcmp(recvBuf,"{\"Alarm\":false}")==0)//报警 关{printf("--- 报警 关 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE0);led_ctl(BEEP_PIN, HI_GPIO_VALUE1);//关蜂鸣器}if(strcmp(recvBuf,"{\"Fan0\":true}")==0)//风扇 开{printf("--- 风扇 开 ---\r\n");g_duojiPinValue=1;}else if(strcmp(recvBuf,"{\"Fan0\":false}")==0)//风扇 关{printf("--- 风扇 关 ---\r\n");g_duojiPinValue=0;}
}
//订阅的回调函数
int8_t mqtt_sub_payload_callback(unsigned char *topic, char *payload)
{printf("[info] topic:[%s]    recv<== **%s**\r\n", topic, payload);//判断订阅收到的消息内容/** 蓝色灯代表 大棚灯0 ,绿色灯代表大棚灯1,红色灯代表报警*/if(strcmp(payload,"{\"light0\":true}")==0)//大棚灯0 开{printf("--- 大棚灯0 开 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE1);}else if(strcmp(payload,"{\"light0\":false}")==0)//大棚灯0 关{printf("--- 大棚灯0 关 ---\r\n");led_ctl(LED_BLUE_GPIO, HI_GPIO_VALUE0);}if(strcmp(payload,"{\"light1\":true}")==0)//大棚灯1 开{printf("--- 大棚灯1 开 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE1);}else if(strcmp(payload,"{\"light1\":false}")==0)//大棚灯1 关{printf("--- 大棚灯1 关 ---\r\n");led_ctl(LED_GREEN_GPIO, HI_GPIO_VALUE0);}if(strcmp(payload,"{\"Alarm\":true}")==0)//报警 开{printf("--- 报警 开 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE1);led_ctl(BEEP_PIN, HI_GPIO_VALUE0);//开蜂鸣器}else if(strcmp(payload,"{\"Alarm\":false}")==0)//报警 关{printf("--- 报警 关 ---\r\n");led_ctl(LED_RED_GPIO, HI_GPIO_VALUE0);led_ctl(BEEP_PIN, HI_GPIO_VALUE1);//关蜂鸣器}if(strcmp(payload,"{\"Fan0\":true}")==0)//风扇 开{printf("--- 风扇 开 ---\r\n");g_duojiPinValue=1;}else if(strcmp(payload,"{\"Fan0\":false}")==0)//风扇 关{printf("--- 风扇 关 ---\r\n");g_duojiPinValue=0;}}//接收任务
void mqtt_recv_task(void)
{while (1) {MQTTClient_sub();usleep(MQTT_RECV_TASK_TIME);}
}//任务1---------------------------------------------------------------------------
void mqtt_send_task(void)
{uint8_t res=0;// 连接Wifiif (WiFi_connectHotspots(WIFI_SSID, WIFI_PAWD) != WIFI_SUCCESS) {printf("[error] WiFi_connectHotspots\r\n");}// 连接MQTT服务器if (MQTTClient_connectServer(SERVER_IP_ADDR, SERVER_IP_PORT) != 0) {printf("[error] MQTTClient_connectServer\r\n");} else {printf("[success] MQTTClient_connectServer\r\n");}sleep(TASK_INIT_TIME);// 初始化MQTT客户端if (MQTTClient_init("mqtt_client_123", MQTT_USERNAME, MQTT_PASSWORD) != 0) {printf("[error] MQTTClient_init\r\n");} else {printf("[success] MQTTClient_init\r\n");}sleep(TASK_INIT_TIME);// 订阅Topic(增加重试)
int retry = 0;
while (MQTTClient_subscribe(MQTT_TOPIC_SUB) != 0 && retry < 3) {printf("[retry %d] MQTTClient_subscribe...\n", retry+1);sleep(1);retry++;
}
if (retry >= 3) {printf("[error] MQTT订阅最终失败!\n");
} else {printf("[success] MQTT订阅成功!\n");
}sleep(TASK_INIT_TIME); osThreadAttr_t options;options.name = "mqtt_recv_task";options.attr_bits = 0;options.cb_mem = NULL;options.cb_size = 0;options.stack_mem = NULL;options.stack_size = 1024*5;options.priority = osPriorityNormal;mqtt_recv_task_id = osThreadNew((osThreadFunc_t)mqtt_recv_task, NULL, &options);if (mqtt_recv_task_id != NULL) {printf("ID = %d, Create mqtt_recv_task_id is OK!\r\n", mqtt_recv_task_id);}while (1) {//MQTTClient_pub(MQTT_TOPIC_PUB, "hello world!!!", strlen("hello world!!!"));// global_temp=temp;// global_humi=humi;DHT11_Read_Data(&temp,&humi);printf("温度=%d°C  湿度=%d%%RH\r\n",temp,humi);sleep(TASK_INIT_TIME);}
}
//任务2-------------------------------------------------------------------------------
static void AdcTask(void *arg)
{// 工作循环,每隔100ms获取一次ADC6通道的值while (1) {// 保存ADC6通道的值unsigned short data = 0;// 获取ADC6通道的值// 读数速率较慢,请避免在中断中使用if (hi_adc_read(LIGHT_SENSOR_CHAN_NAME, &data, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAIS_DEFAULT, 0)== HI_ERR_SUCCESS) {// LIGHT_SENSOR_CHAN_NAME表示ADC4通道// HI_ADC_EQU_MODEL_4表示采样数据平均滤波处理,平均次数4次// HI_ADC_CUR_BAIS_DEFAULT表示模数转换采用默认电压基准// 0表示从配置采样到启动采样的延时时间计数为0// 返回值HI_ERR_SUCCESS表示成功// 打印ADC6通道的值//printf("ADC_VALUE = %d\n", (unsigned int)data);// 打印测量电压的值//printf("电压 = %.2f V\n", data*1.8*4/4096);}co2=(unsigned int)data;// 等待100msosDelay(10);hi_watchdog_feed();}
}
//任务3
void *HUMANSENSORTASK(void *arg)
{human_sensor_init();//hi_watchdog_enable();while(1){hi_gpio_get_input_val(HUMANSENSOR_PIN, &human_sensor_val);//让LED3_PIN输出高电平,led3亮if(human_sensor_val == HI_GPIO_VALUE1){osDelay(20);hi_gpio_get_input_val(HUMANSENSOR_PIN,&human_sensor_val);if(human_sensor_val == HI_GPIO_VALUE1){//printf("检测到有人\n");}}if(human_flag != human_sensor_val){human_flag = human_sensor_val;if(human_flag == 0){renne=0;printf("检测到无人了!!!\n");}else{renne=1;printf("检测到有人了!!!\n");}}osDelay(100);//hi_watchdog_feed();}
}
//任务4
void duoji_init(void)
{hi_gpio_init();//初始化gpio管脚hi_io_set_func(DUOJI_PIN,DUOJI_FUN_GPIO);//设置DUOJI_PIN 复用功能为通用输入输出功能hi_gpio_set_dir(DUOJI_PIN,HI_GPIO_DIR_OUT);//设置DUOJI_PIN管脚为输出
}
void *DUOJITASK(void *arg)
{int count = 0;uint16_t m_time = 1000;uint16_t l_time = 1500;uint16_t s_time = 4000;duoji_init();while(1)// 实现舵机旋转{count++;if(count == 1000){count = 0;usleep(100);}if(g_duojiPinValue == HI_GPIO_VALUE1){hi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE1);//让DUOJI_PIN输出高电平hi_udelay(m_time);//延时1mshi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE0);//让DUOJI_PIN输出低电平hi_udelay(20000-m_time);//延时1ms}else if(g_duojiPinValue = HI_GPIO_VALUE0){//hi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE1);//让DUOJI_PIN输出高电平//hi_udelay(l_time);//延时1.5ms//hi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE0);//让DUOJI_PIN输出低电平//hi_udelay(20000-l_time);//延时1.5mshi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE1);//让DUOJI_PIN输出高电平hi_udelay(s_time);//延时3mshi_gpio_set_ouput_val(DUOJI_PIN,HI_GPIO_VALUE0);//让DUOJI_PIN输出低电平hi_udelay(20000-s_time);//延时3ms}}return NULL;
}//任务五
void thread_func_udp(void *arg)
{// int socket_fd=0;                  // 函数返回值// int result;                  // 函数返回值char ip_buf[16]="";          // IP地址存储缓冲区(IPv4最大15字符+结束符)hi_watchdog_enable();WiFi_connectHotspots(WIFI_SSID,WIFI_PAWD);socket_fd = socket(AF_INET, SOCK_DGRAM, 0);//AF_INET 是ipv4协议家族,SOCK_DGRAM 代表的是udp协议,0,协议栈自动识别协议类型。if((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1){printf("creat socket failed!\r\n");return ;}struct sockaddr_in my_addr;my_addr.sin_family = AF_INET;         // IPv4协议族my_addr.sin_port = htons(9000);  // 端口号(主机转网络字节序)my_addr.sin_addr.s_addr = htonl(INADDR_ANY);  // 绑定所有本地接口result = bind(socket_fd, (struct sockaddr*)&my_addr, sizeof(my_addr));if(result != 0){perror("bind");return 0;}//struct sockaddr_in client_addr;addr_length=sizeof(client_addr);while(1){//printf("%d2",socket_fd);memset(recvBuf, 0, sizeof(recvBuf));  // 清空接收缓冲区初始化地址结构体长度/* 接收数据(阻塞式) */result = recvfrom(socket_fd, recvBuf, sizeof(recvBuf)-1, 0,(struct sockaddr*)&client_addr, &addr_length);memset(ip_buf, 0, sizeof(ip_buf));inet_ntop(AF_INET, &(client_addr.sin_addr), ip_buf, INET_ADDRSTRLEN);printf("收到来自ip地址为:%s的数据,", ip_buf);printf("收到数据字节数:%d, recvBuf:%s\r\n", result, recvBuf);// if (result > 0)// {// printf("收到数据字节数:%d, recvBuf:%s\r\n", result, recvBuf);// 构造包含温湿度的响应数据// char response[256] = {0};// snprintf(response, sizeof(response), // "{\"cmd\":\"%s\",\"temp\":%d,\"humi\":%d}", // recvBuf, global_temp, global_humi);// 发送数据到客户端 [修正] 移除多余的length)// result = sendto(socket_fd, response, strlen(response), 0, // (struct sockaddr *)&client_addr, addr_length);// if (result > 0)// {// [修正] 输出正确的发送内容// printf("发送成功字节数:%d, sendData:%s\r\n", result, response);// }//[优化] 只在接收到数据时处理命令// }udp_judge();/* 解析客户端地址信息 */osDelay(100);hi_watchdog_feed();  }
}//任务六
static void lightAdcTask(void *arg)
{while (1) {unsigned short lumen = 0;//光照强度	读ADC4通道if (hi_adc_read(LuMen_SENSOR_CHAN_NAME, &lumen, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAIS_DEFAULT, 0)== HI_ERR_SUCCESS) {// LIGHT_SENSOR_CHAN_NAME表示ADC4通道// HI_ADC_EQU_MODEL_4表示采样数据平均滤波处理,平均次数4次// HI_ADC_CUR_BAIS_DEFAULT表示模数转换采用默认电压基准// 0表示从配置采样到启动采样的延时时间计数为0// 返回值HI_ERR_SUCCESS表示成功// 打印ADC6通道的值// printf("ADC_VALUE = %d\n", (unsigned int)lumen);// 打印测量电压的值// printf("电压 = %.2f V\n", lumen*1.8*4/4096);}float adc;adc=(float)lumen*(3.3/4096);   Lum=3100-adc*1000;//printf("lumen = %d ", (unsigned int)lumen);//printf("adc = %f ", adc);//printf("LUM = %d ", Lum);// 等待100msosDelay(10);hi_watchdog_feed();}}//任务7
void Lcd_Task(void *arg)
{OledInit();OledFillScreen(0x00);//OledShowString(0,0,"a",FONT8x16);
while(1){char Temp[10];// char Humi[10];// char LUM[10];// char CO2[10];snprintf(Temp, sizeof(Temp), "Temp: %d C", temp);// snprintf(Humi, sizeof(Humi), "Humi: %d %%", humi);// snprintf(LUM, sizeof(LUM), "Light: %d lux", Lum);// snprintf(CO2, sizeof(CO2), "CO2: %d ppm", co2);OledFillScreen(0x00);OledShowString(0, 0, "Temp", FONT8x16);OledShowChar(2, 2, Temp, FONT8x16);	// OledShowChinese2(29 + 0, 3, 2); // OledShowChinese2(29 + 18, 3, 1); // OledShowChar(29 + 36, 3, Humi, FONT6x8);	// OledShowChinese2(29 + 0, 5, 3); // OledShowChinese2(29 + 18, 5, 4); // OledShowChar(29 + 36, 5, LUM, FONT6x8);	// OledShowChinese2(29 + 0, 7, 5); // OledShowChinese2(29 + 18, 7, 6); // OledShowChinese2(29 + 36, 7, 7); // OledShowChinese2(29 + 54, 7, 8); // OledShowChinese2(29 + 72, 7, 9); // OledShowChinese2(29 + 90, 7, 1); // OledShowChar(29 + 108, 7, CO2, FONT6x8);	}}//-----------------------------------------------------------------------------
//任务创建1 mqtt的发送
void wifi_mqtt_task_create(void)
{osThreadAttr_t taskOptions;p_MQTTClient_sub_callback = &mqtt_sub_payload_callback;taskOptions.name = "mqttTask";       // 任务的名字taskOptions.attr_bits = 0;               // 属性位taskOptions.cb_mem = NULL;               // 堆空间地址taskOptions.cb_size = 0;                 // 堆空间大小taskOptions.stack_mem = NULL;            // 栈空间地址taskOptions.stack_size = 1024*5;           // 栈空间大小 单位:字节taskOptions.priority = osPriorityNormal; // 任务的优先级mqtt_send_task_id = osThreadNew((osThreadFunc_t)mqtt_send_task, NULL, &taskOptions); // 创建任务if (mqtt_send_task_id != NULL){printf("ID = %d, mqtt_send_task_id Create OK!\n", mqtt_send_task_id);}
}
//任务创建2 adc
void AdcDemo(void)
{// 定义线程属性osThreadAttr_t attr;attr.name = "AdcTask";attr.attr_bits = 0U;attr.cb_mem = NULL;attr.cb_size = 0U;attr.stack_mem = NULL;attr.stack_size = 4096;attr.priority = osPriorityNormal;// 创建线程if (osThreadNew(AdcTask, NULL, &attr) == NULL) {printf("[AdcDemo] Falied to create ADCLightTask!\n");}
}
//任务创建3
static void human_sensor_demo(void)
{//定义线程属性osThreadAttr_t attrr;attrr.name = "HUMANSENSORTASK";attrr.stack_size = 10*1024;attrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)HUMANSENSORTASK, NULL, &attrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}
//任务创建4
static void duoji_demo(void)
{//定义线程属性osThreadAttr_t attrrr;attrrr.name = "DUOJITASK";attrrr.stack_size = 10*1024;attrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)DUOJITASK, NULL, &attrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task!\n");}
}//任务创建5
static void UDPTask(void)
{//定义线程属性osThreadAttr_t attrrrr;attrrrr.name = "thread_func_udp";attrrrr.stack_size = 10*1024;attrrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)thread_func_udp, NULL, &attrrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}//任务创建6
static void lightTask(void)
{//定义线程属性osThreadAttr_t attrrrrr;attrrrrr.name = "lightAdcTask";attrrrrr.stack_size = 10*1024;attrrrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)lightAdcTask, NULL, &attrrrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}//任务创建7
static void LCDTask(void)
{//定义线程属性osThreadAttr_t attrrrrrr;attrrrrrr.name = "Lcd_Task";attrrrrrr.stack_size = 10*1024;attrrrrrr.priority = osPriorityNormal;if (osThreadNew((osThreadFunc_t)Lcd_Task, NULL, &attrrrrrr) == NULL) {printf("[ThreadTestTask] Falied to create Task01!\n");}
}/*** @description: 初始化并创建任务* @param {*}* @return {*}*/
static void template_demo(void)
{human_sensor_init();key_init();led_init();printf("WIFI MQTT实验\r\n");wifi_mqtt_task_create();//任务1创建AdcDemo();//任务2创建human_sensor_demo();//任务3创建duoji_demo();//任务4创建UDPTask();lightTask();LCDTask();
}
SYS_RUN(template_demo);

​七、应用价值与创新突破​

1. 技术创新突破点​
  • ​双模无缝切换​​:手动操作可临时覆盖自动策略,满足紧急干预需求
  • ​设备联动引擎​​:实现"光照不足→自动补光+温度调节"的复合响应
  • ​设备联动​​:采用QT控制开发板时云平台图标随QT同步变化(前提是在云平台将设备组件属性改为共享模式)​
  • ​QT界面:采用分块布局,左侧为控制面板,右侧为监测面板,布局合理

  • 数据分析:舍弃折线图形式,采用对比数据显示箭头表示数据变化趋势、三态指示灯表示数据异常情况

​八、写在最后

1. 注意事项

a. 编写主程序时需要注意变量的宏定义和引用问题,例如将UDP上传放在按键中断中,在主程序的任务中接收温湿度,需要确定变量类型。

b. 值得注意的是,在使用云平台控制开发板时我们打开MQTTX软件观察,发现MQTTX反复断开后重连,起初我们认为是网络连接不稳定,后来排除后发现云平台每次只能和MQTTX或者开发板其一通信,两者同时使用会产生冲突。

​在整个项目中出现了各个部分的bug包括主程序中线程间的占用问题、网络连接问题、QT的UDP通信问题,甚至在MQTT无法连接时进行各种排查,发现是博主的队友云平台账号被封号。。。。但是最后的完成度还是非常高,有兴趣的朋友可以尝试使用其他外设,添加语音、摄像头、人脸识别,甚至接入deepseek,都是可以尝试的方向。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/86433.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86433.shtml
英文地址,请注明出处:http://en.pswp.cn/web/86433.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

揭开 Git 裸仓库的神秘面纱:`git clone --mirror` 详解与使用指南

大家好&#xff01;在使用 Git 进行版本控制时&#xff0c;我们最熟悉的莫过于那些带有工作目录的本地仓库了——我们在里面编辑文件、提交代码&#xff0c;然后推送到远程仓库。但有时候&#xff0c;我们可能会遇到一种特殊的仓库&#xff1a;裸仓库&#xff08;Bare Reposito…

opensuse安装rabbitmq

您好&#xff01;安装 RabbitMQ 消息队列是一个非常棒的选择&#xff0c;它是许多现代应用架构中的核心组件。 在 openSUSE Tumbleweed 上安装 RabbitMQ 主要有两种流行的方式&#xff1a;一种是使用系统的包管理器 zypper&#xff0c;另一种是使用 Docker 容器。我将为您详细…

超详细YOLOv8/11图像菜品分类全程概述:环境、数据准备、训练、验证/预测、onnx部署(c++/python)详解

文章目录 一、环境准备二、数据准备三、训练四、验证与预测五、模型部署 一、环境准备 我的都是在Linux系统下&#xff0c;训练部署的&#xff1b;模型训练之前&#xff0c;需要配置好环境&#xff0c;Anaconda、显卡驱动、cuda、cudnn、pytorch等&#xff1b; 参考&#xff1…

JUC:4.线程常见操作与两阶段终止模式

在线程中&#xff0c;wait()、join()、sleep()三个方法都是进行阻塞的方法。对应可以使用interrupt()方法进行打断&#xff0c;被打断后线程会抛出打断异常&#xff0c;但是不会修改IsInterrupt&#xff0c;也就是此时去调用IsInterrupted()方法后获得的实际上是false。 而当线…

分布式session解决方案

在实际项目中&#xff0c;前台代码部署在nginx中&#xff0c;后台服务内嵌了tomcat运行在不同的节点中&#xff0c;常见的架构如下&#xff1a; 在上述架构中&#xff0c;nginx转发前台请求&#xff0c;第一次登录后&#xff0c;将用户登录信息写入到一台服务session中&#xf…

UDP 缓冲区

UDP 有接收缓冲区&#xff0c;没有发送缓冲区 引申问题 1、为什么没有发送缓冲区&#xff1f; 直接引用原文 “因为 UDP 是不可靠的&#xff0c;它不必保存应用进程的数据拷贝&#xff0c;因此无需一个真正的发送缓冲区” 2、没有发送缓冲区的情况下&#xff0c;sendto 的数…

解密 C++ 中的左值(lvalue)与右值(rvalue)的核心内容

在 C 中&#xff0c;表达式&#xff08;expression&#xff09; 可以被归类为左值或右值。最简单的理解方式是&#xff1a; 左值&#xff08;lvalue&#xff09;&#xff1a; 能放在赋值号 左边的表达式&#xff0c;通常表示一个有名字、有内存地址、可以持续存在的对象。你可…

MATLAB(2)选择结构

选择结构又可以叫做分支结构&#xff0c;它根据给定的条件是否成立&#xff0c;决定程序运行的方向。在不同的条件下执行不同的操作。 MATLAB可以用来实现选择结构的语句有三种&#xff1a;if语句、switch语句、try语句。 一.if语句 1.if语句 1.1条件为矩阵的情况 if语句的…

Ehcache、Caffeine、Spring Cache、Redis、J2Cache、Memcached 和 Guava Cache 的主要区别

主流缓存技术 Ehcache、Caffeine、Spring Cache、Redis、J2Cache、Memcached 和 Guava Cache 的主要区别&#xff0c;涵盖其架构、功能、适用场景和优缺点等方面&#xff1a; Ehcache 类型: 本地缓存&#xff08;JVM 内存缓存&#xff09; 特点: 轻量级&#xff0c;运行在 JV…

谷歌浏览器截图全屏扩展程序

以下是一些支持跟随鼠标滚轮滚动截图的谷歌全屏截图扩展程序插件&#xff1a; GoFullPage&#xff1a;这是一款专门截取整个网页的截图插件。安装后&#xff0c;点击浏览器右上角的图标或使用快捷键AltShiftP&#xff0c;插件就会自动开始滚动并捕获当前访问的网站&#xff0c…

专线服务器具体是指什么?

专线服务器主要是指在互联网或者是局域网中&#xff0c;为特定用户或者是应用程序所提供的专用服务器设备&#xff0c;专线服务器与传统的共享服务器相比较来说&#xff0c;有着更高的安全性和更为稳定的网络连接&#xff0c;下面我们就来共同了解一下专线服务器的具体内容吧&a…

Jenkins JNLP与SSH节点连接方式对比及连接断开问题解决方案

一、JNLP vs SSH 连接方式优缺点对比 对比维度JNLP&#xff08;Java Web Start&#xff09;SSH&#xff08;Secure Shell&#xff09;核心原理代理节点主动连接Jenkins主节点&#xff0c;通过加密通道通信&#xff0c;支持动态资源分配。Jenkins通过SSH协议远程登录代理节点执…

Git - Commit命令

git commit 是 Git 版本控制系统中核心的提交命令&#xff0c;用于将暂存区&#xff08;Stage/Index&#xff09;中的修改&#xff08;或新增/删除的文件&#xff09;永久记录到本地仓库&#xff08;Repository&#xff09;&#xff0c;生成一个新的提交记录&#xff08;Commit…

Android System WebView Canary:探索前沿,体验最新功能

在移动互联网时代&#xff0c;WebView作为Android系统的核心组件之一&#xff0c;承担着在原生应用中显示Web内容的重要任务。它不仅为用户提供了便捷的网页浏览体验&#xff0c;还为开发者提供了强大的混合式开发能力。Android System WebView Canary&#xff08;金丝雀版本&a…

kubernetes架构原理

目录 一. 为什么需要 Kubernetes 1. 对于开发人员 2. 对于运维人员 3. Kubernetes 带来的挑战 二. Kubernetes 架构解析 1. master 节点的组件 2. Node 节点包含的组件 3. kubernetes网络插件 三. kubeadm块速安装kubernetes集群 1. 基础环境准备(此步骤在三个节点都执…

服务器的安装与安全设置 域环境的搭建和管理 Windows基本配置 网络服务常用网络命令的应用 安全管理Windows Server 2019

高等职业教育计算机网络技术专业实训指导书 2025年目 录 实训的目的和意义 实训的具体目标及主要内容 实训完成后需要提交的内容 项目一 服务器的安装与安全设置 项目二 域环境的搭建和管理 项目三 Windows基本配置 项目四 网络服务 项目五 常用网络命令的应用 项目六…

Springcloud解决jar包运行时无法拉取nacos远程配置文件

问题描述 springcloud微服务&#xff0c;在idea中运行代码&#xff0c;能够正常拉去nacos上的配置文件&#xff0c;打包后&#xff0c;通过jar包启动 java -jar xxx.jar&#xff0c;出现错误&#xff1a;java.nio.charset.MalformedlnputException: Input length 1 问题原因…

【Leetcode刷题随笔】01. 两数之和

1. 题目描述 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素不能使用两遍。 示例: 给定 nums [2, 7, 11,…

【机器学习深度学习】多层神经网络的构成

目录 一、神经网络模型的结构化组成方式 1. 最底层&#xff1a;神经网络模型 (Model) 2. 中间层&#xff1a;单个神经网络层 (Layer) 3. 最顶层&#xff1a;训练参数的细节 (Parameters & Variables) 二、关键理解要点 三、类比理解 场景一&#xff1a;工厂运作 场…

设计模式:揭秘Java原型模式——让复杂对象的创建不再复杂

原型模式 原型模式介绍 定义: 原型模式(Prototype Design Pattern)用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 西游记中的孙悟空&#xff0c;拔毛变小猴&#xff0c;孙悟空这种根据自己的形状复制出多个身外化身的技巧&…