All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.iteaj.iot.modbus.client.rtu.ModbusRtuClientProtocol Maven / Gradle / Ivy

There is a newer version: 3.1.1
Show newest version
package com.iteaj.iot.modbus.client.rtu;

import com.iteaj.iot.AbstractProtocol;
import com.iteaj.iot.ProtocolException;
import com.iteaj.iot.client.ClientConnectProperties;
import com.iteaj.iot.client.IotClient;
import com.iteaj.iot.consts.ExecStatus;
import com.iteaj.iot.format.ABCDFormat;
import com.iteaj.iot.format.DataFormat;
import com.iteaj.iot.modbus.*;
import com.iteaj.iot.modbus.consts.ModbusCode;
import com.iteaj.iot.modbus.consts.ModbusCoilStatus;
import com.iteaj.iot.serial.SerialClient;
import com.iteaj.iot.serial.SerialComponent;
import com.iteaj.iot.serial.SerialConnectProperties;
import com.iteaj.iot.utils.ByteUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.apache.commons.lang3.ObjectUtils;

import java.util.function.Consumer;

public class ModbusRtuClientProtocol extends AbstractProtocol implements ModbusCommonProtocol {

    private int start;
    private ModbusCode code;
    private Payload payload;
    private Object clientKey;

    protected ModbusRtuClientProtocol(ModbusRtuClientMessage message) {
        this.requestMessage = message;
        this.code = message.getBody().getCode();
        this.start = message.getBody().getStart();
    }

    protected ModbusRtuClientProtocol(ModbusCode code, ModbusRtuClientMessage message) {
        this.code = code;
        this.requestMessage = message;
    }

    protected ModbusRtuClientProtocol(int start, ModbusCode code, ModbusRtuClientMessage message) {
        this.code = code;
        this.start = start;
        this.requestMessage = message;
    }

    @Override
    public AbstractProtocol buildRequestMessage() {
        if(this.requestMessage.getMessage() == null) {
            this.requestMessage.writeBuild();
        }
        return this;
    }

    @Override
    public AbstractProtocol buildResponseMessage() {
        ModbusRtuClientMessage responseMessage = this.responseMessage();
        if(responseMessage != null) {
            ModbusCode code = responseMessage.getBody().getCode();
            byte[] content = responseMessage.getBody().getContent();
            if(responseMessage.getBody().getErrCode() != null) {
                return null;
            }

            this.payload = ModbusUtil.resolvePayload(content, (short) this.start, code, ABCDFormat.INSTANCE);
        }

        return null;
    }

    public ModbusRtuClientProtocol request(String com) {
        this.clientKey = com;
        return this.sendRequest(null);
    }

    public ModbusRtuClientProtocol request(String com, Consumer consumer) {
        this.clientKey = com;
        return this.sendRequest(consumer);
    }

    public ModbusRtuClientProtocol request(SerialConnectProperties com) {
        this.clientKey = com;
        return this.sendRequest(null);
    }

    public ModbusRtuClientProtocol request(SerialConnectProperties com, Consumer consumer) {
        this.clientKey = com;
        return this.sendRequest(consumer);
    }

    protected ModbusRtuClientProtocol sendRequest(Consumer consumer) throws ProtocolException {
        this.buildRequestMessage();
        IotClient iotClient = this.getClient();
        if(iotClient instanceof SerialClient) {
            synchronized (iotClient) {
                ClientConnectProperties config = iotClient.getConfig();
                if(!((SerialClient) iotClient).isOpen()) {
                    if(!((SerialClient) iotClient).connect()) {
                        throw new ProtocolException("打开串口异常["+config.connectKey()+"]");
                    }
                }

                // 写新的报文
                byte[] message = this.requestMessage().getMessage();
                ((SerialClient) iotClient).write(message);
                long currentTimeMillis = System.currentTimeMillis();

                ByteBuf buffer = Unpooled.buffer(256);
                while (readBytes((SerialClient) iotClient, buffer, protocolType()) != -1) {
                    if(System.currentTimeMillis() - currentTimeMillis > config.getReaderIdleTime() * 1000) {
                        this.setExecStatus(ExecStatus.timeout); break;
                    }
                }

                if(this.getExecStatus() == ExecStatus.success && buffer.readableBytes() > 0) {
                    byte[] bytes = new byte[buffer.readableBytes()];
                    buffer.readBytes(bytes).release();
                    this.responseMessage = new ModbusRtuClientMessage(bytes);
                    this.responseMessage().doBuild(bytes);
                }
            }

            this.buildResponseMessage();
        }

        if(consumer != null) {
            consumer.accept(this);
        }

        return this;
    }

