【应用】Modbus 通讯协议

news2024/11/18 9:30:57

Modbus 通讯协议

  • Modbus 协议基础
    • Modbus 存储区
    • Modbus-RTU 协议
    • Modbus-TCP 协议
  • Java 实现 Modbus 通讯
    • Modbus Read
    • Modbus Write
  • 模拟数据进行代码测试
    • Modbus-RTU 代码验证
    • Modbus-TCP 代码验证
  • SerialPortWrapper 实现类代码

Modbus 协议基础

  • Modbus 是一种总线通讯协议,其支持多种电气接口(RS-232/RS-485);

  • Modbus 是应用层报文传输协议,其定义了控制器能够认识和使用的消息结构;

  • Modbus 采用主从通讯方式,只有一个设备可以发送请求;

更多详细内容参考:

  1. Modbus协议解析–小白一看就懂的协议_panda@Code的博客-CSDN博客_modbus协议

  2. 详解Modbus通信协议—清晰易懂_Z小旋的博客-CSDN博客_modbus

Modbus 存储区

Modbus 在从机中存储数据,其规定了四个功能区:

区号名称读写概述
0区输出线圈可读写布尔量0=Fales,1=True
1区输入线圈只读布尔量0=Fales,1=True
3区输入寄存器只读寄存器需要指定数据类型
4区保持寄存器可读写寄存器需要指定数据类型

Modbus-RTU 协议

RTU 协议的帧结构为机地址 + 功能码 + 数据 + 校验码

其中的功能码描述了要执行的操作,常用的功能码如下:

功能码功能说明
01H读取输出线圈
02H读取输入线圈
03H读取保持寄存器
04H读取输入寄存器
05H写入单线圈
06H写入单寄存器
0FH写入多线圈
10H写入多寄存器

Modbus-TCP 协议

TCP 协议将 RTU 协议拆分,将功能码与数据提取出来,拼接 MBAP 报文头部组成。TCP 本身就具备差错校验的能力,因此不需要校验码。

在这里插入图片描述

Java 实现 Modbus 通讯

本文中使用 Modbus4j 开源库实现 Modbus 的通讯,其依赖如下:

        <dependency>
            <groupId>com.infiniteautomation</groupId>
            <artifactId>modbus4j</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>io.github.java-native</groupId>
            <artifactId>jssc</artifactId>
            <version>2.9.4</version>
        </dependency>

在该开源库中,对于 RTU 和 TCP 传输来说,其根本不同在于创建不同的 Modbus Master,而读取和写入的方法相同,详见代码。

注意:在实现 RTU 传输时,我们需要实现开源包中的 SerialPortWrapper 方法以创建 Master,对应实现类以及实现类中引用类的代码放至文末。

Modbus Read

public class Modbus_Read {

    // master 工厂
    static ModbusFactory modbusFactory;

    // master 对象
    static ModbusMaster master;

    // 静态方法初始化 master
    static {
        modbusFactory = new ModbusFactory();
        // 使用 Modbus-TCP 进行通信
//        IpParameters param = new IpParameters();
//        param.setHost("localhost");
//        param.setPort(502);
//        master = modbusFactory.createTcpMaster(param, false);
        // 使用 Modbus-RTU 进行通信
        SerialPortWrapper serialParameters = new SerialPortWrapperImpl("COM12", 9600,
                8, 1, 0, 0, 0);
        master = modbusFactory.createRtuMaster(serialParameters);
        try {
            master.init();
        } catch (ModbusInitException e) {
            throw new RuntimeException("master 初始化失败~");
        }
    }

    /**
     * 主方法测试
     */
    public static void main(String[] args) throws ModbusTransportException, ErrorResponseException {
        System.out.println("=====读线圈 CoilStatus=====");
        System.out.println("0>>>" + readCoilStatus(1, 0));
        System.out.println("5>>>" + readCoilStatus(1, 5));
        System.out.println("=====读离散量输入 InputStatus=====");
        System.out.println("7>>>" + readInputStatus(1, 7));
        System.out.println("8>>>" + readInputStatus(1, 8));
        System.out.println("=====读保持寄存器 HoldingRegister=====");
        System.out.println("0>>>" + readHoldingRegister(1, 0, DataType.FOUR_BYTE_FLOAT));
        System.out.println("2>>>" + readHoldingRegister(1, 2, DataType.FOUR_BYTE_FLOAT));
        System.out.println("=====读输入寄存器 InputRegisters=====");
        System.out.println("6>>>" + readInputRegisters(1, 6, DataType.FOUR_BYTE_FLOAT));
        System.out.println("8>>>" + readInputRegisters(1, 8, DataType.FOUR_BYTE_FLOAT));
        System.out.println("=====测试批量读取=====");
        batchRead();
    }

    /**
     * 读线圈 CoilStatus
     * @param slaveId 从机id
     * @param offset 偏移量
     * @return 读取数据值
     */
    public static Boolean readCoilStatus(int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Boolean> locator = BaseLocator.coilStatus(slaveId, offset);
        return master.getValue(locator);
    }


    /**
     * 读离散量输入 InputStatus
     * @param slaveId 从机id
     * @param offset 偏移量
     * @return 读取数据值
     */
    public static Boolean readInputStatus(int slaveId, int offset)
            throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Boolean> locator = BaseLocator.inputStatus(slaveId, offset);
        return master.getValue(locator);
    }

    /**
     * 读保持寄存器 HoldingRegister
     * @param slaveId 从机id
     * @param offset 偏移量
     * @param dataType 数据类型
     * @return 读取数据值
     */
    public static Number readHoldingRegister(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
        return master.getValue(locator);
    }


    /**
     * 读输入寄存器 InputRegisters
     * @param slaveId 从机id
     * @param offset 偏移量
     * @param dataType 数据类型
     * @return 读取数据值
     */
    public static Number readInputRegisters(int slaveId, int offset, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        BaseLocator<Number> locator = BaseLocator.inputRegister(slaveId, offset, dataType);
        return master.getValue(locator);
    }


    public static void batchRead() throws ModbusTransportException, ErrorResponseException {

        BatchRead<Integer> batch = new BatchRead<Integer>();

        batch.addLocator(0, BaseLocator.coilStatus(1, 0));
        batch.addLocator(1, BaseLocator.inputStatus(1, 7));
        batch.addLocator(2, BaseLocator.holdingRegister(1, 0, DataType.FOUR_BYTE_FLOAT));
        batch.addLocator(3, BaseLocator.inputRegister(1, 6, DataType.FOUR_BYTE_FLOAT));

        batch.setContiguousRequests(false);
        BatchResults<Integer> results = master.send(batch);
        System.out.println(results.getValue(0));
        System.out.println(results.getValue(1));
        System.out.println(results.getValue(2));
        System.out.println(results.getValue(3));
    }

}

Modbus Write

public class Modbus_Write {

    // master 工厂
    static ModbusFactory modbusFactory;

    // master 对象
    static ModbusMaster master;

    // 静态方法初始化 master
    static {
        modbusFactory = new ModbusFactory();
        // 使用 Modbus-TCP 进行通信
//        IpParameters param = new IpParameters();
//        param.setHost("localhost");
//        param.setPort(502);
//        master = modbusFactory.createTcpMaster(param, false);
        // 使用 Modbus-RTU 进行通信
        SerialPortWrapper serialParameters = new SerialPortWrapperImpl("COM12", 9600,
                8, 1, 0, 0, 0);
        master = modbusFactory.createRtuMaster(serialParameters);
        try {
            master.init();
        } catch (ModbusInitException e) {
            throw new RuntimeException("master 初始化失败~");
        }
    }

