一、背景介绍
因为工作中需要用到modbus读取和存储字符串,但是modbus本身并不直接支持字符串类型数据。这里只能将字符串进行转换后进行存储,读的时候也是一样,通过读入一段连续的寄存器地址数据,再转成字符串。
二、环境搭建
参见:https://blog.csdn.net/xixiyuguang/article/details/123353651
三、核心代码
本文使用modbus4j来实现读写modbus
package com.borrow.util;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.encap.EncapMessageParser;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import com.serotonin.modbus4j.sero.messaging.IncomingMessage;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* modbus通讯工具类,采用modbus4j实现
*
* @author wangwei
* @dependencies modbus4j-3.0.3.jar
* @website https://github.com/infiniteautomation/modbus4j
*/
public class ModbusUtil {
/**
* 工厂。
*/
static ModbusFactory modbusFactory;
static {
if (modbusFactory == null) {
modbusFactory = new ModbusFactory();
}
}
/**
* 获取master
*
* @return
* @throws ModbusInitException
*/
public static ModbusMaster getMaster() throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost("localhost");
params.setPort(502);
//
// modbusFactory.createRtuMaster(wapper); //RTU 协议
// modbusFactory.createUdpMaster(params);//UDP 协议
// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
ModbusMaster master = modbusFactory.createTcpMaster(params, false);// TCP 协议
master.init();
return master;
}
/**
* 写 [01 Coil Status(0x)]写一个 function ID = 5
*
* @param slaveId
* slave的ID
* @param writeOffset
* 位置
* @param writeValue
* 值
* @return 是否写入成功
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeCoil(int slaveId, int writeOffset, boolean writeValue)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster();
// 创建请求
WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
// 发送请求并获取响应对象
WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
if (response.isException()) {
return false;
} else {
return true;
}
}
/**
* 写[01 Coil Status(0x)] 写多个 function ID = 15
*
* @param slaveId
* slaveId
* @param startOffset
* 开始位置
* @param bdata
* 写入的数据
* @return 是否写入成功
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeCoils(int slaveId, int startOffset, boolean[] bdata)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster();
// 创建请求
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
// 发送请求并获取响应对象
WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
if (response.isException()) {
return false;
} else {
return true;
}
}
/***
* 写[03 Holding Register(4x)] 写一个 function ID = 6
*
* @param slaveId
* @param writeOffset
* @param writeValue
* @return
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeRegister(int slaveId, int writeOffset, short writeValue)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster();
// 创建请求对象
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
if (response.isException()) {
System.out.println(response.getExceptionMessage());
return false;
} else {
return true;
}
}
/**
*
* 写入[03 Holding Register(4x)]写多个 function ID=16
*
* @param slaveId
* modbus的slaveID
* @param startOffset
* 起始位置偏移量值
* @param sdata
* 写入的数据
* @return 返回是否写入成功
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeRegisters(int slaveId, int startOffset, short[] sdata)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster();
// 创建请求对象
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
// 发送请求并获取响应对象
ModbusResponse response = tcpMaster.send(request);
if (response.isException()) {
return false;
} else {
return true;
}
}
/**
* 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
*
* @param slaveId
* @param offset
* @param value
* 写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
* @param registerCount
* ,com.serotonin.modbus4j.code.DataType
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static void writeHoldingRegister(int slaveId, int offset, Number value, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster();
// 类型
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
tcpMaster.setValue(locator, value);
}
/**
* 工具类方法 setString()主要进行了一下数据类型转换
* @param value
* @return
*/
public static short[] SetString(String value) {
byte[] bytesTemp = value.getBytes(StandardCharsets.UTF_8);
byte[] bytes;
if (bytesTemp.length % 2 > 0) {
bytes = Arrays.copyOf(bytesTemp, bytesTemp.length + 1);
} else {
bytes = bytesTemp;
}
return bytesToShort(bytes);
}
/**
* Byte数组转short数组
*
* @param bytes
* @return
*/
public static short[] bytesToShort(byte[] bytes) {
if (bytes == null) {
return null;
}
short[] shorts = new short[bytes.length / 2];
ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
return shorts;
}
/**
*工具类方法getString() 类型转换
* @param src
* @param start
* @param len
* @return
*/
public static String GetString(short[] src, int start, int len) throws UnsupportedEncodingException
{
short[] temp = new short[len];
for (int i = 0; i < len; i++)
{
temp[i] = src[i + start];
}
byte[] bytesTemp = shorts2Bytes(temp);
for (int i = 0; i < bytesTemp.length; i++) {
byte b = bytesTemp[i];
}
String str = new String(bytesTemp, "UTF-8");
return str;
}
/**
*short数组转byte
* @param data
* @return
*/
public static byte [] shorts2Bytes(short [] data){
byte[] byteValue = new byte[data.length * 2];
for (int i = 0; i < data.length; i++) {
byteValue[i * 2] = (byte) (data[i] & 0xff);
byteValue[i * 2 + 1] = (byte) ((data[i] & 0xff00) >> 8);
}
return byteValue;
}
/**
* 读保持寄存器上的内容
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param len 待读寄存器的个数
*/
private static String readHoldingRegistersToString(ModbusMaster master, int slaveId, int start, int len) {
String s = "";
try {
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
if (response.isException())
System.out.println("Exception response: message=" + response.getExceptionMessage());
else
s = GetString(response.getShortData(),0,len);
}
catch (ModbusTransportException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return s;
}
/**
* 测试modbus读写字符
*
* @param args
*/
public static void main(String[] args) {
try {
//往modbus中写入字符
short[] t = SetString("测试读取中文字符");
boolean f = writeRegisters(1,1,t);
//从modbus读取数据,并转换为字符
String str = readHoldingRegistersToString(getMaster(),1,1,23);
System.out.println("读取的字符:"+str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
控制台输出结果:
modbus slave结果: