当前位置: 首页 > news >正文

Qt串口基本设置与协议收发

前言

1.一直都想要做一个Qt上位机,趁着这个周末有时间,动手写一下

2.comboBox没有点击的信号,所以做了一个触发的功能

3.Qt的数据类型很奇怪,转来转去的我也搞得很迷糊

4.给自己挖个坑,下一期做一个查看波形的上位机

有纰漏请指出,转载请说明。

学习交流请发邮件 1280253714@qq.com

串口功能

波特率设置

串口开关

串口异常检测

字符串/HEX收发

定时发送

接收数据分隔

协议组包

协议拆包

源代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QtSerialPort/QSerialPort>         // 提供访问串口的功能
#include <QtSerialPort/QSerialPortInfo>      // 提供系统中存在的串口信息
#include <QTime>
#include <QtCharts>
#include <QTimer>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECT//用于表格设置enum FieldColNum {colTime,colCmd1,colCmd2,colData,};enum CellType {ctTime,ctCmd1,ctCmd2,ctData,};
public:int tableRowCnt = 0;
public:Widget(QWidget *parent = nullptr);~Widget();void serialPortInit();  //串口初始化void windowInit();      //显示窗口初始化void refreshCom();      //刷新串口void tableInit();       //表格初始化void createItemsARow(int rowNum, QByteArray *protocalData); //表格新建一行QString ByteArrayToHexString(QByteArray &ba);
private:Ui::Widget *ui;QSerialPort* serialPort;
public slots:void comboBoxClicked(); //comboBox被点击
private slots:void sendData();            //发送串口数据void receiveData();         //接收串口数据void openSerialport();      //串口开启void closeSerialport();     //串口关闭void setBuad(int);          //设置波特率void clearRcv();            //清楚接收缓存void on_btnConvert_clicked();   //转换按钮被点击void on_btnClear_clicked();     //清楚转化的按钮被点击void sendProtocalHexData();     //以hex格式发送串口数据void handleSerialError(QSerialPort::SerialPortError serialPortErr); //串口异常捕获
};#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include "newcombobox.h"
#include <algorithm>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//创建一个定时器用来定时发送QTimer *timer1 = new QTimer();timer1->start(1000);connect(timer1,&QTimer::timeout,[=](){int timed = ui->comboBox_timedSend->currentText().toInt();timer1->start(timed);if(ui->checkBox_timedSend->isChecked() == true){sendData();}});windowInit();tableInit();serialPort = new QSerialPort();serialPortInit();connect(serialPort,SIGNAL(readyRead()),this,SLOT(receiveData()));connect(serialPort,SIGNAL(errorOccurred(QSerialPort::SerialPortError)),this,SLOT(handleSerialError(QSerialPort::SerialPortError)));connect(ui->pushButton_sendData,SIGNAL(clicked()),this,SLOT(sendData()));connect(ui->pushButton_openSerialPort,SIGNAL(clicked()),this,SLOT(openSerialport()));connect(ui->pushButton_closeSerialPort,SIGNAL(clicked()),this,SLOT(closeSerialport()));connect(ui->comboBox_chooseCom,SIGNAL(clicked()),this,SLOT(comboBoxClicked()));connect(ui->pushButton_clearRcv,SIGNAL(clicked()),this,SLOT(clearRcv()));connect(ui->pushButton_convert,SIGNAL(clicked()),this,SLOT(on_btnConvert_clicked()));connect(ui->pushButton_clearConvertData,SIGNAL(clicked()),this,SLOT(on_btnClear_clicked()));connect(ui->pushButton_sendProtocalData,SIGNAL(clicked()),this,SLOT(sendProtocalHexData()));connect(serialPort,SIGNAL(errorOccurred(QSerialPort::SerialPortError)),this,SLOT(handleSerialError(QSerialPort::SerialPortError)));connect(ui->comboBox_setBuad,SIGNAL(activated(int)),this,SLOT(setBuad(int)));
}Widget::~Widget()
{delete ui;
}//串口异常捕获
void Widget::handleSerialError(QSerialPort::SerialPortError serialPortErr)
{if(serialPortErr == QSerialPort::ResourceError){QMessageBox::critical(NULL, "critical", "设备拔出", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);closeSerialport();}if(serialPortErr == QSerialPort::DeviceNotFoundError){QMessageBox::critical(NULL, "critical", "找不到串口", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);closeSerialport();}
}void Widget::comboBoxClicked()
{refreshCom();
}void Widget::windowInit()
{ui->pushButton_closeSerialPort->setEnabled(false);ui->pushButton_openSerialPort->setEnabled(false);ui->pushButton_sendData->setEnabled(false);setWindowTitle(tr("串口收发"));ui->comboBox_timedSend->addItem("10");ui->comboBox_timedSend->addItem("100");ui->comboBox_timedSend->addItem("1000");ui->comboBox_timedSend->setCurrentIndex(2);
}void Widget::refreshCom()
{//显示串口列表ui->comboBox_chooseCom->clear();foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts())ui->comboBox_chooseCom->addItem(portInfo.portName()+":"+portInfo.description());ui->pushButton_openSerialPort->setEnabled(ui->comboBox_chooseCom->count()>0);	//
}void Widget::serialPortInit(){refreshCom();ui->comboBox_setBuad->addItem("9600");ui->comboBox_setBuad->addItem("115200");ui->comboBox_setBuad->addItem("921600");ui->comboBox_setBuad->setCurrentIndex(1);}void Widget::tableInit()
{QStringList headerText;headerText<<"时间"<<"命令1"<<"命令2"<<"数据";ui->tableWidget->setColumnCount(headerText.size());      //设置表格列数ui->tableWidget->resizeColumnsToContents();for (int i=0;i<ui->tableWidget->columnCount();i++){QTableWidgetItem *headerItem=new QTableWidgetItem(headerText.at(i));QFont font=headerItem->font();   //获取原有字体设置font.setBold(true);              //设置为粗体font.setPointSize(10);           //字体大小headerItem->setForeground(QBrush(Qt::black));  //设置文字颜色headerItem->setFont(font);       //设置字体ui->tableWidget->setHorizontalHeaderItem(i,headerItem);    //设置表头单元格的item}ui->tableWidget->setColumnWidth(0, 60);ui->tableWidget->setColumnWidth(1, 50);ui->tableWidget->setColumnWidth(2, 50);ui->tableWidget->setColumnWidth(3, 250);
}//为一行的单元格创建 Items
void Widget::createItemsARow(int rowNum, QByteArray *protocalData)
{uchar preFix = 0xA5;uchar crc = 0;uchar temp = 0;temp = static_cast<uchar>(protocalData->at(0));if(static_cast<uchar>(protocalData->at(0)) == preFix){for(int i=1; i<protocalData->length()-2; i++){temp = static_cast<uchar>(protocalData->at(i));crc += static_cast<uchar>(protocalData->at(i));}temp = static_cast<uchar>(protocalData->at(protocalData->length()-2));if(crc != static_cast<uchar>(protocalData->at(protocalData->length()-2))){return;}uchar len = protocalData->at(1);uchar cmd1 = protocalData->at(2);uchar cmd2 = protocalData->at(3);QByteArray data = protocalData->mid(4,len-6);QDateTime curTime = QDateTime::currentDateTime();//获取系统现在的时间QString time = curTime.toString("hh:mm:ss"); //设置显示格式uint8_t str1 = static_cast<uint8_t>(cmd1);QString hexStr1 = QString("%1").arg(str1, 2, 16, QLatin1Char('0')).toUpper();uint8_t str2 = static_cast<uint8_t>(cmd2);QString hexStr2 = QString("%1").arg(str2, 2, 16, QLatin1Char('0')).toUpper();QString testdata = ByteArrayToHexString(data).toLatin1().toUpper();QTableWidgetItem *item = new QTableWidgetItem(time, ctTime);ui->tableWidget->setItem(rowNum, colTime, item);item = new QTableWidgetItem(hexStr1, ctCmd1);ui->tableWidget->setItem(rowNum, colCmd1, item);item = new QTableWidgetItem(hexStr2, ctCmd2);ui->tableWidget->setItem(rowNum, colCmd2, item);item = new QTableWidgetItem(testdata, ctData);ui->tableWidget->setItem(rowNum, colData, item);}auto lastRowIndex = ui->tableWidget->rowCount()-1; // 最后一行的索引auto lastModelIndex = ui->tableWidget->model()->index(lastRowIndex, 0);ui->tableWidget->scrollTo(lastModelIndex);         // 滚动到最后一行
}QString Widget::ByteArrayToHexString(QByteArray &ba)
{QDataStream out(&ba,QIODevice::ReadWrite);   //将str的数据 读到out里面去QString buf;while(!out.atEnd()){qint8 outChar = 0;out >> outChar;   //每次一个字节的填充到 outcharQString str = QString("%1").arg(outChar&0xFF,2,16,QLatin1Char('0')).toUpper() + QString(" ");   //2 字符宽度buf += str;}return buf;
}void Widget::sendData()
{QString message = ui->lineEdit_sendData->text();if(ui->checkBox_hexSend->isChecked() == true){serialPort->write(QByteArray::fromHex(message.toLatin1()));}else{serialPort->write(message.toLatin1());}
}void Widget::receiveData()
{QByteArray message;QString hexMsg;message.append(serialPort->readAll());QDateTime time = QDateTime::currentDateTime();  //获取系统现在的时间QString date = time.toString("hh:mm:ss");       //设置显示格式if(ui->checkBox_hexRcv->isChecked() == true){tableRowCnt++;ui->tableWidget->setRowCount(tableRowCnt);createItemsARow(tableRowCnt-1,&message);hexMsg = ByteArrayToHexString(message).toLatin1();ui->textEdit_RecData->append(date+QString("->  ")+hexMsg.toUpper());}else{ui->textEdit_RecData->append(date+QString("->  ")+message);}
}
void Widget::openSerialport()
{ui->pushButton_closeSerialPort->setEnabled(true);ui->pushButton_openSerialPort->setEnabled(false);QList<QSerialPortInfo>  comList = QSerialPortInfo::availablePorts();QSerialPortInfo portInfo = comList.at(ui->comboBox_chooseCom->currentIndex());serialPort->setPort(portInfo);      //设置使用哪个串口if(serialPort->open(QIODevice::ReadWrite) == false){QMessageBox::critical(NULL, "critical", "找不到串口/串口被占用", QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);closeSerialport();}else{serialPort->setBaudRate(QSerialPort::Baud115200);serialPort->setDataBits(QSerialPort::Data8);serialPort->setParity(QSerialPort::NoParity);serialPort->setStopBits(QSerialPort::OneStop);serialPort->setFlowControl(QSerialPort::NoFlowControl);ui->pushButton_sendData->setEnabled(true);}
}void Widget::closeSerialport()
{if(serialPort->isOpen()){serialPort->clear();serialPort->close();}ui->pushButton_closeSerialPort->setEnabled(false);ui->pushButton_openSerialPort->setEnabled(true);
}void Widget::setBuad(int buad)
{QString str = ui->comboBox_setBuad->currentText();serialPort->setBaudRate(str.toInt());
}void Widget::clearRcv()
{ui->textEdit_RecData->clear();
}void Widget::on_btnClear_clicked()
{ui->lineEdit_protocalData->clear();
}void Widget::on_btnConvert_clicked()
{ui->lineEdit_protocalData->clear();bool ok;QString str = "A5";int val1= ui->lineEditCmd1->text().toInt(&ok,16);  //以十六进制数读入QString str1 = QString("%1").arg(val1, 2, 16, QLatin1Char('0'));int val2= ui->lineEditCmd2->text().toInt(&ok,16);  //以十六进制数读入QString str2 = QString("%1").arg(val2, 2, 16, QLatin1Char('0'));if ((str1.isEmpty())||(str2.isEmpty()))return;int val3= ui->lineEditData->text().toInt(&ok,16);  //以十六进制数读入QString str3 = QString("%1").arg(val3, 2, 16, QLatin1Char('0'));uint8_t len = 6 + static_cast<uint8_t>(str3.length()/2);QString hexStr = QString("%1").arg(len, 2, 16, QLatin1Char('0'));str.append(hexStr);str.append(str1);str.append(str2);str.append(str3);uint8_t crc;QString tmp;for(int i=0; i<str3.length(); i+=2){tmp = ui->lineEditData->text()[i];tmp += ui->lineEditData->text()[i+1];crc+= tmp.toInt(&ok,16);tmp = "";}crc += len;crc += val1;crc += val2;QString hexCrc= QString("%1").arg(crc, 2, 16, QLatin1Char('0'));str.append(hexCrc);str.append("5A");str = str.toUpper();ui->lineEdit_protocalData->insert(str);
}void Widget::sendProtocalHexData()
{QString message = ui->lineEdit_protocalData->text();serialPort->write(QByteArray::fromHex(message.toLatin1()));
}

ui

代码框架概览

演示视频

串口上位机(基本设置/协议收发)演示_哔哩哔哩_bilibili

串口上位机(基本设置/协议收发)演示

相关文章:

Qt串口基本设置与协议收发

前言 1.一直都想要做一个Qt上位机&#xff0c;趁着这个周末有时间&#xff0c;动手写一下 2.comboBox没有点击的信号&#xff0c;所以做了一个触发的功能 3.Qt的数据类型很奇怪&#xff0c;转来转去的我也搞得很迷糊 4.给自己挖个坑&#xff0c;下一期做一个查看波形的上位…...

interview3-微服务与MQ

一、SpringCloud篇 &#xff08;1&#xff09;服务注册 常见的注册中心&#xff1a;eureka、nacos、zookeeper eureka做服务注册中心&#xff1a; 服务注册&#xff1a;服务提供者需要把自己的信息注册到eureka&#xff0c;由eureka来保存这些信息&#xff0c;比如服务名称、…...

kafka详解一

kafka详解一 1、消息引擎背景 根据维基百科的定义&#xff0c;消息引擎系统是一组规范。企业利用这组规范在不同系统之间传递语义准确的消息&#xff0c;实现松耦合的异步式数据传递. 即&#xff1a;系统 A 发送消息给消息引擎系统&#xff0c;系统 B 从消息引擎系统中读取 A…...

Flutter yuv 转 rgb

1、引用yuv_converter库 yuv_converter: ^0.0.1 2、导入头文件&#xff1a; import package:yuv_converter/yuv_converter.dart;3、yuv转rgb YuvConverter.yuv420NV21ToRgba8888(yuvRawData, 512, 512) 根据yuv格式选择不同的api。 举个例子&#xff1a; void initState() …...

MySQL——子查询

2023.9.8 相关学习笔记&#xff1a; #子查询 /* 含义&#xff1a; 出现在其他语句中的select语句&#xff0c;称为子查询或内查询 外部的查询语句&#xff0c;称为主查询或外查询分类&#xff1a; 按子查询出现的位置&#xff1a;select后面&#xff1a;仅仅支持标量子查询fro…...

Java学习笔记---多态

面向对象三大特征之一&#xff08;继承&#xff0c;封装&#xff0c;多态&#xff09; 多态的应用场景&#xff1a;根据传递对象的不同&#xff0c;调用不同的show方法 一、多态的定义 同类型的对象&#xff0c;表现出的不同形态&#xff08;对象的多种形态&#xff09; 二…...

2023-09-10 LeetCode每日一题(课程表 II)

2023-09-10每日一题 一、题目编号 210. 课程表 II二、题目链接 点击跳转到题目位置 三、题目描述 现在你总共有 numCourses 门课需要选&#xff0c;记为 0 到 numCourses - 1。给你一个数组 prerequisites &#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示在…...

合并区间【贪心算法】

合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 class Solution {public int[][] merge(int[…...

2023,软件测试人的未来在哪里?

2023年&#xff0c;IT行业出现空前的萧条&#xff0c;首先是年初一开始各大厂像着了魔似的不约而同的纷纷裁员、降薪、奖金包缩水&#xff0c;随之而来的是需求萎缩&#xff0c;HC减少或封锁等等。 而有幸未被列入裁员名单的在职人员&#xff0c;庆幸之余也心有余悸&#xff0…...

Python中的Numpy向量计算(R与Python系列第三篇)

目录 一、什么是Numpy? 二、如何导入NumPy? 三、生成NumPy数组 3.1利用序列生成 3.2使用特定函数生成NumPy数组 &#xff08;1&#xff09;使用np.arange() &#xff08;2&#xff09;使用np.linspace() 四、NumPy数组的其他常用函数 &#xff08;1&#xff09;np.z…...

LeetCode刷题笔记【27】:贪心算法专题-5(无重叠区间、划分字母区间、合并区间)

文章目录 前置知识435. 无重叠区间题目描述参考<452. 用最少数量的箭引爆气球>, 间接求解直接求"重叠区间数量" 763.划分字母区间题目描述贪心 - 建立"最后一个当前字母"数组优化marker创建的过程 56. 合并区间题目描述解题思路代码① 如果有重合就合…...

nvidia-smi 命令详解

nvidia-smi 命令详解 1. nvidia-smi 面板解析2. 显存与GPU的区别 Reference: nvidia-smi命令详解 相关文章&#xff1a; nvidia-smi nvcc -V 及 CUDA、cuDNN 安装 nvidia-smi(NVIDIA System Management Interface) 是一种命令行实用程序&#xff0c;用于监控和管理 NVIDIA G…...

fork()函数的返回值

在程序中&#xff0c;int pd fork() 是一个典型的 fork() 调用。fork() 函数会创建一个新的进程&#xff0c;然后在父进程中返回子进程的进程ID&#xff08;PID&#xff09;&#xff0c;在子进程中返回0。所以 pd 的值会根据当前进程是父进程还是子进程而有所不同&#xff1a;…...

Stable Diffusion WebUI挂VPN不能跑图解决办法(Windows)

如何解决SD在打开VPN的状态不能运行的问题 在我们开VPN的时候会出现无法生成图片&#xff0c;也无法做其他任何事&#xff0c;这个时候是不是很着急呢&#xff1f; 别急&#xff0c;我这里会说明如何解决。 就像这样&#xff0c;运行半天生成不了图&#xff0c;有时还会出现…...

Android的本地数据

何为本地&#xff0c;即写完之后除非手动修改&#xff0c;否像嘎了一样在那固定死了 有些需求可能也会要求我们去写死数据&#xff0c;因为这需求是一成不变的&#xff0c;那么你通常会用什么方法写死呢&#xff1f; 1. 本地存储-SharedPreferences 此方法可以长时间保存于手…...

android NDK 开发包,网盘下载,不限速

记录下ndk 开发包的地址&#xff0c;分享给大家。 另外有Android studio的下载包&#xff0c; 在另一篇文章 链接&#xff1a;http://t.csdn.cn/JSr9x Android Studio.exe 下载 2023 最新更新&#xff0c;网盘下载_hsj-obj的博客-CSDN博客 主要是19-25&#xff0c;其他的没有…...

【每日一题Day320】LC2651计算列车到站时间 | 数学

计算列车到站时间【LC2651】](https://leetcode.cn/problems/calculate-delayed-arrival-time/) 给你一个正整数 arrivalTime 表示列车正点到站的时间&#xff08;单位&#xff1a;小时&#xff09;&#xff0c;另给你一个正整数 delayedTime 表示列车延误的小时数。 返回列车实…...

C语言柔性数组详解:让你的程序更灵活

柔性数组 一、前言二、柔性数组的用法三、柔性数组的内存分布四、柔性数组的优势五、总结 一、前言 仔细观察下面的代码&#xff0c;有没有看出哪里不对劲&#xff1f; struct S {int i;double d;char c;int arr[]; };还有另外一种写法&#xff1a; struct S {int i;double …...

Redis-带你深入学习数据类型list

目录 1、list列表 2、list相关命令 2.1、添加相关命令&#xff1a;rpush、lpush、linsert 2.2、查找相关命令&#xff1a;lrange、lindex、llen 2.3、删除相关命令&#xff1a;lpop、rpop、lrem、ltrim 2.4、修改相关命令&#xff1a;lset 2.5、阻塞相关命令&#xff1a…...

react拖拽依赖库react-dnd

注&#xff1a;对于表格自定义行可以拖拽和树自定义节点可以拖拽等比较适用&#xff0c;其余的拖拽处理可以使用dragstart&#xff0c;drop等js原生事件来实现 react-dnd使用方法很简单&#xff0c;直接上干货 第一步安装依赖并引入 import { DndProvider } from react-dnd;…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...