IAP固件升级进阶(Qt上位机)

前言

时隔近一年,再次接触IAP固件升级,这次修改了以前的一些bug,同时新增一些实用性的功能。

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

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

上位机界面

视频演示

当Up对iap固件升级的机制有了更深的理解后_哔哩哔哩_bilibili

固件升级指令

重要代码

1.通过拖拽实现文件读取

// MainWindow类的dragEnterEvent方法  
// 当鼠标拖动文件进入MainWindow的边界时,触发此方法  
void MainWindow::dragEnterEvent(QDragEnterEvent *event)  
{  // 检查拖动的数据是否包含URLs(即文件路径)  if(event->mimeData()->hasUrls())  // 如果包含URLs,则接受拖放操作  event->acceptProposedAction();  else  // 如果不包含URLs,则忽略拖放操作  event->ignore();  
}  // MainWindow类的dropEvent方法  
// 当鼠标在MainWindow内释放拖动的文件时,触发此方法  
void MainWindow::dropEvent(QDropEvent *event)  
{  // 获取拖放事件中的MIME数据  const QMimeData *mimeData = event->mimeData();  // 检查MIME数据是否包含URLs  if(mimeData->hasUrls())  {  // 获取URLs列表  QList<QUrl> urlList = mimeData->urls();  // 获取第一个URL的本地文件路径  QString fileName = urlList.at(0).toLocalFile();  // 获取textEdit_fwUpdateFile控件中的文本  QString text = ui->textEdit_fwUpdateFile->toPlainText();  // 检查文件名是否为空,并且textEdit_fwUpdateFile中的文本为空  if((!fileName.isEmpty()) && (text.isEmpty()))  {  // 检查文件扩展名是否为.bin  if (fileName.endsWith(".bin"))  {  // 创建一个QFile对象,用于读取文件  QFile file(fileName);  // 创建一个QFileInfo对象,用于获取文件信息  QFileInfo fileInfo(fileName);  // 初始化固件更新相关变量  fwPackIndex = 0;  // 获取文件大小  fwFileLen = fileInfo.size();  // 计算固件包的数量(根据fwPackLength,但fwPackLength在此代码中未定义)  fwPackNum = fwFileLen/fwPackLength+1;  // 尝试以只读方式打开文件  if(!file.open(QIODevice::ReadOnly))  // 如果文件打开失败,则返回不执行后续操作  return;  // 读取文件全部内容  binRawData = file.readAll();  // 在lineEdit_fwUpdateFile控件中显示文件名  ui->lineEdit_fwUpdateFile->setText(fileName);  // 在textEdit_fwUpdateFile控件中追加文件的十六进制表示  ui->textEdit_fwUpdateFile->append(binRawData.toHex());  // 关闭文件  file.close();  // 启用开始固件更新按钮  ui->pushButton_startFwUpdate->setEnabled(true);  // 禁用停止固件更新按钮  ui->pushButton_stopFwUpdate->setEnabled(false);  // 初始化固件更新状态  fwUpdateState = fwInit;  // 重置固件包索引  fwPackIndex = 0;  // 重置超出标记和索引(这些变量在代码中没有明确的定义和用途)  fwExceedFlag = 0;  fwExceedIndex = 0;  // 重置进度条  ui->progressBar_upgrade->reset();  }  else  {  // 如果文件扩展名不是.bin,则显示警告消息  QMessageBox::warning(this, tr("错误"), tr("无法打开正确的文件!"));  }  }  }  
}

2.串口初始化自动识别并连接带CH340的串口

