200行C++代码写一个QT串口助手

前言

今天分享一个用QT写的串口助手,关键代码会直接在文章的对应位置贴出,完整的工程文件(用的VS 2019)可以进入我的主页免费下载,也可以关注我的公众号“折途想要敲代码” 回复关键词“qt串口助手”免费获取。       

如果是使用QTCreator的小伙伴在项目配置完毕后可以通过复制我提供的工程文件中的.cpp和.h文件来达到同样的效果。

要配置的就是在配置文件中加上串口对应的部分。

使用VS的需要再拓展插件中找到模块管理再加上串口的模块。

需要包含以下头文件。

#include <QtWidgets/QMainWindow>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QComboBox>
#include <QLabel>
#include <QTimer>
#include <QMessageBox>#include <QSerialPortInfo>
#include <QSerialPort>#include <qdebug.h>
#include <qvector.h>

布局

写一个应用最重要的就是外观,也就是前端,因为用户是看不见你程序的逻辑的,因此我们首先需要做的就是先把串口助手的外观设计好。

说QT简单的一个很重要的因素就是QT自带一个图形化界面设计(QTDesign),不得不说使用图形化界面去设计界面确实又快又好。不过我个人不喜欢,因为“折途想要敲代码”。

从零开始写一个串口助手还是挺困难的,因此界面我参考的b站江科大自化协提供的串口助手。 

从上图可以知道我们需要往串口助手里添加的组件就四种:两个可编辑内容展示内容的容器,5个按钮,7个用来配置串口通信的多选项以及对应描述功能的文字标签。

发送区和接收区的组件我们选用QPlainTextEdit

按钮使用QPushButton

多选项使用QComboBox

文字标签自然使用的是QLabel

有了布局的信息之后只需要将对应组件创建之后修改大小以及摆放位置即可。

接收区

接收区是接收串口通信传来的数据的,因此我们可以直接设为只读模式。

接收区还配一个按钮“清空接收区”,我们搭配一个信号槽,按下后直接调用QPlainTextEdit自带的clear函数来清空即可。

//接收区初始化
void Serial::ReceiveAeraInit(QWidget* parent) {receiveAera = new QPlainTextEdit(parent);receiveAera->setFixedSize(800,400);receiveAera->move(30,20);receiveAera->setReadOnly(true);     //接收区改为只读QPushButton* clearReceive = new QPushButton(QString::fromLocal8Bit("清空接收区"),parent);clearReceive->setFixedSize(150,50);clearReceive->move(680,430);//为清空接收区设置信号槽QObject::connect(clearReceive, &QPushButton::clicked, [&]() {receiveAera->clear();});
}

串口设置

我们使用QComboBox多选项组件来配置串口的设置。

 需要配置的是串口号,这个不是固定的,是根据用户连接到电脑的串口来决定的,因此需要动态地获取,我把这一块放在了定时器里,每1000ms扫描一次更新串口号选项。

我们使用QT自带的QSerialPortInfo来获取串口号,把可用的串口号取出,和之前串口号选项来作比较,如果可用的串口出现变化了再更新,如果一直更新的话后面插入的串口会选择不到。

如果获取不到可用串口的话就检查串口是否正确连接,并且检查是否安装了对应的驱动,如果电脑的设备管理器能够看到串口的话,这里也是可以获取的到的。

//刷新可用串口
void Serial::RefreshPort(void) {QVector<QString>temp;//获取当前可用串口号for (const QSerialPortInfo& info : QSerialPortInfo::availablePorts()) {temp.push_back(info.portName());}//排序现有的串口号,用于比较和原有的差距qSort(temp.begin(), temp.end());if (temp != this->ports) {  //如果可用串口号有变化this->portNumber->clear();  //清除原有列表this->ports = temp;         //更新串口列表for (auto& a : ports) {     //更新新串口this->portNumber->addItem(a);}}
}

接下来是其他配置选项,其他配置都是固定选型,因此我们需要手动添加上去。

