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);
            
        
    


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

建立连接

读请求

写请求

关键理解
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通信,以及与工控机监控程序配置案例