    /**
     * 主方法测试
     */
    public static void main(String[] args) throws ModbusTransportException, ErrorResponseException {
        System.out.println("=====写线圈 CoilStatus=====");
        System.out.println(writeCoil(1, 5, true));
        System.out.println("=====批量写线圈 CoilStatus=====");
        System.out.println(writeCoils(1, 6, new boolean[]{true, false, true, false}));
        System.out.println("=====写保持寄存器 HoldingRegister=====");
        writeRegister(1, 8, 11.1234, DataType.FOUR_BYTE_FLOAT);
    }

    /**
     * 写线圈 CoilStatus
     * @param slaveId 从机id
     * @param writeOffset 偏移量
     * @param writeValue 写入值
     * @return 写入结果
     */
    public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
            throws ModbusTransportException {
        // 创建请求
        WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
        // 发送请求并获取响应对象
        WriteCoilResponse response = (WriteCoilResponse) master.send(request);
        return !response.isException();
    }

    /**
     * 批量写线圈 CoilStatus
     * @param slaveId 从机id
     * @param startOffset 写入起始偏移量
     * @param bdata 写入数据集
     * @return 写入结果
     */
    public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)
            throws ModbusTransportException {
        // 创建请求
        WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
        // 发送请求并获取响应对象
        WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);
        return !response.isException();
    }

    /**
     * 写保持寄存器 HoldingRegister
     * @param slaveId 从机id
     * @param writeOffset 偏移量
     * @param writeValue 写入值
     * @param dataType 写入值数据类型
     */
    public static void writeRegister(int slaveId, int writeOffset, Number writeValue, int dataType)
            throws ModbusTransportException, ErrorResponseException {
        // 创建寻址对象
        BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, writeOffset, dataType);
        // 执行写入操作
        master.setValue(locator, writeValue);
    }

}

模拟数据进行代码测试

本文使用 Modbus slave 模拟从机进行测试,百度网盘下载链接如下:

链接:https://pan.baidu.com/s/1PIGT8Zpi2cYCFRTRZwjiNA?pwd=hku4 
提取码:hku4

Modbus-RTU 代码验证

RTU 协议需要通过总线进行传输,因此需要借助软件在计算机上开机虚拟串口,此处使用 Virtual Serial Port Driver,百度网盘下载链接如下:

链接:https://pan.baidu.com/s/1bQMUIOnK56LyvxcH4Gsdqg?pwd=p5fm 
提取码:p5fm

打开 Virtual Serial Port Driver,在计算机上创建一对虚拟串口

在这里插入图片描述

创建成功后在设备管理器中可以查找到对应的虚拟串口

在这里插入图片描述

使用 Modbus Slave 创建从机,File --> new,在打开的窗口的空白处点击右键,选择Slave Definotion可以对从机进行配置

在这里插入图片描述

选中偏移量对应的格子,右键点击Format可以设置存储的类型

在这里插入图片描述

按照上述流程创建四个从机,分别对应四个功能区

在这里插入图片描述