void MainWindow::serialPortInit()
{// 获取所有可用的串口信息,并存储在QList<QSerialPortInfo>中  QList<QSerialPortInfo> comList = QSerialPortInfo::availablePorts();  // 清空comboBox_chooseCom控件中的所有项  ui->comboBox_chooseCom->clear();  // 创建一个QMap,用于存储串口名(portName)到comboBox中索引的映射  QMap<QString, int> portNameToIndexMap;  // 遍历所有可用的串口信息  foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) {  // 构建一个显示文本,由串口名和描述组成  QString displayText = info.portName() + ": " + info.description();  // 获取当前comboBox的项数,作为新项的索引  int index = ui->comboBox_chooseCom->count();  // 在comboBox_chooseCom控件中添加新的显示文本  ui->comboBox_chooseCom->addItem(displayText);  // 将当前串口名及其索引存入映射中  portNameToIndexMap.insert(info.portName(), index);  // 检查串口的描述或制造商信息是否包含"CH340"(不区分大小写)  if (info.description().contains("CH340", Qt::CaseInsensitive) ||  info.manufacturer().contains("CH340", Qt::CaseInsensitive)) {  // 使用映射来查找该串口名对应的索引  // 由于之前已经将串口名和索引存入映射,这里肯定能找到  if (portNameToIndexMap.contains(info.portName())) {  // 设置comboBox_chooseCom的当前项为找到的包含"CH340"的串口  ui->comboBox_chooseCom->setCurrentIndex(portNameToIndexMap.value(info.portName()));  }  // 找到后退出循环,因为我们只需要设置第一个匹配的串口  break;  }  }
}

3.IAP相关代码,跟上位机通信的逻辑部分

// IapRcvDataProc 函数用于处理接收到的IAP(In-Application Programming)数据消息  
void IapRcvDataProc(MSG_S stMsg)  
{  // 定义一个缓冲区用于存储接收到的消息数据  u8 MsgData[100];  // 将接收到的消息数据复制到MsgData缓冲区中  memcpy(MsgData, stMsg.szData, stMsg.u8Len);  u8 cmd = MsgData[__CmdIndex]; // 获取命令码  u8 i = 0; // 循环计数器  // 根据接收到的命令码的第3位(MsgData[2]的高4位)来构造回复命令的第1个字节  u8 replyCmd1 = 0xB0 | (MsgData[2] >> 4);  // 定义一个发送消息的缓冲区  u8 txMsg[20] = {0};  // 根据命令码执行不同的操作  switch(cmd)  {  case 0xF0: // 固件更新请求  // 读取固件更新标志位  SystemFlashRead(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stIap.u32FwFlag);  // 清除IAP结构体stIap的所有数据  memset(&stIap, 0, sizeof(IAP_S));  // 复制固件版本号到IAP结构体的flashData字段  memcpy(&stIap.stFwVer.flashData, &MsgData[6], 3);  // 将固件版本号写入到固件版本地址  SystemFlashWrite(__FwVersionAddr, __FwUpdateFlagSize, &stIap.stFwVer.flashData);  // 构造回复消息  txMsg[0] = replyCmd1;  txMsg[1] = cmd;  txMsg[2] = 1; // 表示成功的标志位  // 发送回复消息  UartLoadTxMsg(1, txMsg, 3);  break;  case 0xF1: // 固件擦除 // 设置固件标志位为无固件  stIap.u32FwFlag = __NO_FW;// 写入固件更新标志位  SystemFlashWrite(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stIap.u32FwFlag);  // 擦除应用程序空间  EraseFwSpace(__APP_START_ADDR, __APP_SIZE / __FLASH_PAGE_SIZE);  break;  case 0xF2: // 固件数据接收  // 更新帧索引  stIap.u16FwFrameIndex++;  // 设置接收帧的长度  stIap.stRcvFrame.u8Length = MsgData[6];  // 复制接收到的数据到IAP结构体的接收帧缓冲区  memcpy(&stIap.stRcvFrame.u8Data, &MsgData[7], stIap.stRcvFrame.u8Length);  // 逐块(每块4个字节)将数据写入到应用程序起始地址  for(i = 0; i < stIap.stRcvFrame.u8Length; i += 4)  {  FlashWriteWord(__APP_START_ADDR + stIap.u32WriteAddrIndex, *(u32 *)&stIap.stRcvFrame.u8Data[i]);  stIap.u32WriteAddrIndex += 4; // 更新写入地址  }  // 构造回复消息,包含帧编号和成功标志位  txMsg[0] = replyCmd1;  txMsg[1] = cmd;  txMsg[2] = MsgData[4]; // 帧编号的高字节  txMsg[3] = MsgData[5]; // 帧编号的低字节  txMsg[4] = 1; // 成功标志位  // 发送回复消息  UartLoadTxMsg(1, txMsg, 5);  break;  case 0xF3: // 固件更新完成  // 设置固件标志位为已有固件  stIap.u32FwFlag = __HAVE_FW_REPLY;  // 将固件标志位写入到固件更新标志地址  SystemFlashWrite(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stIap.u32FwFlag);  // 跳转到应用程序开始执行(切换到新的固件)  JumpToApplication();  break;  case 0xF4: // 固件版本查询  // 从固件版本地址读取固件版本数据  SystemFlashRead(__FwVersionAddr, __FwUpdateFlagSize, &stIap.stFwVer.flashData);  // 如果读取到的固件版本数据不是0xFFFFFFFF(表示无固件或无效版本)  if (stIap.stFwVer.flashData != 0xFFFFFFFF)   {  // 解析固件版本数据到结构体中的major、minor、patch字段  stIap.stFwVer.major =  stIap.stFwVer.flashData & 0xFF; // 低8位为major版本  stIap.stFwVer.minor =  (stIap.stFwVer.flashData >> 8) & 0xFF; // 接下来的8位为minor版本  stIap.stFwVer.patch =  (stIap.stFwVer.flashData >> 16) & 0xFF; // 再接下来的8位为patch版本  }  else  {  // 如果固件版本数据为0xFFFFFFFF,则清空固件版本结构体  memset(&stIap.stFwVer, 0, sizeof(stIap.stFwVer));  }  // 构造回复消息,包含固件版本号  txMsg[0] = replyCmd1;  txMsg[1] = cmd;  txMsg[2] = stIap.stFwVer.major; // 固件major版本  txMsg[3] = stIap.stFwVer.minor; // 固件minor版本  txMsg[4] = stIap.stFwVer.patch; // 固件patch版本  // 发送固件版本回复消息  UartLoadTxMsg(1, txMsg, 5);  break;}			
}

 4.APP检测将要更新的固件版本是否高于当前固件版本

// 从系统闪存中读取固件版本信息  
SystemFlashRead(__FwVersionAddr, __FwUpdateFlagSize, &stApp.stFwVer.flashData);  // 检查读取到的固件版本数据是否不是0xFFFFFFFF(通常表示无效或未设置的值)  
if (stApp.stFwVer.flashData != 0xFFFFFFFF)   
{  // 如果不是无效值,则解析固件版本数据  // 提取主要版本号(低8位)  stApp.stFwVer.major = stApp.stFwVer.flashData & 0xFF;  // 提取次要版本号(接下来的8位)  stApp.stFwVer.minor = (stApp.stFwVer.flashData >> 8) & 0xFF;  // 提取补丁版本号(再接下来的8位,但注意这里实际上只用了24位来表示版本)  stApp.stFwVer.patch = (stApp.stFwVer.flashData >> 16) & 0xFF;  
}  
else  
{  // 如果固件版本数据是无效值,则清空固件版本结构体  memset(&stApp.stFwVer, 0, sizeof(stApp.stFwVer));  
}  // 从接收到的消息数据中复制固件版本数据(MsgData是一个包含固件版本数据的数组)  
memcpy(&stRxFwVer.flashData, &MsgData[6], 3);  // 解析接收到的固件版本数据  
// 提取主要版本号  
stRxFwVer.major = stRxFwVer.flashData & 0xFF;  
// 提取次要版本号  
stRxFwVer.minor = (stRxFwVer.flashData >> 8) & 0xFF;  
// 提取补丁版本号  
stRxFwVer.patch = (stRxFwVer.flashData >> 16) & 0xFF;  // 调用函数比较当前固件版本(stApp.stFwVer)和接收到的固件版本(stRxFwVer)  
// 如果接收到的版本更高或等于当前版本,则返回1  
tmp = isNewVersionHigherOrEqual(&stApp.stFwVer, &stRxFwVer);  // 根据比较结果决定下一步操作  
if (tmp == 1)  
{  // 如果接收到的版本更高或等于当前版本  // 设置固件标志位为需要固件更新  stApp.u32FwFlag = __HAVE_FW_UPDATE;  // 将固件标志位写入到固件更新标志地址  SystemFlashWrite(__FwUpdateFlagAddr, __FwUpdateFlagSize, &stApp.u32FwFlag);  // 创建一个任务来跳转到IAP模式进行固件更新(假设Task2和JumpToIap是任务相关的函数和参数)  OS_TaskCreat(Task2, JumpToIap, 100);  // 注意:这里可能还需要发送一个确认消息给发送方,但代码中没有体现  
}  
else  
{  // 构造回复消息  txMsg[0] = replyCmd1; // 回复命令标识符  txMsg[1] = cmd;       // 原始命令  // 如果接收到的版本不是更高,则在回复消息中添加0表示无需更新  txMsg[2] = 0;  // 发送无需更新的确认消息  UartLoadTxMsg(1, txMsg, 3);  
} 

 5.上位机对收到命令进行处理