    protected synchronized IotClient getClient() {
        SerialComponent instance = SerialComponent.instance();
        SerialClient client = instance.getClient(this.clientKey);
        if(client == null) {
            if(this.clientKey instanceof String) {
                this.clientKey = new SerialConnectProperties((String) this.clientKey);
            }

            return instance.createNewClientAndConnect((ClientConnectProperties) clientKey);
        }

        return client;
    }

    protected int readBytes(SerialClient client, ByteBuf buffer, ModbusCode code) {
        if(client.bytesAvailable() == 0) {
            return 0; // 无需读取
        }

        int readableBytes = buffer.readableBytes();
        if(readableBytes == 0) { // 先校验是否是错误报文
            // 先读取两个字节
            byte[] message = new byte[2];
            client.read(message);
            buffer.writeBytes(message);
            int respCode = message[1] & 0xFF;
            if(respCode > 0x80) { // 错误报文
                message = new byte[3];
                // 读取剩下的3个字节
                client.read(message);
                buffer.writeBytes(message);

                return -1; // 结束
            }
        }

        // 写类型的报文
        if(code.getCode() >= 0x05) {
            if(readableBytes < 8) {
                // 读取剩下的六个字节
                byte[] message = new byte[6];
                client.read(message);
                buffer.writeBytes(message);
            }

            return -1; // 结束
        } else { // 读类型的报文
            if(readableBytes > 2) { // 已经获取长度字段
                buffer.readerIndex(2);
                int length = buffer.readByte() & 0xFF;
                // 读取剩下的报文
                byte[] message = new byte[length + 2]; // 长度字段 + CRC
                client.read(message);
                buffer.writeBytes(message).resetReaderIndex();
                return -1; //读取结束
            } else {
                // 读取第三个字节
                byte[] message = new byte[1];
                client.read(message);
                buffer.writeBytes(message); return message.length;
            }
        }
    }

    public void doBuildResponseMessage(ModbusRtuClientMessage responseMessage) {
        if(getExecStatus() == ExecStatus.success) {
            ModbusCode code = responseMessage.getBody().getCode();
            byte[] content = responseMessage.getBody().getContent();
            if(responseMessage.getBody().getErrCode() != null) {
                return;
            }

            switch (code) {
                case Read01:
                case Read02:
                    this.payload = new RealCoilPayload(this.start, content); break;
                case Read03:
                case Read04:
                    this.payload = new ReadPayload(content, this.start, ABCDFormat.INSTANCE); break;
                default:
                    this.payload = WritePayload.getInstance();
            }
        }
    }

