网站首页 > 编程文章 正文
1、引入modbus4j的maven
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>modbus-demo</artifactId>
<version>0.0.1</version>
<name>modbus-demo</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>ias-snapshots</id>
<name>Infinite Automation Snapshot Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>ias-releases</id>
<name>Infinite Automation Release Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-release/</url>
</repository>
</repositories>
</project>
需留意,在 pom.xml 中运用 repository 时,于 Maven 中的 settings.xml 文件里,mirrorOf 节点务必将 ias-snapshots 以及 ias-releases 予以排除。
<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*,!ias-snapshots,!ias-releases,!huaweicloud</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
<mirror>
<id>huaweicloud</id>
<mirrorOf>*,!ias-snapshots,!ias-releases,</mirrorOf>
<url>https://repo.huaweicloud.com/repository/maven/</url>
</mirror>
</mirrors>
2、编写ModbusUtils工具类
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.msg.ModbusRequest;
import com.serotonin.modbus4j.msg.ModbusResponse;
import com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest;
import com.serotonin.modbus4j.msg.WriteRegistersRequest;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
/**
* Modbus TCP 协议工具类
* <p>提供 Modbus TCP 通信核心功能及数据转换工具方法</p>
*/
@Slf4j
public class Modbus4jUtils {
/**
* Modbus 工厂实例(单例模式)
*/
static ModbusFactory modbusFactory;
// 静态初始化块:保证工厂实例唯一性
static {
if (modbusFactory == null) {
modbusFactory = new ModbusFactory();
}
}
/**
* 创建并初始化 Modbus TCP 主站连接
*
* @param ip 设备IP地址(如 "192.168.1.100")
* @param port 设备端口(如 502)
* @return 初始化后的 ModbusMaster 实例
*/
public static ModbusMaster getMaster(String ip, Integer port) {
// 配置连接参数
IpParameters params = new IpParameters();
params.setHost(ip); // 设置目标IP
params.setPort(port); // 设置目标端口
params.setEncapsulated(true); // 启用 RTU over TCP 封装
// 创建 TCP 主站(第二个参数 false 表示不共享连接)
ModbusMaster master = modbusFactory.createTcpMaster(params, false);
try {
master.init();
} catch (ModbusInitException e) {
log.error("Modbus 初始化失败", e);
e.printStackTrace();
}
return master;
}
/**
* 读取保持寄存器(功能码 03)
*
* @param slaveId 从站设备地址
* @param tcpMaster Modbus 主站实例
* @param start 寄存器起始地址(十进制)
* @param readLenth 读取寄存器数量
* @return 包含响应数据的字节队列
*/
public static ByteQueue modbusTCPRead(int slaveId, ModbusMaster tcpMaster, int start, int readLenth) {
//发送请求
ModbusRequest modbusRequest = null;
try {
// 创建读寄存器请求(功能码 03)
modbusRequest = new ReadHoldingRegistersRequest(slaveId, start, readLenth);//功能码03
} catch (ModbusTransportException e) {
log.error("请求构造失败", e);
e.printStackTrace();
}
//收到响应
ModbusResponse modbusResponse = null;
try {
// 发送请求并获取响应
modbusResponse = tcpMaster.send(modbusRequest);
} catch (ModbusTransportException e) {
log.error("通信异常", e);
e.printStackTrace();
}
// 将响应写入字节队列
ByteQueue byteQueue = new ByteQueue(12); // 初始容量 12 字节
modbusResponse.write(byteQueue);
log.debug("功能码:" + modbusRequest.getFunctionCode());
log.debug("从站地址:" + modbusRequest.getSlaveId());
log.debug("开始地址:" + start);
log.debug("响应大小:" + byteQueue.size() + " bytes");
log.debug("原始响应值:" + byteQueue);
return byteQueue;
}
/**
* 写多个寄存器(功能码 16)
*
* @param slaveId 从站设备地址
* @param tcpMaster Modbus 主站实例
* @param writeOffset 写入起始地址
* @param data 要写入的 short 数组数据
* @return 包含响应数据的字节队列
*/
public static ByteQueue modbusTCPWrite(int slaveId, ModbusMaster tcpMaster, int writeOffset, short[] data) {
WriteRegistersRequest writeRegistersRequest = null;
//收到响应
ModbusResponse modbusResponse = null;
try {
// 创建写寄存器请求(功能码 16)
writeRegistersRequest = new WriteRegistersRequest(slaveId, writeOffset, data);
modbusResponse = tcpMaster.send(writeRegistersRequest);
// 处理异常响应
if (modbusResponse.isException()) {
log.error("Exception response: message=" + modbusResponse.getExceptionMessage());
} else {
log.debug("Success");
}
} catch (ModbusTransportException e) {
log.error("通信异常", e);
e.printStackTrace();
}
// 将响应写入字节队列
ByteQueue byteQueue = new ByteQueue(12);
modbusResponse.write(byteQueue);
log.debug("功能码:" + writeRegistersRequest.getFunctionCode());
log.debug("从站地址:" + writeRegistersRequest.getSlaveId());
log.debug("收到的响应信息大小:" + byteQueue.size());
log.debug("收到的响应信息值:" + byteQueue);
return byteQueue;
}
// region 数据转换工具方法
/**
* 将字节数组转换为浮点数(IEEE 754 单精度)
*
* @param bytes 4字节数组
* @return 转换后的双精度浮点数
*/
public static double toDouble(byte[] bytes) {
return ByteBuffer.wrap(bytes).getFloat();
}
/**
* 字节数组转浮点数(保留两位小数)
*
* @param bytes 4字节数组
* @return 保留两位小数的双精度值
*/
public static double toDouble00(byte[] bytes) {
double f = ByteBuffer.wrap(bytes).getFloat();
return toDouble00(f);
}
/**
* 浮点数四舍五入保留两位小数
*
* @param f 原始浮点数
* @return 格式化后的双精度值
*/
public static double toDouble00(double f) {
BigDecimal b = new BigDecimal(f);
// 是小数点后只有两位的双精度类型数据
double f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
return f1;
}
/**
* 十六进制字符串转十进制整数
*
* @param s 十六进制字符串(不带 0x 前缀)
* @return 十进制整数值
*/
public static int get16to10(String s) {
return Integer.parseInt(s, 16);
}
/**
* 将byte[]转为各种进制的字符串
*
* @param bytes byte[]
* @param radix 基数可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制
* @return 转换后的字符串
* <p>
* 类型 占用 bit(位)
* byte(字节) 8
* short(短整型) 16
* int(整型) 32
* long(长整型) 64
* float(单精度浮点型) 32
* double(双精度浮点型) 64
* char(字符) 16
* boolean(布尔型) 1
*/
public static String binary(byte[] bytes, int radix) {
return new BigInteger(1, bytes).toString(radix);// 这里的1代表正数
}
/**
* 将byte数组转换为整数
* 转换为bit后,最左边的那位表示,符号位(有符号/无符号)
*/
public static int bytesToInt(byte[] bs) {
int a = 0;
for (int i = bs.length - 1; i >= 0; i--) {
a += bs[i] * Math.pow(255, bs.length - i - 1);
}
return a;
}
/**
* 字节数组转 short(大端序)
*
* @param b 2字节数组
* @return 转换后的 short 值
*/
public static short bytesToshort(byte[] b) {
short l = 0;
for (int i = 0; i < 2; i++) {
l <<= 8; // 左移 8 位腾出空间
l |= (b[i] & 0xff); //和上面也是一样的 l = l | (b[i]&0xff)
}
return l;
}
// endregion
}
3、测试
import com.example.modbusdemo.modbus.Modbus4jUtils;
import com.serotonin.modbus4j.sero.util.queue.ByteQueue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
@RestController
@Slf4j
public class IndexController {
@GetMapping("/")
public String index() {
ByteQueue byteQueue = Modbus4jUtils.modbusTCPRead(1, Modbus4jUtils.getMaster("127.0.0.1", 502), 0, 4);
// 01 03 08 00 0C 00 00 00 00 00 0D 98 12 // 完整数据
// 01 03 08 00 0C 00 00 00 00 00 0D //排除校验位的数据
byte[] bytes = byteQueue.peekAll();
// 08 长度
byte[] lengthBytes = Arrays.copyOfRange(bytes, 2, 3);
int dataLength = Modbus4jUtils.bytesToInt(lengthBytes);
// 每个数据占两个字节
short[] data = new short[dataLength / 2];
// 00 0C 00 00 00 00 00 0D //数据
byte[] dataBytes = Arrays.copyOfRange(bytes, 3, dataLength + 3);
for (int i = 0; i < dataBytes.length; i++) {
if (i % 2 != 0) {
data[i / 2] = Modbus4jUtils.bytesToshort(new byte[]{dataBytes[i - 1], dataBytes[i]});
}
}
log.debug(Arrays.toString(data));
return "index";
}
}
猜你喜欢
- 2025-05-25 实战经验分享:12个网络命令,帮你快速诊断并解决问题
- 2025-05-25 Java程序员必备——Linux的面试常见问题及面试题!你知道多少?
- 2025-05-25 B站二面:TCP的滑动窗口协议有什么用?讲一下原理
- 2025-05-25 macOS/Linux/Windows 网络命令全家桶,让你直接 “命令大师”
- 2025-05-25 监控摄像头常用测试命令大全
- 2025-05-25 学习计算机网络需要掌握以下几方面基础知识
- 2025-05-25 「底层原理」epoll源码分析,还搞不懂epoll的看过来
- 2025-05-25 Linux 进阶知识点总结
- 2025-05-25 腾讯云国际站:腾讯云的哪些命令可快速诊断网络?
- 2025-05-25 能ping通,TCP就一定能连通吗?
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- spire.doc (59)
- system.data.oracleclient (61)
- 按键小精灵源码提取 (66)
- pyqt5designer教程 (65)
- 联想刷bios工具 (66)
- c#源码 (64)
- graphics.h头文件 (62)
- mysqldump下载 (66)
- sqljdbc4.jar下载 (56)
- libmp3lame (60)
- maven3.3.9 (63)
- 二调符号库 (57)
- 苹果ios字体下载 (56)
- git.exe下载 (68)
- diskgenius_winpe (72)
- pythoncrc16 (57)
- solidworks宏文件下载 (59)
- qt帮助文档中文版 (73)
- satacontroller (66)
- hgcad (64)
- bootimg.exe (69)
- android-gif-drawable (62)
- axure9元件库免费下载 (57)
- libmysqlclient.so.18 (58)
- springbootdemo (64)
本文暂时没有评论,来添加一个吧(●'◡'●)