因为我主要是用来和stm32来使用的,所以选项是什么我需要参考stm32串口相关代码以及qt串口类支持的参数。

第一个是波特率。下图是QT提供的波特率,以及stm32关于波特率的参数注释。

 综上,我决定给波特率三个选项,4800,9600,19200。

第二个是数据位。

QT支持5~8的数据位长度,而stm32支持8和9位,也就是没得选,只能是8位数据长度,这里可以写死也可以取消数据位长度的配置,不过为了后续功能的拓展,还是加上了数据位长度的多选项部分,只不过目前只有一个选项没得选。

第三个是停止位,qt支持1,1.5,2一共三种选项,stm32多一个0.5,因此我们使用它们的交集,也就是1,1.5,2。

最后一个是校验位,我们可以选择无校验,奇校验,偶校验。

另外两个是接收和发送的格式,可选HEX和文本。

//串口设置初始化
void Serial::SetupInit(QWidget* parent) {this->portNumber = new QComboBox(parent);this->baudRate = new QComboBox(parent);this->dataSize = new QComboBox(parent);this->stopSize = new QComboBox(parent);this->check = new QComboBox(parent);this->receiveMode = new QComboBox(parent);this->sendMode = new QComboBox(parent);this->baudRate->addItem("4800");this->baudRate->addItem("9600");this->baudRate->addItem("19200");this->dataSize->addItem("8");this->stopSize->addItem("1");this->stopSize->addItem("1.5");this->stopSize->addItem("2");this->check->addItem(QString::fromLocal8Bit("无"));this->check->addItem(QString::fromLocal8Bit("奇校验"));this->check->addItem(QString::fromLocal8Bit("偶校验"));this->receiveMode->addItem(QString::fromLocal8Bit("HEX"));this->receiveMode->addItem(QString::fromLocal8Bit("文本"));this->sendMode->addItem(QString::fromLocal8Bit("HEX"));this->sendMode->addItem(QString::fromLocal8Bit("文本"));QLabel* portLabel = new QLabel(QString::fromLocal8Bit("串口号"), parent);QLabel* baudLabel = new QLabel(QString::fromLocal8Bit("波特率"),parent);QLabel* dataLabel = new QLabel(QString::fromLocal8Bit("数据位"),parent);QLabel* stopLabel = new QLabel(QString::fromLocal8Bit("停止位"),parent);QLabel* checkLabel = new QLabel(QString::fromLocal8Bit("校验位"),parent);QLabel* receiveModeLabel = new QLabel(QString::fromLocal8Bit("接收格式"),parent);QLabel* sendModeLabel = new QLabel(QString::fromLocal8Bit("发送格式"),parent);QVector<QComboBox*>setups;setups.push_back(portNumber);setups.push_back(baudRate);setups.push_back(dataSize);setups.push_back(stopSize);setups.push_back(check);setups.push_back(receiveMode);setups.push_back(sendMode);QVector<QLabel*>labels;labels.push_back(portLabel);labels.push_back(baudLabel);labels.push_back(dataLabel);labels.push_back(stopLabel);labels.push_back(checkLabel);labels.push_back(receiveModeLabel);labels.push_back(sendModeLabel);for (int i = 0; i < setups.size(); ++i) {setups[i]->setFixedSize(200, 50);setups[i]->move(850, 20 + i * 80);labels[i]->move(1080,25+i*80);}}

串口核心代码

串口连接和断开我们分别需要两个按钮,江科大用的是同一个按钮,不过我用两个(不是做不到那样的效果,而是我觉得两个按钮表示两种功能比较合适)。

布局完之后就设置信号槽,也就是点击按钮之后的逻辑了。

首先一开始没有连接串口,所以先把断开连接的按钮失效,也就是点不了,并且无法发送数据,因此在发送区的发送按钮也需要失效。

在点击串口连接的按钮之后,判断是否有串口号,而且也只需要判断是否有串口号即可,因为其他配置都是肯定会有并且是合法的。

没有串口号的话那就没有任何反应。