    /**
     * 构建Modbus读线圈协议
     * @see ModbusCode#Read01
     * @param device 从机的设备地址
     * @param start 从哪个寄存器开始读
     * @param num 读多少个
     * @return
     */
    public static ModbusRtuClientProtocol buildRead01(int device, int start, int num) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildRead01Message(device, start, num);
        return new ModbusRtuClientProtocol(num, ModbusCode.Read01, message);
    }

    /**
     * 构建Modbus读线圈协议
     * @see ModbusCode#Read02
     * @param device 从机的设备地址
     * @param start 从哪个寄存器开始读
     * @param num 读多少个
     * @return
     */
    public static ModbusRtuClientProtocol buildRead02(int device, int start, int num) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildRead02Message(device, start, num);
        return new ModbusRtuClientProtocol(num, ModbusCode.Read02, message);
    }

    /**
     * 构建Modbus读保持寄存器报文
     * @param device 从机的设备地址
     * @param start 从哪个寄存器开始读
     * @param num 读几个寄存器
     * @return
     */
    public static ModbusRtuClientProtocol buildRead03(int device, int start, int num) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildRead03Message(device, start, num);
        return new ModbusRtuClientProtocol(start, ModbusCode.Read03, message);
    }

    /**
     * 构建Modbus读输入寄存器协议
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始读 (1-65535)
     * @param num 读几个寄存器(1-2000)
     * @return
     */
    public static ModbusRtuClientProtocol buildRead04(int device, int start, int num) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildRead04Message(device, start, num);
        return new ModbusRtuClientProtocol(start, ModbusCode.Read04, message);
    }

    /**
     * 构建Modbus写单个线圈报文
     * @param device 访问的设备
     * @param start 写哪个寄存器
     * @param status 写内容
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite05(int device, int start, ModbusCoilStatus status) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildWrite05Message(device, start, status);
        return new ModbusRtuClientProtocol(ModbusCode.Write05, message);
    }

    /**
     * 构建Modbus写单个寄存器报文
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始写 (1-65535)
     * @param write 写内容
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite06(int device, int start, byte[] write) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildWrite06Message(device, start, write);
        return new ModbusRtuClientProtocol(ModbusCode.Write06, message);
    }

    /**
     * 构建Modbus写单个寄存器报文
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始写 (1-65535)
     * @param value 写内容
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite06(int device, int start, short value) {
        byte[] write = ByteUtil.getBytesOfReverse(value);
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildWrite06Message(device, start, write);
        return new ModbusRtuClientProtocol(ModbusCode.Write06, message);
    }

    /**
     * 构建Modbus写多个线圈
     *
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始写
     * @param write 写到设备的内容
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite0F(int device, int start, byte[] write) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildWrite0FMessage(device, start, write);
        return new ModbusRtuClientProtocol(ModbusCode.Write0F, message);
    }

    /**
     * 构建Modbus写多个寄存器报文
     * @see ByteUtil#getBytes(int)
     * @see ByteUtil#getBytes(byte)
     * @see ByteUtil#getBytes(long)
     * @see ByteUtil#getBytes(float)
     * ......
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始写
     * @param num 写几个寄存器
     * @param write 写到设备的内容
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite10(int device, int start, int num, byte[] write) {
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildWrite10Message(device, start, num, write);
        return new ModbusRtuClientProtocol(ModbusCode.Write10, message);
    }

    /**
     * 构建Modbus写多个寄存器报文
     * @param device
     * @param start
     * @param convert 将值做转换
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite10(int device, int start, WriteConvert convert) {
        byte[] write = convert.getWrite();
        return buildWrite10(device, start, write.length / 2, write);
    }

    /**
     * 使用默认数据格式构建Modbus写多个寄存器报文(字符串类型使用UTF-8)
     * @see ByteUtil#getBytes(int)
     * @see ByteUtil#getBytes(byte)
     * @see ByteUtil#getBytes(long)
     * @see ByteUtil#getBytes(float)
     * ......
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始写
     * @param args 写到设备的内容(可以是Number类型和String类型) 如果是字符串类型使用UTF-8编码
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite10(int device, int start, Object... args) {
        return buildWrite10(device, start, DataFormat.ABCD, args);
    }

    /**
     * 构建Modbus写多个寄存器报文(字符串类型使用UTF-8)
     * @see ByteUtil#getBytes(int)
     * @see ByteUtil#getBytes(byte)
     * @see ByteUtil#getBytes(long)
     * @see ByteUtil#getBytes(float)
     * ......
     * @param device 从机的设备地址 (1-255)
     * @param start 从哪个寄存器开始写
     * @param format 数据格式
     * @param args 写到设备的内容(可以是Number类型和String类型) 如果是字符串类型使用UTF-8编码
     * @return
     */
    public static ModbusRtuClientProtocol buildWrite10(int device, int start, DataFormat format, Object... args) {
        if(ObjectUtils.isEmpty(args)) {
            throw new ModbusProtocolException("未指定要写的内容", ModbusCode.Write10);
        }

        ModbusUtil.Write10Build write10Build = ModbusUtil.write10Build(format, args);
        ModbusRtuClientMessage message = ModbusRtuMessageBuilder.buildWrite10Message(
                device, start, write10Build.num, write10Build.message);
        return new ModbusRtuClientProtocol(ModbusCode.Write10, message);
    }

    /**
     * 根据使用自定义的message构建报文
     * @return
     */
    public static ModbusRtuClientProtocol build(byte[] message) {
        ModbusCode code = ModbusCode.INSTANCE(message[1]);
        if(code.getCode() < 0x05) { // 读构建
            short start;
            if(code.getCode() <= 0x02) { // 读线圈使用读取数量
                start = ByteUtil.bytesToShortOfReverse(message, 4);
            } else { // 读其它使用起始地址
                start = ByteUtil.bytesToShortOfReverse(message, 2);
            }
            return new ModbusRtuClientProtocol(start, code, new ModbusRtuClientMessage(message));
        } else {
            return new ModbusRtuClientProtocol(code, new ModbusRtuClientMessage(message));
        }
    }

    public Object getClientKey() {
        return clientKey;
    }

    @Override
    public Payload getPayload() {
        return this.payload;
    }

    @Override
    public ModbusCode protocolType() {
        return this.code;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy