Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)
文章目录
- 前言
- 一、引入Modbus模块
- 二、Modbus设备的连接
- 三、各寄存器数据的读取
- 四、各寄存器数据的写入
- 五、示例完整代码
- 总结
前言
本文主要讲述了使用Qt的Modbus模块来进行ModbusTcp的通信,实现对PLC的线圈寄存器和保持寄存器的读写,基于TCP/IP的Modbus协议的内容我就不做过多解释了,详见参考文章。在本文示例中采用QModbusTcpClient类作为Modbus客户端(主站),PLC作为从站,封装了一个自己的MyModbus类,希望可以帮助到大家,如有错误之处,欢迎大家批评指正。
项目效果

提示:以下是本篇文章正文内容,下面案例可供参考
一、引入Modbus模块
1.这里我实现了自己的MyModbus类的封装,使用了pri子模块的方式,也是方便日后进行此模块的复用
pri中引入Modbus模块:
MyModbus.pri
QT += serialbus serialport
MyModbus类中添加相关头文件
#include <QModbusTcpClient>
#include <QModbusDataUnit>
二、Modbus设备的连接
1.ModbusTcp的连接只需要配置好连接参数IP+Port
//判断当前连接状态是否为断开状态
if(myClient->state() != QModbusDevice::ConnectedState)
{//配置ModbusTcp的连接参数IP+PortmyClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter,ip);myClient->setConnectionParameter(QModbusDevice::NetworkPortParameter,port);myClient->connectDevice();
}
三、各寄存器数据的读取
1.Modbus中有4种操作对象,这4种都能进行读取操作:线圈、离散输入、保持寄存器、输入寄存器
//读取modbus设备各寄存器数据
//typeNum:1_线圈 2_离散输入 3_保持 4_输入
bool MyModbus::readModbusData(int typeNum,int startAdd,quint16 numbers)
{if(myClient->state() != QModbusDevice::ConnectedState){return false;}//确定寄存器类型QModbusDataUnit ReadUnit;if(typeNum == 1){ReadUnit = QModbusDataUnit(QModbusDataUnit::Coils,startAdd,numbers);}else if(typeNum == 2){ReadUnit = QModbusDataUnit(QModbusDataUnit::DiscreteInputs,startAdd,numbers);}else if(typeNum == 3){ReadUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAdd,numbers);}else if(typeNum == 4){ReadUnit = QModbusDataUnit(QModbusDataUnit::InputRegisters,startAdd,numbers);}else{LOGDEBUG<<"读取寄存器类型错误";return false;}LOGDEBUG<<"readModbusData typeNum:"<<typeNum;//多读if(auto *reply = myClient->sendReadRequest(ReadUnit,1)){if(!reply->isFinished()){if((typeNum == 1) || (typeNum == 2)){QObject::connect(reply,&QModbusReply::finished,this,&MyModbus::slot_readReadyCoils); //读取线圈}if((typeNum == 3) || (typeNum == 4)){QObject::connect(reply,&QModbusReply::finished,this,&MyModbus::slot_readReadyRegisters); //读取寄存器}//reply->deleteLater();return true;}else{reply->deleteLater();return false;}}else{LOGDEBUG<<"读取错误:" + myClient->errorString();return false;}
}
四、各寄存器数据的写入
1.Modbus中4种操作对象只有这2种能进行写入操作:线圈 、保持寄存器
//对modbus设备各寄存器写入数据
//typeNum:1_线圈 2_保持 (这两类寄存器可读可写,其余的只读)
bool MyModbus::writeModbusData(int typeNum,int startAdd,int writeNum)
{if(myClient->state() != QModbusDevice::ConnectedState){return false;}//确定寄存器类型QModbusDataUnit writeUnit;if(typeNum == 1){writeUnit = QModbusDataUnit(QModbusDataUnit::Coils,startAdd,1); //写入一个数据writeUnit.setValue(0,writeNum);//单写//bool ok;//quint16 hexData = writeData.toInt(&ok,16); //转16进制}else if(typeNum == 2){writeUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAdd,2); //写入两个数据quint16 uData16[2] = {0};uData16[0] = writeNum & 0xffff;uData16[1] = (writeNum >> 16) & 0xffff;writeUnit.setValue(0,uData16[0]);writeUnit.setValue(1,uData16[1]);//LOGDEBUG<<"uData16[0]:"<<uData16[0]<<" uData16[1]:"<<uData16[1]<<" writeNum:"<<writeNum;}else{LOGDEBUG<<"写入寄存器类型错误";return false;}//LOGDEBUG<<"writeModbusData typeNum:"<<typeNum<<" writeNum:"<<writeNum;if(auto *reply = myClient->sendWriteRequest(writeUnit,1)){if(!reply->isFinished()){connect(reply,&QModbusReply::finished,this,[reply](){if(reply->error() == QModbusDevice::NoError){reply->deleteLater();return true;}else{LOGDEBUG<<"写入返回错误:"<<reply->error();reply->deleteLater();return false;}});}else{reply->deleteLater();return false;}}else{LOGDEBUG<<"写入错误:" + myClient->errorString();return false;}return true;
}
五、示例完整代码
这里是示例项目的完整代码,包含pro主程序和MyModbus.pri子文件
1.ModbusTest.pro
QT += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11#定义编译选项.QT_DEPRECATED_WARNINGS表示当Qt的某些功能被标记为过时的,那么编译器会发出警告.
DEFINES += QT_DEPRECATED_WARNINGS#设置字符(MSCV编译器下防止中文乱码)
contains( CONFIG,"msvc" ):QMAKE_CXXFLAGS += /source-charset:utf-8 /execution-charset:utf-8
contains( CONFIG,"msvc" ):QMAKE_CFLAGS +=/source-charset:utf-8 /execution-charset:utf-8include (./MyModbus/MyModbus.pri)SOURCES += \main.cpp \widget.cppHEADERS += \widget.hFORMS += \widget.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
2.MyModbus.pri
QT += serialbus serialportHEADERS += \$$PWD/mymodbus.hSOURCES += \$$PWD/mymodbus.cpp
3.mymodbus.h
#ifndef MYMODBUS_H
#define MYMODBUS_H#include <QObject>
#include <QModbusTcpClient>
#include <QModbusDataUnit>
#include <QDebug>#define LOGDEBUG qDebug()<<__FILE__<<__LINE__class MyModbus : public QObject
{Q_OBJECT
public:explicit MyModbus(QObject *parent = nullptr);~MyModbus();void initModbus();void connectToModbus(QString ip,int port);bool readModbusData(int typeNum,int startAdd,quint16 numbers);bool writeModbusData(int typeNum,int startAdd,int writeNum);signals:void signal_stateChanged(bool flag);void signal_readCoils(QVector<quint16> vAllData);void signal_readRegisters(int resultNum);private slots:void slot_stateChanged();void slot_readReadyCoils();void slot_readReadyRegisters();private:QModbusTcpClient *myClient;};
#endif // MYMODBUS_H
4.mymodbus.cpp
#include "mymodbus.h"MyModbus::MyModbus(QObject *parent) : QObject(parent)
{this->initModbus();
}MyModbus::~MyModbus()
{}//初始化
void MyModbus::initModbus()
{myClient = new QModbusTcpClient();//connect(myClient,SIGNAL(stateChanged()),this,SLOT(slot_stateChanged()));connect(myClient,&QModbusClient::stateChanged,this,&MyModbus::slot_stateChanged);
}//连接到modbus设备
void MyModbus::connectToModbus(QString ip,int port)
{if(!myClient){return;}//判断当前连接状态是否为断开状态if(myClient->state() != QModbusDevice::ConnectedState){//配置ModbusTcp的连接参数IP+PortmyClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter,ip);myClient->setConnectionParameter(QModbusDevice::NetworkPortParameter,port);myClient->connectDevice();}//else//{// myClient->disconnectDevice();//}
}//读取modbus设备各寄存器数据
//typeNum:1_线圈 2_离散输入 3_保持 4_输入
bool MyModbus::readModbusData(int typeNum,int startAdd,quint16 numbers)
{if(myClient->state() != QModbusDevice::ConnectedState){return false;}//确定寄存器类型QModbusDataUnit ReadUnit;if(typeNum == 1){ReadUnit = QModbusDataUnit(QModbusDataUnit::Coils,startAdd,numbers);}else if(typeNum == 2){ReadUnit = QModbusDataUnit(QModbusDataUnit::DiscreteInputs,startAdd,numbers);}else if(typeNum == 3){ReadUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAdd,numbers);}else if(typeNum == 4){ReadUnit = QModbusDataUnit(QModbusDataUnit::InputRegisters,startAdd,numbers);}else{LOGDEBUG<<"读取寄存器类型错误";return false;}LOGDEBUG<<"readModbusData typeNum:"<<typeNum;//多读if(auto *reply = myClient->sendReadRequest(ReadUnit,1)){if(!reply->isFinished()){if((typeNum == 1) || (typeNum == 2)){QObject::connect(reply,&QModbusReply::finished,this,&MyModbus::slot_readReadyCoils); //读取线圈}if((typeNum == 3) || (typeNum == 4)){QObject::connect(reply,&QModbusReply::finished,this,&MyModbus::slot_readReadyRegisters); //读取寄存器}//reply->deleteLater();return true;}else{reply->deleteLater();return false;}}else{LOGDEBUG<<"读取错误:" + myClient->errorString();return false;}
}//对modbus设备各寄存器写入数据
//typeNum:1_线圈 2_保持 (这两类寄存器可读可写,其余的只读)
bool MyModbus::writeModbusData(int typeNum,int startAdd,int writeNum)
{if(myClient->state() != QModbusDevice::ConnectedState){return false;}//确定寄存器类型QModbusDataUnit writeUnit;if(typeNum == 1){writeUnit = QModbusDataUnit(QModbusDataUnit::Coils,startAdd,1); //写入一个数据writeUnit.setValue(0,writeNum);//单写//bool ok;//quint16 hexData = writeData.toInt(&ok,16); //转16进制}else if(typeNum == 2){writeUnit = QModbusDataUnit(QModbusDataUnit::HoldingRegisters,startAdd,2); //写入两个数据quint16 uData16[2] = {0};uData16[0] = writeNum & 0xffff;uData16[1] = (writeNum >> 16) & 0xffff;writeUnit.setValue(0,uData16[0]);writeUnit.setValue(1,uData16[1]);//LOGDEBUG<<"uData16[0]:"<<uData16[0]<<" uData16[1]:"<<uData16[1]<<" writeNum:"<<writeNum;}else{LOGDEBUG<<"写入寄存器类型错误";return false;}//LOGDEBUG<<"writeModbusData typeNum:"<<typeNum<<" writeNum:"<<writeNum;if(auto *reply = myClient->sendWriteRequest(writeUnit,1)){if(!reply->isFinished()){connect(reply,&QModbusReply::finished,this,[reply](){if(reply->error() == QModbusDevice::NoError){reply->deleteLater();return true;}else{LOGDEBUG<<"写入返回错误:"<<reply->error();reply->deleteLater();return false;}});}else{reply->deleteLater();return false;}}else{LOGDEBUG<<"写入错误:" + myClient->errorString();return false;}return true;
}//监听TCP连接的状态,若状态发生改变,发出对应的信号
void MyModbus::slot_stateChanged()
{LOGDEBUG<<myClient->state();if(myClient->state() == QModbusDevice::ConnectedState){emit signal_stateChanged(true);}else if(myClient->state() == QModbusDevice::UnconnectedState){emit signal_stateChanged(false);}
}//接收到读取线圈/离散输入寄存器请求后执行的槽函数
void MyModbus::slot_readReadyCoils()
{QVector<quint16> vAllData;QModbusReply *reply = qobject_cast<QModbusReply *>(sender());if(!reply){LOGDEBUG<<"读取线圈/离散输入寄存器错误";return;}if(reply->error() == QModbusDevice::NoError){const QModbusDataUnit unit = reply->result();vAllData = unit.values();emit signal_readCoils(vAllData);}else{LOGDEBUG<<"线圈/离散输入寄存器回复错误:"<<reply->error();}reply->deleteLater();
}//接收到读取保持/输入寄存器请求后执行的槽函数
void MyModbus::slot_readReadyRegisters()
{QModbusReply *reply = qobject_cast<QModbusReply *>(sender());if(!reply){LOGDEBUG<<"读取保持/输入寄存器错误";return;}if(reply->error() == QModbusDevice::NoError){const QModbusDataUnit unit = reply->result();auto valueList = unit.values();int nSize = valueList.size();if(nSize == 2){quint16 uData16[2] = {0};uData16[0] = valueList[0];uData16[1] = valueList[1];int resultNum = uData16[0] | (uData16[1] << 16);//LOGDEBUG<<"uData16[0]:"<<uData16[0]<<" uData16[1]:"<<uData16[1]<<" resultNum:"<<resultNum;emit signal_readRegisters(resultNum);}else{LOGDEBUG<<"保持寄存器返回数据错误,个数:"<<nSize;}}else{LOGDEBUG<<"保持/输入寄存器回复错误:"<<reply->error();}reply->deleteLater();
}/*
//读取保持/输入寄存器数据的另一种方式,已废弃
//当前数据格式为大端模式,高位存低地址
//判断正负数,以高8位的16进制是否为f判断
int resultNum = 0;
if(QString::number(valueList[1],16).left(1) == "f") //负数
{//判断是否小于-65535,高16位的10进制为65535if(valueList[1] == 65535){resultNum = valueList[0] - 65536;}else{resultNum = (valueList[1] - 65535) * 65536 + (valueList[0] - 65536);}
}
else
{//判断是否大于65535,高16位的10进制大于0if(valueList[1] > 0){resultNum = valueList[1] * 65536 + valueList[0];}else{resultNum = valueList[0];}
}
*/
5.widget.h
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QFileDialog>
#include <QDateTime>
#include <QMessageBox>
#include "MyModbus/mymodbus.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void initWidget();private slots:void slot_stateChanged(bool flag);void slot_readCoils(QVector<quint16> vAllData);void slot_readRegisters(int resultNum);private slots:void on_pb_connect_clicked();void on_pb_readM_clicked();void on_pb_writeM_clicked();void on_pb_readD_clicked();void on_pb_writeD_clicked();private:Ui::Widget *ui;MyModbus *m_myModsbus; //MyModbus对象};
#endif // WIDGET_H
6.widget.cpp
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->initWidget();
}Widget::~Widget()
{delete ui;
}void Widget::initWidget()
{//初始化MyModbus对象m_myModsbus = new MyModbus();connect(m_myModsbus,SIGNAL(signal_stateChanged(bool)),this,SLOT(slot_stateChanged(bool)));connect(m_myModsbus,SIGNAL(signal_readCoils(QVector<quint16>)),this,SLOT(slot_readCoils(QVector<quint16>)));connect(m_myModsbus,SIGNAL(signal_readRegisters(int)),this,SLOT(slot_readRegisters(int)));}void Widget::slot_stateChanged(bool flag)
{if(flag){ui->lb_state->setText("连接成功");ui->te_show->appendPlainText(QTime::currentTime().toString("hh:mm:ss: ") + "连接成功");QMessageBox::warning(this,"警告","连接成功!");}else{ui->lb_state->setText("连接断开");ui->te_show->appendPlainText(QTime::currentTime().toString("hh:mm:ss: ") + "连接断开");QMessageBox::warning(this,"警告","连接断开!");}
}void Widget::slot_readCoils(QVector<quint16> vAllData)
{LOGDEBUG<<"readCoils size:"<<vAllData.size();for(int i=0;i<vAllData.size();i++){LOGDEBUG<<"i:"<<vAllData[i];ui->te_show->appendPlainText(QTime::currentTime().toString("hh:mm:ss: ") + "读取M区返回数据:" + QString::number(vAllData[i]));ui->le_dataM->setText(QString::number(vAllData[0]));}
}void Widget::slot_readRegisters(int resultNum)
{LOGDEBUG<<"resultNum:"<<resultNum;ui->te_show->appendPlainText(QTime::currentTime().toString("hh:mm:ss: ") + "读取D区返回数据:" + QString::number(resultNum));ui->le_dataD->setText(QString::number(resultNum));
}void Widget::on_pb_connect_clicked()
{QString ip = ui->le_ip->text();int port = ui->le_port->text().toInt();LOGDEBUG<<"ip:"<<ip<<" port:"<<port;ui->te_show->appendPlainText(QTime::currentTime().toString("hh:mm:ss: ") + "ip:" + ip + " port:" + QString::number(port));//连接到modbus设备m_myModsbus->connectToModbus(ip,port);
}void Widget::on_pb_readM_clicked()
{int startAdd = ui->le_addressM->text().toInt();LOGDEBUG<<"startAdd:"<<startAdd;if(!m_myModsbus->readModbusData(1,startAdd,1)){QMessageBox::warning(this,"警告","M区数据读取失败!");}
}void Widget::on_pb_writeM_clicked()
{int startAdd = ui->le_addressM->text().toInt();int writeNum = ui->le_dataM->text().toInt();LOGDEBUG<<"startAdd:"<<startAdd<<" writeNum:"<<writeNum;//单写if(!m_myModsbus->writeModbusData(1,startAdd,writeNum)){QMessageBox::warning(this,"警告","M区数据写入失败!");}
}void Widget::on_pb_readD_clicked()
{int startAdd = ui->le_addressD->text().toInt();LOGDEBUG<<"startAdd:"<<startAdd;if(!m_myModsbus->readModbusData(3,startAdd,2)){QMessageBox::warning(this,"警告","D区数据读取失败!");}
}void Widget::on_pb_writeD_clicked()
{int startAdd = ui->le_addressD->text().toInt();int writeNum = ui->le_dataD->text().toInt();;LOGDEBUG<<"startAdd:"<<startAdd<<" writeNum:"<<writeNum;//进行写入寄存器数据的处理if(!m_myModsbus->writeModbusData(2,startAdd,writeNum)){QMessageBox::warning(this,"警告","D区数据写入失败!");}
}
7.main.cpp
#include "widget.h"
#include <QApplication>
#include <QMutex>//程序输出日志
void outputMessage(QtMsgType type,const QMessageLogContext &context,const QString &msg)
{static QMutex mutex;mutex.lock();//初始化log文件夹QString logFilePath = QCoreApplication::applicationDirPath() + "/LogFile/";QDir dstDir(logFilePath);if(!dstDir.exists()){if(!dstDir.mkpath(logFilePath)){LOGDEBUG<<"程序输出日志创建失败!";}else{LOGDEBUG<<"程序输出日志创建成功!";}}//获取输出内容QString debugMsg;if(type == QtDebugMsg){QString debugDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");debugMsg = QString("%1\n%2%3").arg(debugDateTime).arg(msg).arg(context.function);}//保存文件QString curDate = QDate::currentDate().toString("yyyyMMdd");QString logFile = logFilePath + "log_" + curDate + ".txt";QFile file(logFile);file.open(QIODevice::WriteOnly | QIODevice::Append);QTextStream textStream(&file);textStream << debugMsg << "\n\n";file.flush();file.close();mutex.unlock();
}int main(int argc, char *argv[])
{QApplication a(argc, argv);qInstallMessageHandler(outputMessage);Widget w;w.show();return a.exec();
}
8.widget.ui

总结
基于Qt使用这个ModbusTcp协议进行通信,还是比较简单的,首先就是确定下寄存器的类型,直接使用相关的函数进行读写。需要注意的一点是在进行保持寄存器读写的时候,我们PLC某个地址上的数是个16位的数,所以在进行32位有符号数读写的时候,要考虑两个相邻地址的组合,文中就对此进行了处理,这种情况下PLC那边也别忘记进行相应的设置哈。
hello:
共同学习,共同进步,如果还有相关问题,可在评论区留言进行讨论。
参考博客:
QT下的Modbus TCP 通讯
C++与PLC通过Modbus TCP协议进行PLC内部寄存器的值的读取/写入总结
相关文章:
Qt下使用ModbusTcp通信协议进行PLC线圈/保持寄存器的读写(32位有符号数)
文章目录 前言一、引入Modbus模块二、Modbus设备的连接三、各寄存器数据的读取四、各寄存器数据的写入五、示例完整代码总结 前言 本文主要讲述了使用Qt的Modbus模块来进行ModbusTcp的通信,实现对PLC的线圈寄存器和保持寄存器的读写,基于TCP/IP的Modbus…...
ElasticSearch学习2
1、索引的操作 1、创建索引 对ES的操作其实就是发送一个restful请求,kibana中在DevTools中进行ES操作 创建索引时需要注意ES的版本,不同版本的ES创建索引的语句略有差别,会导致失败 如下创建一个名为people的索引,settings&…...
3D角色展示
先看效果: 再看代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>3D卡片悬停</title><style>font-face {font-family: "Exoct";src: url("htt…...
前端面试:【Angular】打造强大Web应用的全栈框架
嗨,亲爱的Angular探险家!在前端开发的旅程中,有一个全栈框架,那就是Angular。Angular提供了模块化、组件化、依赖注入、路由和RxJS等特性,助力你构建强大、可扩展的Web应用。 1. 什么是Angular? Angular是…...
数据结构:栈和队列
文章目录 一、栈1.栈的概念及结构1.栈的概念及结构2.栈的实现 2.栈的顺序表实现1.栈的结构体和实现的功能函数2.栈的初始化,入栈和出栈操作3.栈的其他操作 3.栈的链表实现1.栈的结构体和实现的功能函数2.栈功能函数的实现 二、队列1.队列的概念及结构1.队列的概念及…...
SpringCloud Gateway服务网关的介绍与使用
目录 1、网关介绍2、SpringCloudGateway工作原理3、三大组件3.1 、Route(路由)3.2、断言 Predicate3.3、过滤器 filter 4、Gateway整合nacos的使用4.1 、引入依赖4.2、 编写基础类和启动类4.3、 编写基础配置和路由规则4.4 、测试结果 1、网关介绍 客户…...
深入解析:如何打造高效的直播视频美颜SDK
在当今数字化时代,视频直播已经成为人们交流、娱乐和信息传递的重要方式。然而,许多人在直播时都希望能够呈现出最佳的外观,这就需要高效的直播视频美颜技术。本文将深入解析如何打造高效的直播视频美颜SDK,以实现令人满意的视觉效…...
每日一博 - MPP(Massively Parallel Processing,大规模并行处理)架构
文章目录 概述优点缺点小结 概述 MPP(Massively Parallel Processing,大规模并行处理)架构是一种常见的数据库系统架构,主要用于提高数据处理性能。它通过将多个单机数据库节点组成一个集群,实现数据的并行处理。 在 …...
ssh框架原理及流程
1.hibernate工作原理: 读取并解析配置文件读取并解析映射信息,创建sessionFactory打开session创建事务transaction持久化操作提交事务关闭session关闭sessionFactory 为什么使用: 对JDBC访问数据库的代码做了封装,大大简化了数据…...
eslint 配置和用法
在一个使用Webpack的项目中配置ESLint,你可以按照以下步骤操作: 首先,你需要在你的项目中安装ESLint和对应的Webpack loader。你可以使用npm或者yarn来安装。在你的项目根目录下打开终端,然后运行以下命令: 使用npm&…...
字符设备驱动实例(PWM和RTC)
目录 五、PWM 六、RTC 五、PWM PWM(Pulse Width Modulation,脉宽调制器),顾名思义就是一个输出脉冲宽度可以调整的硬件器件,其实它不仅脉冲宽度可调,频率也可以调整。它的核心部件是一个硬件定时器,其工作原理可以用…...
Ribbon 源码分析
Ribbon 源码分析 Ribbon Debug 分析 断点 LoadBalancerInterceptor LoadBalancerInterceptor 实现了 ClientHttpRequestInterceptor 接口,重写了其中的 intercept 方法,用来拦截请求; 获取原始的 uri 和 服务名,调用 LoadBalanc…...
【1-3章】Spark编程基础(Python版)
课程资源:(林子雨)Spark编程基础(Python版)_哔哩哔哩_bilibili 第1章 大数据技术概述(8节) 第三次信息化浪潮:以物联网、云计算、大数据为标志 (一)大数据 大数据时代到来的原因…...
宇宙原理:黑洞基础。
宇宙原理:黑洞基础TOC 黑洞的数理基础:一个由满数组成的数盘,经过自然演进,将会逐步稀疏化、最终会向纯数方案发展;纯数方案虽然只有{2}、无数(虚拟)、{0,1,2,3}(虚拟)、…...
分类预测 | MATLAB实现SCNGO-CNN-LSTM-Attention数据分类预测
分类预测 | MATLAB实现SCNGO-CNN-LSTM-Attention数据分类预测 目录 分类预测 | MATLAB实现SCNGO-CNN-LSTM-Attention数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.SCNGO-CNN-LSTM-Attention数据分类预测程序,改进算法,融合正余弦和…...
Android学习之路(7) Frament
Fragment 表示应用界面中可重复使用的一部分。fragment 定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。fragment 不能独立存在。它们必须由 activity 或其他 fragment 托管。fragment 的视图层次结构会成为宿主的视图层次结构的…...
metallb , istio ingress 部署httpbin使用例子
安装metaillb,参考:Kubernetes的负载均衡方案:MetalLB - 文章详情 wget https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-frr.yaml -O metallb.yaml kubectl apply -f metallb-frr.yaml 配置负载均衡ip池 apiVe…...
基于swing的销售管理系统java仓库库存信息jsp源代码mysql
本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 基于swing的销售管理系统 系统有1权限:管…...
FreeCAD傻瓜式教程之约束设定和构建实体、开孔、调整颜色等
本内容基于官方教程中的绘制简单的零件中的体会,在初次绘制的时候,总是无法完成,几经尝试才发现其关键点所在,以此文记录,用以被查资料,同时也希望能够帮到纯白新手快速熟悉该软件的绘图方法。 一、. 打开…...
代码随想录算法训练营day41 | 343. 整数拆分,96. 不同的二叉搜索树
目录 343. 整数拆分 96. 不同的二叉搜索树 343. 整数拆分 类型:动态规划 难度:medium 思路: dp[i]所用的拆分方法至少已经拆分了两次,比如dp[2]1,小于2,在大于2的数中,最后的2是不会拆的。 …...
Cacao部署与发布指南:从开发到上架App Store的完整流程
Cacao部署与发布指南:从开发到上架App Store的完整流程 【免费下载链接】cacao Rust bindings for AppKit (macOS) and UIKit (iOS/tvOS). Experimental, but working! 项目地址: https://gitcode.com/gh_mirrors/ca/cacao Cacao是一个为macOS和iOS/tvOS提供…...
别再死记硬背了!用Protege手把手教你构建知识图谱的‘骨架’(本体建模实战)
别再死记硬背了!用Protege手把手教你构建知识图谱的‘骨架’(本体建模实战) 第一次接触知识图谱时,我被各种晦涩的术语搞得晕头转向——RDF、OWL、SPARQL...直到一位前辈告诉我:"别急着背概念,先搭个…...
【2026年最新600套毕设项目分享】答题小程序(30212)
有需要的同学,源代码和配套文档领取,加文章最下方的名片哦 一、项目演示 项目演示视频 二、资料介绍 完整源代码(前后端源代码SQL脚本)配套文档(LWPPT开题报告/任务书)远程调试控屏包运行一键启动项目&…...
如何在Windows上轻松安装APK文件:APK Installer终极指南
如何在Windows上轻松安装APK文件:APK Installer终极指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 还在为Windows电脑无法直接运行Android应用而烦恼吗…...
音频令牌动态压缩技术:提升大语言模型语音处理效率
1. 项目概述:音频驱动的动态令牌压缩技术 在语音交互与多模态AI快速发展的今天,大语言模型处理长音频输入时面临两个关键挑战:计算资源消耗随序列长度平方级增长,以及语音信息中存在大量冗余信号。OmniZip技术通过实时分析音频频谱…...
想进民航局搞适航审定?一文说清CAAC适航司、审定中心、地区管理局的职责与招聘门槛
民航适航审定体系职业指南:从入门到精通的择业策略 当C919国产大飞机划破天际线的那一刻,无数航空人的目光不仅追随着银翼的轨迹,更聚焦在这架国之重器背后那群"航空器守门人"——适航审定团队。这个由适航审定司、六大地区管理局和…...
Windows 11任务栏拖放功能修复工具:终极使用指南与配置技巧
Windows 11任务栏拖放功能修复工具:终极使用指南与配置技巧 【免费下载链接】Windows11DragAndDropToTaskbarFix "Windows 11 Drag & Drop to the Taskbar (Fix)" fixes the missing "Drag & Drop to the Taskbar" support in Windows …...
开发者在 Taotoken 控制台进行 API Key 管理与访问控制的实践
开发者在 Taotoken 控制台进行 API Key 管理与访问控制的实践 1. 多项目环境下的 API Key 管理需求 在团队协作或多项目开发场景中,不同成员或子系统往往需要独立的大模型访问权限。Taotoken 控制台提供了细粒度的 API Key 管理功能,允许管理员为每个应…...
别再死记硬背了!用这3个趣味游戏,让孩子5分钟记住26个英文字母
3个魔法游戏让孩子5天征服26个字母 在儿童语言启蒙的黄金期,传统填鸭式字母教学常陷入"今天学明天忘"的困境。我们追踪了327组家庭的教学实践发现,融入多感官刺激的游戏化学习,能使字母记忆效率提升4倍以上。下面这三个经过幼教专…...
S32K3内存告急?手把手教你用ld文件优化RAM/FLASH分配(附实战代码)
S32K3内存告急?手把手教你用ld文件优化RAM/FLASH分配(附实战代码) 当你在S32K3项目开发中遇到编译错误"region RAM overflowed"时,那种头皮发麻的感觉我太熟悉了。去年我们团队在开发车载ECU固件时,就因为一…...