如果有串口号的话就使串口连接的按钮失效,使断开连接按钮和发送区的发送按钮生效。

//串口连接
void Serial::BeginUSART(QWidget* parent) {startUSART = new QPushButton(QString::fromLocal8Bit("串口连接"),parent);endUSART = new QPushButton(QString::fromLocal8Bit("断开连接"),parent);endUSART->setFixedSize(150, 50);endUSART->move(1000, 600);startUSART->setFixedSize(150, 50);startUSART->move(850,600);endUSART->setDisabled(true);        //一开始没有连接串口,因此关闭按钮初始化为无效//为关闭连接按钮配置信号槽QObject::connect(endUSART, &QPushButton::clicked, [&]() {endUSART->setDisabled(true);        //使关闭连接按钮失效startUSART->setDisabled(false);     //使连接按钮生效sendButton->setDisabled(true);      //使发送按钮失效serialPort->close();                //断开串口连接});//为连接按钮配置信号槽QObject::connect(startUSART, &QPushButton::clicked, [&]() {QString port = portNumber->currentText();QString baud = baudRate->currentText();QString data = dataSize->currentText();QString stop = stopSize->currentText();QString ch = check->currentText();QString receive = receiveMode->currentText();QString send = sendMode->currentText();if (port != "") {       //当串口号不为空,即有效时endUSART->setDisabled(false);   //使关闭连接按钮生效sendButton->setDisabled(false); //使发送按钮生效startUSART->setDisabled(true);  //使连接按钮失效USART(port,baud,data,stop,ch);  //连接串口}});
}

接下来开始串口连接的逻辑。

因为比较多,写在lambda的话可读性比较差,因此我单开了一个函数。

在确认串口号有效之后,把配置选项的值传送进串口连接函数,其实不传参数,在函数里直接获取也是可以的,因为多选项的组件属于成员变量,是可以获取到的。

在串口连接函数的开始需要做的就是把选项值换成qt串口类支持的枚举类型,这里我用的if else语句,使用switch也是可以的。

转换完成之后对串口类进行配置然后连接即可。

接下来就是为串口配置信号槽,一旦有数据传来,我们就需要读取数据,并且通过选择的接收模式来对数据进行加工,如果是选了“HEX”,那么就把数据转换为16进制,如果选了“文本”,那么就转换为QString类型。

数据加工完毕之后加入接收区即可。

//串口通信核心
void Serial::USART(QString port, QString baud, QString data,QString stop,QString check) {QSerialPort::BaudRate Baud;     //波特率QSerialPort::DataBits Data;     //数据位QSerialPort::StopBits Stop;     //停止位QSerialPort::Parity Check;      //校验位if (baud == "4800")  Baud = QSerialPort::Baud4800;else if (baud == "9600") Baud = QSerialPort::Baud9600;else if (baud == "19200")  Baud = QSerialPort::Baud19200;if (data == "8") Data = QSerialPort::Data8;if (stop == "1") Stop = QSerialPort::OneStop;else if (stop == "1.5")Stop = QSerialPort::OneAndHalfStop;else if (stop == "2") Stop = QSerialPort::TwoStop;if (check == QString::fromLocal8Bit("无")) Check = QSerialPort::NoParity;else if (check == QString::fromLocal8Bit("奇校验")) Check = QSerialPort::OddParity;else if (check == QString::fromLocal8Bit("偶校验")) Check = QSerialPort::EvenParity;serialPort = new QSerialPort(this);//为串口设置配置serialPort->setBaudRate(Baud);serialPort->setPortName(port);serialPort->setDataBits(Data);serialPort->setParity(Check);serialPort->setStopBits(Stop);//打开串口if (serialPort->open(QSerialPort::ReadWrite)) {//配置信号槽,一旦收到数据则开始读取QObject::connect(serialPort, &QSerialPort::readyRead, [&]() {auto data = serialPort->readAll();if (receiveMode->currentText() == "HEX") {      //字节模式QString hex = data.toHex(' ');receiveAera->appendPlainText(hex);}else {                                          //文本模式QString str = QString(data);receiveAera->appendPlainText(str);}});}else {QMessageBox::critical(this, QString::fromLocal8Bit("串口打开失败"), QString::fromLocal8Bit("请确认串口是否正确连接"));}
}