void MainWindow::rcvFwReply(QByteArray *protocalData)
{// 定义一个字节数组pRxData来存储接收到的数据uint8_t pRxData[100];// 定义一个QString对象来存储接收到的固件版本号信息(但在此段代码中未使用)QString rcvFwVer;// 将传入的QByteArray中的数据复制到pRxData数组中memcpy(pRxData, protocalData->data(), protocalData->size());// 清空并设置文本编辑框的换行模式ui->textEdit_fwRcvInfo->clear();ui->textEdit_fwRcvInfo->setWordWrapMode(QTextOption::WordWrap);// 根据接收到的数据的第四个字节进行不同的处理switch (pRxData[3]){case 0xF0:if(pRxData[4] == 1){// 重置fwExceedIndex和fwUpdateState变量,并启动相关的定时器fwExceedIndex = 0;fwUpdateState = fwTransfer;fwNumCmd(); // 调用fwNumCmd函数(该函数在此段代码中未给出)fwUpdateTimer->start(1000); // 定时器每隔1000毫秒(1秒)触发一次fwUpdateTimeOut->start(2000); // 另一个定时器每隔2000毫秒(2秒)触发一次// 在文本编辑框中插入“开始升级”文本ui->textEdit_fwRcvInfo->insertPlainText(QString("开始升级"));// 这里fwExceedIndex被重复设置为了0,可能是冗余代码fwExceedIndex = 0;}else if (pRxData[4] == 0){// 在文本编辑框中插入“固件已是最新版/对方拒绝升级”文本ui->textEdit_fwRcvInfo->insertPlainText(QString("固件已是最新版/对方拒绝升级"));}break;case 0xF2:// 从接收到的数据的第五个和第六个字节中解析出一个uint16_t类型的索引值uint16_t u16FwPackIndex;*(uint16_t *)&u16FwPackIndex = *(uint16_t *)&pRxData[4];// 如果解析出的索引值与当前fwPackIndex相等if(u16FwPackIndex == fwPackIndex){// 如果接收到的数据的第七个字节是1if(pRxData[6] == 1){// 重置fwExceedIndex变量fwExceedIndex = 0;// 在文本编辑框中插入“第 [索引值] 帧传输成功”的文本ui->textEdit_fwRcvInfo->insertPlainText(QString("第 ["));ui->textEdit_fwRcvInfo->insertPlainText(QString::number(fwPackIndex));ui->textEdit_fwRcvInfo->insertPlainText(QString("]  帧传输成功"));// 重置fwExceedIndex变量(这里再次被重复设置,可能是冗余代码)fwExceedIndex = 0;// 启动相关的定时器fwUpdateTimer->start(20); // 定时器每隔20毫秒触发一次fwUpdateTimeOut->start(2000); // 另一个定时器每隔2000毫秒触发一次// 更新进度条的值ui->progressBar_upgrade->setValue(static_cast<int>(fwPackIndex * 100.0 / fwPackNum));}else if (pRxData[6] == 0){// 在文本编辑框中插入“[索引值] 帧传输失败”的文本ui->textEdit_fwRcvInfo->insertPlainText(QString("["));ui->textEdit_fwRcvInfo->insertPlainText(QString::number(fwPackIndex));ui->textEdit_fwRcvInfo->insertPlainText(QString("] 帧传输失败"));}}break;case 0xF3:if(pRxData[4] == 1){// 更新状态为升级完成fwUpdateState = fwComplete;// 在文本编辑框中插入“对方回复升级成功”的文本ui->textEdit_fwRcvInfo->insertPlainText(QString("对方回复升级成功"));// 重置一些变量fwExceedIndex = 0;fwUpdateTimeOut->stop();}else if (pRxData[4] == 0){// 在文本编辑框中插入“对方回复升级失败”的文本ui->textEdit_fwRcvInfo->insertPlainText(QString("对方回复升级失败"));}// 无论成功还是失败,都重置UI中的按钮状态并更新fwUpdateState和其他相关变量ui->pushButton_startFwUpdate->setEnabled(true);ui->pushButton_stopFwUpdate->setEnabled(false);fwUpdateState = fwInit;fwPackIndex = 0;fwExceedFlag = 0;fwExceedIndex = 0;break;case 0xF4:// 从接收到的数据的第五、六、七个字节中分别获取固件的主版本号、次版本号和补丁版本号quint8 data1 = static_cast<quint8>(pRxData[4]);quint8 data2 = static_cast<quint8>(pRxData[5]);quint8 data3 = static_cast<quint8>(pRxData[6]);// 将十六进制转换为十进制int versionMajor = data1;int versionMinor = data2;int versionPatch = data3;// 组合成版本号字符串,并确保补丁版本号至少为两位数,不足补0QString versionString = QString("对方固件版本号为:%1.%2.%3").arg(versionMajor).arg(versionMinor).arg(versionPatch, 2, 10, QChar('0')).toUpper();// 在文本编辑框中插入版本号字符串ui->textEdit_fwRcvInfo->insertPlainText(versionString);break;}
}

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

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

