java中使用modbusTcp与plc通信

Posted FreeFly辉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中使用modbusTcp与plc通信相关的知识,希望对你有一定的参考价值。

本人只是用此协议写过上位机,所以这里介绍的只是在java中的一些使用

首先引入jar包
    <dependency>
        <groupId>com.intelligt.modbus</groupId>
        <artifactId>jlibmodbus</artifactId>
        <version>1.2.9.7</version>
    </dependency>
下面是自己封装的统一连接类
@Data
@Slf4j
public class ModBusConnector {
    private static final Map<InetAddress, ModBusConnector> connectorMap = new HashMap<>();
    private static final int MOD_BUS_PORT = 502;
    private static final int SERVER_ADDRESS = 1;
    private static final boolean KEEP_ALIVE = true;
    private final ModbusMaster modbusMaster;
    private final InetAddress ip;


    protected ModBusConnector(ModbusMaster modbusMaster,InetAddress ip) {
        this.modbusMaster = modbusMaster;
        this.ip = ip;
    }

    public static ModBusConnector fetchOrCreate(InetAddress ip, int timeout) {
        ModBusConnector connector = connectorMap.get(ip);
        if (connector == null) {
            ModbusMaster masterTCP = ModbusMasterFactory.createModbusMasterTCP(new TcpParameters(ip, MOD_BUS_PORT, KEEP_ALIVE));
            connector = new ModBusConnector(masterTCP, ip);
        }
        log.info("ModBus connector connecting: {}", connector);
        connector.getModbusMaster().setResponseTimeout(timeout);
        try {
            connector.getModbusMaster().connect();
            log.info("ModBus connector connected: {}", connector);
        } catch (ModbusIOException e) {
            throw new IllegalStateException(e+ip.getHostAddress());
        }
        return connector;
    }

    public static ModBusConnector fetchOrCreate(InetAddress ip) {
        return fetchOrCreate(ip, 3000);
    }

    public ReadHoldingRegistersResponse readRegisters(PlcReadRequest readRequest) {
        return WrappingUtil.runWithRetry(() -> {
            final long sending = System.currentTimeMillis();
//                log.debug("sending modbus read request: " +
//                        "waiting consume {}ms, ip={}, request={}", (sending - wait), ip, readRequest);
            ModbusRequest request;
            synchronized (modbusMaster) {
                try {
                    if (!modbusMaster.isConnected()) {
                        modbusMaster.connect();
                    }
                    request = ModbusRequestBuilder.getInstance().buildReadHoldingRegisters(SERVER_ADDRESS,readRequest.getStartAddress(),readRequest.getQuantity());
                    final ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) modbusMaster.processRequest(request);
                    if (response.isException()) {
                        throw new IllegalStateException("ModBus返回异常:" + response.getModbusExceptionCode());
                    }
                    final long sent = System.currentTimeMillis();
                    log.debug("Successfully sent modbus read request : " +
                                    "sending consume {}ms, ip={}, request={}, response: {}"
                            ,(sent - sending),ip,readRequest,response.getHoldingRegisters().getRegisters());
                    return response;
                } catch (Exception e) {
                    if (isConnected()) {
                        disconnect();
                    }
                    throw new IllegalStateException(e);
                }
            }
        }, PlcConfig.getRetryTimes(),new IllegalStateException("failed refresh rgv status"+ip.getHostAddress()));
    }

    public void writeRegisters(PlcWriteRequest writeRequest) {
        WrappingUtil.runWithRetry (() -> {
            final long wait = System.currentTimeMillis ();
            final long sending = System.currentTimeMillis ();
            log.debug ("sending modbus write request: " +
                    "waiting consume {}ms, ip={}, request={}", (sending - wait), ip, writeRequest);
            ModbusRequest request;
            synchronized (modbusMaster) {
                try {
                    if (!modbusMaster.isConnected()) {
                        modbusMaster.connect();
                    }
                    request = ModbusRequestBuilder.getInstance().buildWriteMultipleRegisters(SERVER_ADDRESS,writeRequest.getStartAddress(),writeRequest.getRegisters());
                    ModbusResponse response = modbusMaster.processRequest(request);
                    final long sent = System.currentTimeMillis();
                    log.info("successfully sent modbus write request : time={}" +
                            "sending consume {}ms, ip={},  request={}, response: {}",System.currentTimeMillis(),(sent - sending),ip,writeRequest,response.getModbusExceptionCode());
                } catch (Exception e) {
                    if (isConnected()) {
                        disconnect();
                    }
                    throw new IllegalStateException(e);
                }
            }
        }, PlcConfig.getRetryTimes (),new IllegalStateException("failed send command rgv status"+ip.getHostAddress()));
    }

    public boolean isConnected() {
        return modbusMaster.isConnected();
    }

    public void disconnect() {
        if (modbusMaster != null) {
            if (!modbusMaster.isConnected()) {
                log.info("connection already disconnected {}",this);
            } else {
                try {
                    modbusMaster.disconnect();
                } catch (ModbusIOException e) {
                    log.info("modbus disconnected false: {}",this,e);
                    throw new IllegalStateException(e);
                }
                log.info("modbus disconnected success: {}",this);
            }
        }
    }


}

代码快中用到了自己封装的工具类会放在文章结尾,这里截图解释以下关键部分

建立连接
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507150921420.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjQxNTE4OQ==,size_16,color_FFFFFF,t_70)
读请求
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210507151446793.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjQxNTE4OQ==,size_16,color_FFFFFF,t_70)

在这里插入图片描述

写请求

在这里插入图片描述

关键理解
java中每个 int 是 4个字节 32位,但是寄存器中int数据类型是 16 位,而一个int类型就是寄存器中的一个地址位。
好比我想在 寄存器的第2个地址位的倒数第2个bit位写入 1,那么java中构建的就是
int [2] = new int[2]
int[1] = 1<<2//第几个bit位就要向前移几位
注意,此时随在第二个地址位写了 数值 2,但是由于java中int默认值是0,所以这样构建还会把第一个地址位数据置0,所以如果不想影响到第一个地址为数值,需要如下构建
int [1] = new int[1]
int[0] = 1<<2//第几个bit位就要向前移几位
此时下发时起始地址为要携程 1,不能再是 0

还有一点要注意的,一个plc中一个int如果只用了 8 个bit位,那么这 8个bit位是 16位中的高8位,即先高8位后低8位。

以上是关于java中使用modbusTcp与plc通信的主要内容,如果未能解决你的问题,请参考以下文章

西门子300PLC连接组态王KingSCADA实现ModbusTCP通信

三菱Q系列PLC与上位机易控组态软件ModbusTCP通信案例

三菱FX系列PLC与上位机易控INSPEC软件ModbusTCP 通信

串口转以太网与监控软件modbusTCP客户端通信配置

串口转以太网与监控软件modbusTCP客户端通信配置

三菱系串口PLC实现ModbusTCP通信,以及与工控机监控程序配置案例