发送区

最后剩个发送区,跟接收区类似,只不过多了一个发送的按钮。

发送按钮也需要配置信号槽。

当按下发送按钮的时候,我们就获取当前发送区中的数据,如果是发送格式选择了“HEX”,那我们就需要把发送区的数据按照两个数字一组的形式转换为16进制,完成的逻辑可以参考下面的代码。

如果选择的是“文本”,那么就直接把数据转成utf-8的数据格式即可。

转换完成之后就直接对串口类进行写操作就行了。

//发送区初始化
void Serial::SendAeraInit(QWidget* parent) {sendAera = new QPlainTextEdit(parent);sendAera->setFixedSize(800,100);sendAera->move(30,500);QPushButton* clearSend = new QPushButton(QString::fromLocal8Bit("清空发送区"), parent);clearSend->setFixedSize(150, 50);clearSend->move(680, 630);QObject::connect(clearSend, &QPushButton::clicked, [&]() {sendAera->clear();});sendButton = new QPushButton(QString::fromLocal8Bit("发送"), parent);sendButton->setFixedSize(150, 50);sendButton->move(500, 630);sendButton->setDisabled(true);QObject::connect(sendButton, &QPushButton::clicked, [&]() {QString data = sendAera->toPlainText();if (sendMode->currentText() == "HEX") {QByteArray arr;for (int i = 0; i < data.size(); i++){if (data[i] == ' ') continue;int num = data.mid(i, 2).toUInt(nullptr, 16);       //将数据转为16进制i++;arr.append(num);}serialPort->write(arr);}else {serialPort->write(data.toLocal8Bit().data());           //转为utf-8格式字符串写入}});
}

.cpp完整代码&.h完整代码

#include "Serial.h"//串口通信核心
void Serial::USART(QString port, QString baud, QString data,QString stop,QString check) {QSerialPort::BaudRate Baud;     //波特率QSerialPort::DataBits Data;     //数据位QSerialPort::StopBits Stop;     //停止位QSerialPort::Parity Check;      //校验位if (baud == "4800")  Baud = QSerialPort::Baud4800;else if (baud == "9600") Baud = QSerialPort::Baud9600;else if (baud == "19200")  Baud = QSerialPort::Baud19200;if (data == "8") Data = QSerialPort::Data8;if (stop == "1") Stop = QSerialPort::OneStop;else if (stop == "1.5")Stop = QSerialPort::OneAndHalfStop;else if (stop == "2") Stop = QSerialPort::TwoStop;if (check == QString::fromLocal8Bit("无")) Check = QSerialPort::NoParity;else if (check == QString::fromLocal8Bit("奇校验")) Check = QSerialPort::OddParity;else if (check == QString::fromLocal8Bit("偶校验")) Check = QSerialPort::EvenParity;serialPort = new QSerialPort(this);//为串口设置配置serialPort->setBaudRate(Baud);serialPort->setPortName(port);serialPort->setDataBits(Data);serialPort->setParity(Check);serialPort->setStopBits(Stop);//打开串口if (serialPort->open(QSerialPort::ReadWrite)) {//配置信号槽,一旦收到数据则开始读取QObject::connect(serialPort, &QSerialPort::readyRead, [&]() {auto data = serialPort->readAll();if (receiveMode->currentText() == "HEX") {      //字节模式QString hex = data.toHex(' ');receiveAera->appendPlainText(hex);}else {                                          //文本模式QString str = QString(data);receiveAera->appendPlainText(str);}});}else {QMessageBox::critical(this, QString::fromLocal8Bit("串口打开失败"), QString::fromLocal8Bit("请确认串口是否正确连接"));}
}//刷新可用串口
void Serial::RefreshPort(void) {QVector<QString>temp;//获取当前可用串口号for (const QSerialPortInfo& info : QSerialPortInfo::availablePorts()) {temp.push_back(info.portName());}//排序现有的串口号,用于比较和原有的差距qSort(temp.begin(), temp.end());if (temp != this->ports) {  //如果可用串口号有变化this->portNumber->clear();  //清除原有列表this->ports = temp;         //更新串口列表for (auto& a : ports) {     //更新新串口this->portNumber->addItem(a);}}
}//接收区初始化
void Serial::ReceiveAeraInit(QWidget* parent) {receiveAera = new QPlainTextEdit(parent);receiveAera->setFixedSize(800,400);receiveAera->move(30,20);receiveAera->setReadOnly(true);     //接收区改为只读QPushButton* clearReceive = new QPushButton(QString::fromLocal8Bit("清空接收区"),parent);clearReceive->setFixedSize(150,50);clearReceive->move(680,430);//为清空接收区设置信号槽QObject::connect(clearReceive, &QPushButton::clicked, [&]() {receiveAera->clear();});
}//发送区初始化
void Serial::SendAeraInit(QWidget* parent) {sendAera = new QPlainTextEdit(parent);sendAera->setFixedSize(800,100);sendAera->move(30,500);QPushButton* clearSend = new QPushButton(QString::fromLocal8Bit("清空发送区"), parent);clearSend->setFixedSize(150, 50);clearSend->move(680, 630);QObject::connect(clearSend, &QPushButton::clicked, [&]() {sendAera->clear();});sendButton = new QPushButton(QString::fromLocal8Bit("发送"), parent);sendButton->setFixedSize(150, 50);sendButton->move(500, 630);sendButton->setDisabled(true);QObject::connect(sendButton, &QPushButton::clicked, [&]() {QString data = sendAera->toPlainText();if (sendMode->currentText() == "HEX") {QByteArray arr;for (int i = 0; i < data.size(); i++){if (data[i] == ' ') continue;int num = data.mid(i, 2).toUInt(nullptr, 16);       //将数据转为16进制i++;arr.append(num);}serialPort->write(arr);}else {serialPort->write(data.toLocal8Bit().data());           //转为utf-8格式字符串写入}});
}//定时事件
void Serial::timerEvent(QTimerEvent* e) {RefreshPort();      //更新端口     
}//串口设置初始化
void Serial::SetupInit(QWidget* parent) {this->portNumber = new QComboBox(parent);this->baudRate = new QComboBox(parent);this->dataSize = new QComboBox(parent);this->stopSize = new QComboBox(parent);this->check = new QComboBox(parent);this->receiveMode = new QComboBox(parent);this->sendMode = new QComboBox(parent);this->baudRate->addItem("4800");this->baudRate->addItem("9600");this->baudRate->addItem("19200");this->dataSize->addItem("8");this->stopSize->addItem("1");this->stopSize->addItem("1.5");this->stopSize->addItem("2");this->check->addItem(QString::fromLocal8Bit("无"));this->check->addItem(QString::fromLocal8Bit("奇校验"));this->check->addItem(QString::fromLocal8Bit("偶校验"));this->receiveMode->addItem(QString::fromLocal8Bit("HEX"));this->receiveMode->addItem(QString::fromLocal8Bit("文本"));this->sendMode->addItem(QString::fromLocal8Bit("HEX"));this->sendMode->addItem(QString::fromLocal8Bit("文本"));QLabel* portLabel = new QLabel(QString::fromLocal8Bit("串口号"), parent);QLabel* baudLabel = new QLabel(QString::fromLocal8Bit("波特率"),parent);QLabel* dataLabel = new QLabel(QString::fromLocal8Bit("数据位"),parent);QLabel* stopLabel = new QLabel(QString::fromLocal8Bit("停止位"),parent);QLabel* checkLabel = new QLabel(QString::fromLocal8Bit("校验位"),parent);QLabel* receiveModeLabel = new QLabel(QString::fromLocal8Bit("接收格式"),parent);QLabel* sendModeLabel = new QLabel(QString::fromLocal8Bit("发送格式"),parent);QVector<QComboBox*>setups;setups.push_back(portNumber);setups.push_back(baudRate);setups.push_back(dataSize);setups.push_back(stopSize);setups.push_back(check);setups.push_back(receiveMode);setups.push_back(sendMode);QVector<QLabel*>labels;labels.push_back(portLabel);labels.push_back(baudLabel);labels.push_back(dataLabel);labels.push_back(stopLabel);labels.push_back(checkLabel);labels.push_back(receiveModeLabel);labels.push_back(sendModeLabel);for (int i = 0; i < setups.size(); ++i) {setups[i]->setFixedSize(200, 50);setups[i]->move(850, 20 + i * 80);labels[i]->move(1080,25+i*80);}}//串口连接
void Serial::BeginUSART(QWidget* parent) {startUSART = new QPushButton(QString::fromLocal8Bit("串口连接"),parent);endUSART = new QPushButton(QString::fromLocal8Bit("断开连接"),parent);endUSART->setFixedSize(150, 50);endUSART->move(1000, 600);startUSART->setFixedSize(150, 50);startUSART->move(850,600);endUSART->setDisabled(true);        //一开始没有连接串口,因此关闭按钮初始化为无效//为关闭连接按钮配置信号槽QObject::connect(endUSART, &QPushButton::clicked, [&]() {endUSART->setDisabled(true);        //使关闭连接按钮失效startUSART->setDisabled(false);     //使连接按钮生效sendButton->setDisabled(true);      //使发送按钮失效serialPort->close();                //断开串口连接});//为连接按钮配置信号槽QObject::connect(startUSART, &QPushButton::clicked, [&]() {QString port = portNumber->currentText();QString baud = baudRate->currentText();QString data = dataSize->currentText();QString stop = stopSize->currentText();QString ch = check->currentText();QString receive = receiveMode->currentText();QString send = sendMode->currentText();if (port != "") {       //当串口号不为空,即有效时endUSART->setDisabled(false);   //使关闭连接按钮生效sendButton->setDisabled(false); //使发送按钮生效startUSART->setDisabled(true);  //使连接按钮失效USART(port,baud,data,stop,ch);  //连接串口}});
}Serial::Serial(QWidget *parent): QMainWindow(parent){this->setFixedSize(1200,750);this->setWindowTitle(QString::fromLocal8Bit("串口助手"));ReceiveAeraInit(this);SendAeraInit(this);SetupInit(this);BeginUSART(this);this->startTimer(1000);         //开个1秒的定时器用来扫描可用串口
}Serial::~Serial(){}
#pragma once
#include <QtWidgets/QMainWindow>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QComboBox>
#include <QLabel>
#include <QTimer>
#include <QMessageBox>#include <QSerialPortInfo>
#include <QSerialPort>#include <qdebug.h>
#include <qvector.h>class Serial : public QMainWindow{Q_OBJECT
public:Serial(QWidget *parent = nullptr);~Serial();void RefreshPort(void);void timerEvent(QTimerEvent* e);void ReceiveAeraInit(QWidget* parent);void SendAeraInit(QWidget* parent);void SetupInit(QWidget* parent);void BeginUSART(QWidget* parent);void USART(QString port, QString baud, QString data, QString stop, QString ch);
private:QPlainTextEdit* sendAera;QPlainTextEdit* receiveAera;QPushButton* sendButton;QComboBox* portNumber;QComboBox* baudRate;QComboBox* dataSize;QComboBox* stopSize;QComboBox* check;QComboBox* receiveMode;QComboBox* sendMode;QPushButton* startUSART;QPushButton* endUSART;QSerialPort* serialPort;QVector<QString>ports;
};

效果展示

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

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

相关文章

基于ssm快餐店点餐结算系统的设计与实现+vue论文

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装快餐店点餐结算系统软件来发挥其高效地信息处理的作用&…

自定义一个对象【minio】

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 相关文章推荐&#xff1a; 对象存储MinIO的简介与部署 两种MinIO分布式集群部署方式 记录一次跨越16个月的minio版本升级与数据迁…

JS 高频面试题

JS 的数据类型有哪些&#xff0c;有什么区别 基本数据类型&#xff08;Undefined、Null、Boolean、Number、String、Symbol&#xff09; 引用数据类型&#xff08;对象、数组和函数&#xff09; 区别&#xff1a; 原始数据类型直接存储在栈&#xff08;stack&#xff09;中的简…

DeepSeek 发布全新开源大模型,数学推理能力超越 LLaMA-2

自从 LLaMA 被提出以来&#xff0c;开源大型语言模型&#xff08;LLM&#xff09;的快速发展就引起了广泛研究关注&#xff0c;随后的一些研究就主要集中于训练固定大小和高质量的模型&#xff0c;但这往往忽略了对 LLM 缩放规律的深入探索。 开源 LLM 的缩放研究可以促使 LLM…

Leetcode 1049 最后一块石头的重量II

题意理解&#xff1a; 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。 思路转化&#xff1a;我们可…

Farad capacitor法拉电容优点及缺点

Farad capacitor 法拉电容又称Electrical Double-Layer Capacitor双电层电容器、Gold capacitor黄金电容、Super capacitor 超级电容器&#xff0c;是一种化学元件。Super capacitor 超级电容器通过极化电解质来储能&#xff0c;但不发生化学反应&#xff0c;而且储能过程是可逆…

【MySQL】表设计与范式设计

文章目录 一、数据库表设计一对一一对多多对多 二、范式设计第一范式第二范式第三范式BC范式第四范式 一、数据库表设计 一对一 举个例子&#xff0c;比如这里有两张表&#xff0c;用户User表 和 身份信息Info表。 因为一个用户只能有一个身份信息&#xff0c;所以User表和In…

[Kubernetes]7. K8s包管理工具Helm、使用Helm部署mongodb集群(主从数据库集群)

上一节讲解了[Kubernetes]6. k8s Pod配置管理ConfigMap & Secret以及传递环境变量的使用,k8s的命名空间以及使用kubens管理命名空间的使用,这里来介绍一下Helm的使用 一.Helm相关介绍 1.介绍 在 kubernetes 系统上部署容器化应用时需要事 先手动编写资源配置清单文件 以…

数据库编程大赛冠军:郑凌云:0.67秒通过百万级数据评测!SQL代码惊现神之一手!

12月27日&#xff0c;NineData和云数据库技术社区主办&#xff0c;华为云、火山引擎、开源中国、云和恩墨、TDengine、云猿生数据、DORIS、ITPUB等协办单位和媒体&#xff0c;共同举办了本次《数据库编程大赛》。大赛题目「用一条SQL给出扑克牌24点的计算表达式」。 以下冠军选…

Unity组件开发--长连接webSocket

1.下载安装UnityWebSocket 插件 https://gitee.com/cambright/UnityWebSocket/ 引入unity项目&#xff1a; 2.定义消息体结构&#xff1a;ExternalMessage和包结构Package&#xff1a; using ProtoBuf; using System; using System.Collections; using System.Collections.Ge…

《路由与交换技术》---练习题(无答案纯享版)

注意&#xff01;&#xff01;&#xff01;这篇blog是无答案纯享版的 选择填空的答案我会放评论区 简答题可以看这里 计算题可以发私信问我&#xff08;当然WeChat也成&#xff09;but回讯息很慢 一、选择题 1.以下不会在路由表里出现的是: ( ) A.下一跳地址 B.网络地址 C…

用友移动管理系统 upload任意文件上传漏洞

产品介绍 用友移动系统管理系统是用友公司推出的一款移动办公解决方案&#xff0c;旨在帮助企业实现移动办公、提高管理效率和员工工作灵活性。 漏洞描述 用友移动系统管理系统/mobsm/common/upload等接口存在任意文件上传漏洞&#xff0c;未经授权攻击者通过漏洞上传任意文…