单击工具栏的Connection --> Connect,配置连接(若已连接需要先断开Connection --> cDisonnect

在这里插入图片描述

至此模拟从机配置完毕,可以修改代码中的串口,通过 Modbus-RTU 的模式读取不同功能区的不同偏移量上的数值,上述代码亲测可用,此处不再演示

Modbus-TCP 代码验证

在上述从机配置的基础上,首先断开连接,并重新创建连接,连接方式选择Modbus TCP/IP即可

在这里插入图片描述

然后修改代码,采用 Modbus-Tcp 进行通信的方式创建 Master,随后即可通过配置不同的功能能区和偏移量实现数据的读写,上述代码亲测可用,此处不再演示

SerialPortWrapper 实现类代码

package com.zqf.modbus.modbus_tcp.impl;

import com.serotonin.modbus4j.serial.SerialPortWrapper;
import jssc.SerialPort;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;
import java.io.OutputStream;

public class SerialPortWrapperImpl implements SerialPortWrapper {

    private static final Logger LOG = LoggerFactory.getLogger(SerialPortWrapperImpl.class);
    private final SerialPort port;
    private final int baudRate;
    private final int dataBits;
    private final int stopBits;
    private final int parity;
    private final int flowControlIn;
    private final int flowControlOut;

    public SerialPortWrapperImpl(String commPortId, int baudRate, int dataBits, int stopBits,
                                 int parity, int flowControlIn, int flowControlOut) {
        this.baudRate = baudRate;
        this.dataBits = dataBits;
        this.stopBits = stopBits;
        this.parity = parity;
        this.flowControlIn = flowControlIn;
        this.flowControlOut = flowControlOut;

        port = new SerialPort(commPortId);

    }

    @Override
    public void close() throws Exception {
        port.closePort();
        //listeners.forEach(PortConnectionListener::closed);
        LOG.debug("Serial port {} closed", port.getPortName());
    }

    @Override
    public void open() {
        try {
            port.openPort();
            port.setParams(this.getBaudRate(), this.getDataBits(), this.getStopBits(), this.getParity());
            port.setFlowControlMode(this.getFlowControlIn() | this.getFlowControlOut());

            //listeners.forEach(PortConnectionListener::opened);
            LOG.debug("Serial port {} opened", port.getPortName());
        } catch (Exception ex) {
            LOG.error("Error opening port : {} for {} ", port.getPortName(), ex);
        }
    }

    @Override
    public InputStream getInputStream() {
        return new SerialInputStream(port);
    }

    @Override
    public OutputStream getOutputStream() {
        return new SerialOutputStream(port);
    }

    @Override
    public int getBaudRate() {
        return baudRate;
        //return SerialPort.BAUDRATE_9600;
    }

    @Override
    public int getFlowControlIn() {
        return flowControlIn;
        //return SerialPort.FLOWCONTROL_NONE;
    }

    @Override
    public int getFlowControlOut() {
        return flowControlOut;
        //return SerialPort.FLOWCONTROL_NONE;
    }

    @Override
    public int getDataBits() {
        return dataBits;
        //return SerialPort.DATABITS_8;
    }

    @Override
    public int getStopBits() {
        return stopBits;
        //return SerialPort.STOPBITS_1;
    }

    @Override
    public int getParity() {
        return parity;
        //return SerialPort.PARITY_NONE;
    }
}

其中 SerialInputStream 和 SerialOutputStream 类代码如下:

package com.zqf.modbus.modbus_tcp.impl;

import jssc.SerialPort;

import java.io.IOException;
import java.io.InputStream;

public class SerialInputStream extends InputStream {

    private SerialPort serialPort;
    private int defaultTimeout = 0;

    /**
     * Instantiates a SerialInputStream for the given {@link SerialPort} Do not
     * create multiple streams for the same serial port unless you implement
     * your own synchronization.
     *
     * @param sp The serial port to stream.
     */
    public SerialInputStream(SerialPort sp) {
        serialPort = sp;
    }

    /**
     * Set the default timeout (ms) of this SerialInputStream. This affects
     * subsequent calls to {@link #read()}
     * The default timeout can be 'unset'
     * by setting it to 0.
     *
     * @param time The timeout in milliseconds.
     */
    public void setTimeout(int time) {
        defaultTimeout = time;
    }

    /**
     * Reads the next byte from the port. If the timeout of this stream has been
     * set, then this method blocks until data is available or until the timeout
     * has been hit. If the timeout is not set or has been set to 0, then this
     * method blocks indefinitely.
     */
    @Override
    public int read() throws IOException {
        return read(defaultTimeout);
    }

    /**
     * The same contract as {@link #read()}, except overrides this stream's
     * default timeout with the given timeout in milliseconds.
     *
     * @param timeout The timeout in milliseconds.
     * @return The read byte.
     * @throws IOException On serial port error or timeout
     */
    public int read(int timeout) throws IOException {
        byte[] buf = new byte[1];
        try {
            if (timeout > 0) {
                buf = serialPort.readBytes(1, timeout);
            } else {
                buf = serialPort.readBytes(1);
            }
            return buf[0];
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    /**
     * Non-blocking read of up to buf.length bytes from the stream. This call
     * behaves as read(buf, 0, buf.length) would.
     *
     * @param buf The buffer to fill.
     * @return The number of bytes read, which can be 0.
     * @throws IOException on error.
     */
    @Override
    public int read(byte[] buf) throws IOException {
        return read(buf, 0, buf.length);
    }

    /**
     * Non-blocking read of up to length bytes from the stream. This method
     * returns what is immediately available in the input buffer.
     *
     * @param buf The buffer to fill.
     * @param offset The offset into the buffer to start copying data.
     * @param length The maximum number of bytes to read.
     * @return The actual number of bytes read, which can be 0.
     * @throws IOException on error.
     */
    @Override
    public int read(byte[] buf, int offset, int length) throws IOException {

        if (buf.length < offset + length) {
            length = buf.length - offset;
        }

        int available = this.available();

        if (available > length) {
            available = length;
        }

        try {
            byte[] readBuf = serialPort.readBytes(available);
//            System.arraycopy(readBuf, 0, buf, offset, length);
            System.arraycopy(readBuf, 0, buf, offset, readBuf.length);
            return readBuf.length;
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    /**
     * Blocks until buf.length bytes are read, an error occurs, or the default
     * timeout is hit (if specified). This behaves as blockingRead(buf, 0,
     * buf.length) would.
     *
     * @param buf The buffer to fill with data.
     * @return The number of bytes read.
     * @throws IOException On error or timeout.
     */
    public int blockingRead(byte[] buf) throws IOException {
        return blockingRead(buf, 0, buf.length, defaultTimeout);
    }

    /**
     * The same contract as {@link #blockingRead(byte[])} except overrides this
     * stream's default timeout with the given one.
     *
     * @param buf The buffer to fill.
     * @param timeout The timeout in milliseconds.
     * @return The number of bytes read.
     * @throws IOException On error or timeout.
     */
    public int blockingRead(byte[] buf, int timeout) throws IOException {
        return blockingRead(buf, 0, buf.length, timeout);
    }

    /**
     * Blocks until length bytes are read, an error occurs, or the default
     * timeout is hit (if specified). Saves the data into the given buffer at
     * the specified offset. If the stream's timeout is not set, behaves as
     * {@link #read(byte[], int, int)} would.
     *
     * @param buf The buffer to fill.
     * @param offset The offset in buffer to save the data.
     * @param length The number of bytes to read.
     * @return the number of bytes read.
     * @throws IOException on error or timeout.
     */
    public int blockingRead(byte[] buf, int offset, int length) throws IOException {
        return blockingRead(buf, offset, length, defaultTimeout);
    }

    /**
     * The same contract as {@link #blockingRead(byte[], int, int)} except
     * overrides this stream's default timeout with the given one.
     *
     * @param buf The buffer to fill.
     * @param offset Offset in the buffer to start saving data.
     * @param length The number of bytes to read.
     * @param timeout The timeout in milliseconds.
     * @return The number of bytes read.
     * @throws IOException On error or timeout.
     */
    public int blockingRead(byte[] buf, int offset, int length, int timeout) throws IOException {
        if (buf.length < offset + length) {
            throw new IOException("Not enough buffer space for serial data");
        }

        if (timeout < 1) {
            return read(buf, offset, length);
        }

        try {
            byte[] readBuf = serialPort.readBytes(length, timeout);
            System.arraycopy(readBuf, 0, buf, offset, length);
            return readBuf.length;
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    @Override
    public int available() throws IOException {
        int ret;
        try {
            ret = serialPort.getInputBufferBytesCount();
            if (ret >= 0) {
                return ret;
            }
            throw new IOException("Error checking available bytes from the serial port.");
        } catch (Exception e) {
            throw new IOException("Error checking available bytes from the serial port.");
        }
    }

}
package com.zqf.modbus.modbus_tcp.impl;

import jssc.SerialPort;
import jssc.SerialPortException;

import java.io.IOException;
import java.io.OutputStream;

public class SerialOutputStream extends OutputStream {

    SerialPort serialPort;

    /**
     * Instantiates a SerialOutputStream for the given {@link SerialPort} Do not
     * create multiple streams for the same serial port unless you implement
     * your own synchronization.
     *
     * @param sp The serial port to stream.
     */
    public SerialOutputStream(SerialPort sp) {
        serialPort = sp;
    }

    @Override
    public void write(int b) throws IOException {
        try {
            serialPort.writeInt(b);
        } catch (SerialPortException e) {
            throw new IOException(e);
        }
    }

    @Override
    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);

    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        byte[] buffer = new byte[len];
        System.arraycopy(b, off, buffer, 0, len);
        try {
            serialPort.writeBytes(buffer);
        } catch (SerialPortException e) {
            throw new IOException(e);
        }
    }

}

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

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

相关文章

[附源码]计算机毕业设计贵港高铁站志愿者服务平台Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

Spring Bean的生命周期

一、首先我们要知道什么是Spring Bean&#xff1a;Spring Bean是Spring框架在运行管理时的对象。 二、Spring Bean的生命周期&#xff1a; 简单来说bean会经历四个阶段&#xff1a; 实例化 -》 属性赋值 -》初始化 -》销毁 下面我们来具体看一下&#xff1a; 1.实例化 Bea…

软件质量评估模型

软件质量是指软件产品满足用户要求的程度。可以从多个方面来理解此处所指的用户要求,包括用户期望的软件系统的功能、性能、可维护性、可操作性、可重用性等等。在软件项目实施过程中,经常会听到用户关于软件系统的以下一组质量评价。 软件系统没有某些方面的功能软件系统运行…

【5G MAC】NR Timing Advance(RAR TA 和 MAC-CE TA)

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

[Linux]基础命令(1)

Linux基本命令&#xff08;1&#xff09; 文章目录Linux基本命令&#xff08;1&#xff09;1.操作系统&#xff1a;&#xff08;1&#xff09;什么是操作系统&#xff1a;&#xff08;2&#xff09;为什么要有操作系统&#xff1a;2. ls命令:3. pwd指令&#xff1a;4. cd命令:5…

2022-12-11

文章目录前言PWMPwmChannelPwmAssignedHwUnitPwmChannelIdPwmCoherentUpdatePwmDutycycleDefaultPwmIdleStatePwmNotificationPwmChannelClassPwmPeriodDefaultPwmPolarityPwmReferenceChannelPwmSafetySignalPwmShiftValuePWM输出偏移的使用PwmConfigurationOfOptApiServicesP…

windows 基于 MediaPipe 实现 Holistic

主页: https://google.github.io/mediapipe/solutions/holistic.html MediaPipe Holistic pipelines 集成了姿势、面部和手部组件的独立模型&#xff0c;每个组件都针对其特定领域进行了优化&#xff0c;每个组件的推断输入图不同。 MediaPipe Holistic 首先通过 BlazePose 的姿…

基于极限学习机进行股市预测(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

MySQL-InnoDB的事务隔离级别

MySQL 是一个服务器&#xff0f;客户端架构的软件&#xff0c;对于同一个服务器来说&#xff0c;可以有若干个客户端与之连接&#xff0c;每个客户端与服务器连接上之后&#xff0c;就可以称之为一个会话&#xff08; Session &#xff09;。我们可以同时在不同的会话里输入各种…

【图像处理】opencv | 图像的载入,显示,保存 | 视频流的载入,显示,保存

文章目录前言一、cv2读取图片并展示1.1、cv2.imread读取图片1.2、cv2.imshow展示图片1.3、完整代码1.4、封装函数调用1.5、cv2读取为灰度图像1.6、cv2.imwrite保存图像二、cv2读取视频并且展示2.1 展示彩色视频2.2 展示灰度视频2.3 保存视频前言 本文参考视频&#xff1a;唐宇…

二进制搭建k8s——部署etcd集群和单master

二进制搭建k8s——部署etcd集群和单master二进制搭建k8s——部署etcd集群和单master环境1、操作系统初始化配置&#xff08;全部节点&#xff09;2、部署 docker 引擎&#xff08;所有节点&#xff09;3、部署 etcd 集群准备签发证书环境在 master01 节点上操作在 node01 和 no…

端口隔离实现同一vlan下,二层和三层的互不通

如图&#xff1a;我们要实现下图中&#xff0c;PC1和PC2不通&#xff0c;但都和PC3互通&#xff1a; 配置如下&#xff1a; vlan batch 10 port-isolate mode all # interface GigabitEthernet0/0/1 port link-type access port default vlan 10 port-isolate enable grou…

【golang】 demo 之王realworld,使用golang+gin做后端技术,使用vue做前端项目的开源博客项目

目录前言1&#xff0c;关于realworld项目2&#xff0c;前端项目使用vue3开发的3&#xff0c;后端使用golanggin进行接口开发4&#xff0c;总结前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 未经博主允许不得转载。 博主CSDN地址是&…

idea中打包docker镜像

idea中打包docker镜像 说明 ​ 构建和推送镜像都是需要docker环境的&#xff0c;这个大家可以使用同一个远程的环境&#xff0c;这里说的就是idea使用服务器上的docker进行镜像的构建和发布&#xff0c; ​ 默认高版本的idea中默认集成了docker插件&#xff0c;这个插件的官…

高并发高可用

一、高并发 1、异步并发 同步阻塞 异步Future 异步CallBack 异步编排CompletableFuture 请求缓存 请求合并 2、扩容 单体应用垂直扩容 单体应用水平扩容 应用拆分 数据库拆分水平/垂直拆分 使用Sharding-jdbc分库分表/读写分离 数据异构 任务系统扩容 3、队列 …

[LeetCode周赛复盘] 第 93 场双周赛20221015-补

[LeetCode周赛复盘] 第 93 场双周赛20221015-补 一、本周周赛总结二、 [Easy] 6261. 数组中字符串的最大值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6262. 图中最大星和1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6263. 青蛙过河 II1. 题目描述2. 思路分析3. 代码实…

【OpenCV学习】第13课:基本阈值操作

仅自学做笔记用,后续有错误会更改 参考文章:https://blog.csdn.net/qq_37835727/article/details/123373339 理论 图像阈值&#xff1a;什么是图像阈值&#xff1f;简单点来说就是把图像分割的标尺&#xff0c; 举个栗子&#xff0c; 现在想象一个场景&#xff0c; 一个桌子上…

BA_重投影误差e对于相机的位姿ξ和对空间点的坐标P的雅可比矩阵的推导

1. 基本思路 重投影误差表示为e, 相机的位姿表示为ξ (或者表示为T(R,t))&#xff0c; 空间点表示为P, 则空间点投影到相机坐标系下的空间坐标点的相机坐标表示为P[X, Y, Z], 则 重投影误差e对于相机的位姿ξ的雅克比矩阵表示为 分别求等式右侧的两半部分&#xff08;误差对空…

DevExpress WinForms 22.2

DevExpress WinForms 22.2 添加了对Microsoft.NET 7的完全支持。 此版本现在需要.NET 6和Microsoft Visual Studio 2022(v17.0)或更高版本。 这不会影响.NET Framework客户&#xff0c;产品程序集在此发布周期中将继续以.NET Framework 4.5.2为目标。 蒙皮和矢量图标 WXI皮肤的…

每天五分钟机器学习:经典的降维算法——主成分分析法PCA

本文重点 前面我们学习了降维算法的两大应用场景,本节课程我们将学习具体的降维算法PCA,它是主成分分析法。 PCA要做什么? 将二维数据降维到一维,关键就是找到一个方向向量,然后把所有的数据都投射到该向量上,那么什么样的方向向量最好呢? 我们希望投射平均均方误差…