相关文章

【C++】类和对象的引入

文章目录 前言一、类的定义二、类的访问控制与封装三、类的作用域四、类的实例化五、类的存储方式及大小计算六、隐藏的this指针 前言 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 C是基于面向对象的&…

算法题解记录29+++全排列(百日筑基)

一、题目描述 题目难度&#xff1a;中等 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示…

iCopy for Mac 剪切板 粘贴工具 历史记录 安装(保姆级教程,新手小白轻松上手)

Mac分享吧 文章目录 效果可留存文本、图片、文件等复制历史记录也可根据关键字进行历史记录检索点击一下&#xff0c;可复制双击两下&#xff0c;复制内容&#xff0c;并将信息粘贴至鼠标指针处 一、准备工作二、开始安装1、双击运行软件&#xff0c;将其从左侧拖入右侧文件夹…

Audio2Face相关建模BS绑定对应表

文章说明 相信大家都可以在网上看到52点位的ARKit绑定对照表&#xff0c;但英伟达旗下Audio2Face这款软件出来的数据应该如何映射到BS上边&#xff0c;都比较迷茫。 所以对于Unity方面的46点位在建模时的对照图&#xff0c;我这边总结了一下。发出来供大家参考&#xff0c;如果…

简易版 | 代码生成器(包含插件)

一、代码生成器 先导入依赖 <!-- Mybatis-Plus --> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.6</version> </dependency><!-- 代码生成器 --…

Cisco Packet Tracer实验(五)不同vlan间的通信简单配置

1&#xff0e;单臂路由(图) 环境&#xff1a;一台路由器&#xff0c;一台二层交换机&#xff0c;两台pc机 单臂路由&#xff08;Single Arm Routing&#xff09;是指在网络架构中&#xff0c;只有一个物理接口&#xff08;单臂&#xff09;连接到路由器三层交换机&#xff0c;而…

碉堡了!云原生大佬撰写的K8s学习指南,有点炸裂,建议运维都收藏!

在这个数字化转型的时代&#xff0c;容器化和云原生技术已成为软件开发的重要趋势。【Kubernetes】作为云原生领域的旗舰项目&#xff0c;不仅是一种容器编排工具&#xff0c;更是构建现代、弹性和可扩展应用程序的基础平台。 因此&#xff0c;对于运维人员来说&#xff0c;学…

基于51单片机的音乐彩灯设计

基于51单片机的音乐彩灯设计 &#xff08;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 由STC单片机ADC0809模块LM386功放模块喇叭音频接口发光二极管电源构成 1.通过音频线输入可以播放电脑、手机、MP3里面的音乐。 2.AD对音频…

17.零代码八爪鱼采集器数据采集与数据导出——如何导出不同格式数据和数据推送到数据库

首先&#xff0c;多数情况下免费版本的功能&#xff0c;已经可以满足绝大多数采集需求&#xff0c;想了解八爪鱼采集器版本区别的详情&#xff0c;请访问这篇帖子&#xff1a; 3.无代码爬虫八爪鱼采集器工具介绍——个人版本、团队版本的适用性_八爪鱼采集器 有单机版本吗-CSDN…

苹果电脑病毒怎么处理 苹果电脑病毒查杀用什么软件 苹果电脑病毒软件

