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

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的通信&#xff0c;实现对PLC的线圈寄存器和保持寄存器的读写&#xff0c;基于TCP/IP的Modbus…...

ElasticSearch学习2

1、索引的操作 1、创建索引 对ES的操作其实就是发送一个restful请求&#xff0c;kibana中在DevTools中进行ES操作 创建索引时需要注意ES的版本&#xff0c;不同版本的ES创建索引的语句略有差别&#xff0c;会导致失败 如下创建一个名为people的索引&#xff0c;settings&…...

3D角色展示

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>3D卡片悬停</title><style>font-face {font-family: "Exoct";src: url("htt…...

前端面试:【Angular】打造强大Web应用的全栈框架

嗨&#xff0c;亲爱的Angular探险家&#xff01;在前端开发的旅程中&#xff0c;有一个全栈框架&#xff0c;那就是Angular。Angular提供了模块化、组件化、依赖注入、路由和RxJS等特性&#xff0c;助力你构建强大、可扩展的Web应用。 1. 什么是Angular&#xff1f; Angular是…...

数据结构:栈和队列

文章目录 一、栈1.栈的概念及结构1.栈的概念及结构2.栈的实现 2.栈的顺序表实现1.栈的结构体和实现的功能函数2.栈的初始化&#xff0c;入栈和出栈操作3.栈的其他操作 3.栈的链表实现1.栈的结构体和实现的功能函数2.栈功能函数的实现 二、队列1.队列的概念及结构1.队列的概念及…...

SpringCloud Gateway服务网关的介绍与使用

目录 1、网关介绍2、SpringCloudGateway工作原理3、三大组件3.1 、Route&#xff08;路由&#xff09;3.2、断言 Predicate3.3、过滤器 filter 4、Gateway整合nacos的使用4.1 、引入依赖4.2、 编写基础类和启动类4.3、 编写基础配置和路由规则4.4 、测试结果 1、网关介绍 客户…...

深入解析:如何打造高效的直播视频美颜SDK

在当今数字化时代&#xff0c;视频直播已经成为人们交流、娱乐和信息传递的重要方式。然而&#xff0c;许多人在直播时都希望能够呈现出最佳的外观&#xff0c;这就需要高效的直播视频美颜技术。本文将深入解析如何打造高效的直播视频美颜SDK&#xff0c;以实现令人满意的视觉效…...

每日一博 - MPP(Massively Parallel Processing,大规模并行处理)架构

文章目录 概述优点缺点小结 概述 MPP&#xff08;Massively Parallel Processing&#xff0c;大规模并行处理&#xff09;架构是一种常见的数据库系统架构&#xff0c;主要用于提高数据处理性能。它通过将多个单机数据库节点组成一个集群&#xff0c;实现数据的并行处理。 在 …...

ssh框架原理及流程

1.hibernate工作原理&#xff1a; 读取并解析配置文件读取并解析映射信息&#xff0c;创建sessionFactory打开session创建事务transaction持久化操作提交事务关闭session关闭sessionFactory 为什么使用&#xff1a; 对JDBC访问数据库的代码做了封装&#xff0c;大大简化了数据…...

eslint 配置和用法

在一个使用Webpack的项目中配置ESLint&#xff0c;你可以按照以下步骤操作&#xff1a; 首先&#xff0c;你需要在你的项目中安装ESLint和对应的Webpack loader。你可以使用npm或者yarn来安装。在你的项目根目录下打开终端&#xff0c;然后运行以下命令&#xff1a; 使用npm&…...

字符设备驱动实例(PWM和RTC)

目录 五、PWM 六、RTC 五、PWM PWM(Pulse Width Modulation&#xff0c;脉宽调制器)&#xff0c;顾名思义就是一个输出脉冲宽度可以调整的硬件器件&#xff0c;其实它不仅脉冲宽度可调&#xff0c;频率也可以调整。它的核心部件是一个硬件定时器&#xff0c;其工作原理可以用…...

Ribbon 源码分析

Ribbon 源码分析 Ribbon Debug 分析 断点 LoadBalancerInterceptor LoadBalancerInterceptor 实现了 ClientHttpRequestInterceptor 接口&#xff0c;重写了其中的 intercept 方法&#xff0c;用来拦截请求&#xff1b; 获取原始的 uri 和 服务名&#xff0c;调用 LoadBalanc…...

【1-3章】Spark编程基础(Python版)

课程资源&#xff1a;&#xff08;林子雨&#xff09;Spark编程基础(Python版)_哔哩哔哩_bilibili 第1章 大数据技术概述&#xff08;8节&#xff09; 第三次信息化浪潮&#xff1a;以物联网、云计算、大数据为标志 &#xff08;一&#xff09;大数据 大数据时代到来的原因…...

宇宙原理:黑洞基础。

宇宙原理&#xff1a;黑洞基础TOC 黑洞的数理基础&#xff1a;一个由满数组成的数盘&#xff0c;经过自然演进&#xff0c;将会逐步稀疏化、最终会向纯数方案发展&#xff1b;纯数方案虽然只有{2}、无数&#xff08;虚拟&#xff09;、{0,1,2,3}&#xff08;虚拟&#xff09;、…...

分类预测 | MATLAB实现SCNGO-CNN-LSTM-Attention数据分类预测

分类预测 | MATLAB实现SCNGO-CNN-LSTM-Attention数据分类预测 目录 分类预测 | MATLAB实现SCNGO-CNN-LSTM-Attention数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.SCNGO-CNN-LSTM-Attention数据分类预测程序&#xff0c;改进算法&#xff0c;融合正余弦和…...

Android学习之路(7) Frament

Fragment 表示应用界面中可重复使用的一部分。fragment 定义和管理自己的布局&#xff0c;具有自己的生命周期&#xff0c;并且可以处理自己的输入事件。fragment 不能独立存在。它们必须由 activity 或其他 fragment 托管。fragment 的视图层次结构会成为宿主的视图层次结构的…...

metallb , istio ingress 部署httpbin使用例子

安装metaillb,参考&#xff1a;Kubernetes的负载均衡方案&#xff1a;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

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 基于swing的销售管理系统 系统有1权限&#xff1a;管…...

FreeCAD傻瓜式教程之约束设定和构建实体、开孔、调整颜色等

本内容基于官方教程中的绘制简单的零件中的体会&#xff0c;在初次绘制的时候&#xff0c;总是无法完成&#xff0c;几经尝试才发现其关键点所在&#xff0c;以此文记录&#xff0c;用以被查资料&#xff0c;同时也希望能够帮到纯白新手快速熟悉该软件的绘图方法。 一、. 打开…...

代码随想录算法训练营day41 | 343. 整数拆分,96. 不同的二叉搜索树

目录 343. 整数拆分 96. 不同的二叉搜索树 343. 整数拆分 类型&#xff1a;动态规划 难度&#xff1a;medium 思路&#xff1a; dp[i]所用的拆分方法至少已经拆分了两次&#xff0c;比如dp[2]1&#xff0c;小于2&#xff0c;在大于2的数中&#xff0c;最后的2是不会拆的。 …...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

遍历 Map 类型集合的方法汇总

1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

CMake 从 GitHub 下载第三方库并使用

有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...