苹果电脑并不是完全免疫于病毒的威胁&#xff0c;尤其是在使用了一些不安全的软件或网站后&#xff0c;可能会感染一些恶意程序&#xff0c;导致电脑运行缓慢&#xff0c;数据丢失&#xff0c;甚至被黑客控制。那么&#xff0c;苹果电脑病毒怎么处理呢&#xff1f;苹果电脑病毒…

vue分页

先看效果 再看代码 <!-- 分页 --><div v-if"pageParams.pageCount > 1" class"flex justify-end mt-6"><n-paginationv-model:page"pageParams.page" v-model:page-size"pageParams.pageSize" :page-count"pa…

Hazelcast 分布式缓存 在Seatunnel中的使用

1、背景 最近在调研seatunnel的时候&#xff0c;发现新版的seatunnel提供了一个web服务&#xff0c;可以用于图形化的创建数据同步任务&#xff0c;然后管理任务。这里面有个日志模块&#xff0c;可以查看任务的执行状态。其中有个取读数据条数和同步数据条数。很好奇这个数据…

羊城杯 2020 a_piece_of_java

考点:JDBC反序列化打CC链动态代理类触发readobject 一眼看过去 好像只有一个mysql-connector-java 可以利用jdbc 可能的攻击路径就有1) Mysql服务器任意文件读取 2) JDBC反序列化打依赖链 出现了一个不常见的依赖库 serialkiller 做了反序列化的过滤器 可以尝试查看其源码 htt…

2000-2022年上市公司员工、工资数据

2000-2022年上市公司员工、工资数据 1、时间&#xff1a;2000-2022年 2、来源&#xff1a;上市公司年报 3、指标&#xff1a;年份、股票代码、股票简称、行业名称、行业代码、省份、城市、区县、行政区划代码、城市代码、区县代码、首次上市年份、上市状态、员工人数_人、应…

Windows 与 Java 环境下的 Redis 利用分析

1 前言 在最近的一次攻防演练中&#xff0c;遇到了两个未授权访问的 Redis 实例。起初以为可以直接利用&#xff0c;但后来发现竟然是Windows Java (Tomcat)。因为网上没有看到相关的利用文章&#xff0c;所以在经过摸索&#xff0c;成功解决之后决定简单写一写。 本文介绍了…

【工程2区】毕业神刊 —— 1-2个月录用!非黑!非预警!

【欧亚科睿学术】 电力能源类SCIE ✅ 进展超顺 ✅ 录用率高 ✅ 领域相关均可 【期刊简介】IF&#xff1a;1.0-2.0&#xff0c;JCR2区&#xff0c;中科院4区 【版面类型】正刊&#xff0c;仅少量版面 【终审周期】走期刊部系统&#xff0c;预计3个月左右录用 【检索情况…

使用SpringBoot对接Kafka

Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 一、Kafka与流处理 我们先来看看比较正式的介绍&#xff1a;Kafka是一种流处理平台&#xff0c;由LinkedIn公司创建&#xff0c;现在是Apache下的开源项目。Kafka通过发布/订阅机制实现消息的异步传输和处理。它具有高…

组件二次封装,通过属性事件透传,插槽使用,组件实例方法的绑定,深入理解 Vue.js 组件扩展与插槽

透传&#xff0c;插槽&#xff0c;组件实例方法的绑定&#xff0c;深入理解 Vue.js 组件扩展与插槽 前言 Vue.js 提供了强大的组件化系统&#xff0c;允许开发者构建可复用、可组合的UI组件。在实际项目中&#xff0c;直接使用第三方库提供的基础组件&#xff08;如Element UI…

Internet Download Manager(IDM6.41)软件下载-详细安装教程视频

Internet Download Manager有一个智能下载逻辑加速器&#xff0c;具有智能动态文件分割和安全的多部分下载技术&#xff0c;可以加速下载。与其他下载加速器和管理器不同&#xff0c;Internet下载管理器在下载开始之前对文件进行分段&#xff0c;而Internet下载管理器在下载过程…

用TensorRT-LLM进行LLama的推理和部署

Deploy an AI Coding Assistant with NVIDIA TensorRT-LLM and NVIDIA Triton | NVIDIA Technical BlogQuick Start Guide — tensorrt_llm documentation (nvidia.github.io) 使用TensorRT-LLM的源码&#xff0c;来下载docker并在docker里编译TensorRT-LLM&#xff1b; 模型…