场景
Windows上ModbusTCP模拟Master与Slave工具的使用:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/135290463
Modebus TCP
Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。
1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP。
Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。
标准的Modbus协议物理层接口有RS232、RS422、RS485和以太网接口,采用master/slave方式通信。
Modbus的操作对象有四种:
线圈、离散输入、保持寄存器、输入寄存器。
Modbus功能码:
关于Java的开源库:
Jamod:
Java Modbus实现:Java Modbus库。该库由Dieter Wimberger实施。
ModbusPal:
ModbusPal是一个正在进行的Java项目,用于创建逼真的Modbus从站模拟器。
下面介绍基于Modebus4j实现读线圈状态数据。
由于预定义的数学函数和/或Python脚本,寄存器值是动态生成的。
ModbusPal依赖于RxTx进行串行通信,而Jython则依赖于脚本支持。
Modbus4J:
Serotonin Software用Java编写的Modbus协议的高性能且易于使用的实现。
支持ASCII,RTU,TCP和UDP传输作为从站或主站,自动请求分区,响应数据类型解析和节点扫描。
JLibModbus:
JLibModbus是java语言中Modbus协议的一种实现。jSSC和RXTX用于通过串行端口进行通信。
该库是一个经过积极测试和改进的项目。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
1、基于modbus4j实现线圈状态数据的读取。
modbus4j
https://github.com/MangoAutomation/modbus4j
按照官网介绍,需要在pom文件中添加repository的配置
<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>
添加位置
然后添加依赖
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
2、新建modbus4j工具类
package com.badao.demo.utils;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
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.locator.BaseLocator;
public class Modbus4jUtils {
/**
* 工厂。
*/
static ModbusFactory
modbusFactory;
static {
if
(modbusFactory == null) {
modbusFactory = new
ModbusFactory();
}
}
/**
* 获取master
*
* @return
* @throws ModbusInitException
*/
public static
ModbusMaster getMaster(String ip,int port) throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost(ip);
params.setPort(port);
//
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]类型 开关数据
*
* @param
slaveId
* slaveId
* @param offset
* 位置
* @return 读取值
* @throws ModbusTransportException
* 异常
* @throws
ErrorResponseException
* 异常
*/
public static Boolean
readCoilStatus(ModbusMaster master,int slaveId, int offset)
throws ModbusTransportException,
ErrorResponseException {
// 01 Coil Status
BaseLocator<Boolean> loc =
BaseLocator.coilStatus(slaveId, offset);
Boolean
value = master.getValue(loc);
return value;
}
/**
* 读取[02 Input Status
1x]类型 开关数据
*
* @param
slaveId
* @param offset
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
*/
public static Boolean
readInputStatus(ModbusMaster master,int slaveId, int offset)
throws ModbusTransportException,
ErrorResponseException {
// 02 Input Status
BaseLocator<Boolean> loc =
BaseLocator.inputStatus(slaveId, offset);
Boolean
value = master.getValue(loc);
return value;
}
/**
* 读取[03 Holding
Register类型 2x]模拟量数据
*
*
@param slaveId
* slave Id
* @param offset
* 位置
* @param dataType
*
数据类型,来自com.serotonin.modbus4j.code.DataType
*
@return
* @throws ModbusTransportException
* 异常
* @throws
ErrorResponseException
* 异常
*/
public static Number
readHoldingRegister(ModbusMaster master,int slaveId, int offset, int
dataType)
throws ModbusTransportException,
ErrorResponseException {
// 03 Holding
Register类型数据读取
BaseLocator<Number> loc =
BaseLocator.holdingRegister(slaveId, offset, dataType);
Number value = master.getValue(loc);
return value;
}
/**
* 读取[04 Input
Registers 3x]类型 模拟量数据
*
* @param slaveId
* slaveId
* @param offset
* 位置
* @param dataType
*
数据类型,来自com.serotonin.modbus4j.code.DataType
*
@return 返回结果
* @throws ModbusTransportException
* 异常
* @throws
ErrorResponseException
* 异常
*/
public static Number
readInputRegisters(ModbusMaster master,int slaveId, int offset, int dataType)
throws ModbusTransportException,
ErrorResponseException {
// 04 Input
Registers类型数据读取
BaseLocator<Number> loc =
BaseLocator.inputRegister(slaveId, offset, dataType);
Number value = master.getValue(loc);
return value;
}
/**
* 批量读取使用方法
*
* @throws
ModbusTransportException
* @throws
ErrorResponseException
* @throws
ModbusInitException
* @return
*/
public static
BatchResults<Integer> batchRead(ModbusMaster
master,BatchRead<Integer> batchRead) throws ModbusTransportException,
ErrorResponseException, ModbusInitException {
//
是否连续请求
batchRead.setContiguousRequests(false);
BatchResults<Integer> results =
master.send(batchRead);
return results;
}
}
3、使用方式
新建master
ModbusMaster master = Modbus4jUtils.getMaster(modebustcpIp, port);
这里的ip和端口来自配置文件
单个读取数据
Boolean aBoolean = Modbus4jUtils.readCoilStatus(master, 1, 0);
这里1代表slaveId,0代表offset
批量读取数据
BatchRead<Integer> batch = new
BatchRead<>();
List<Locator>
locatorList = locatorConfig.getLocatorList();
locatorList.forEach(locator -> batch.addLocator(locator.getId(),
BaseLocator.coilStatus(locator.getSlaveId(),locator.getOffset())));
BatchResults<Integer> batchResults =
Modbus4jUtils.batchRead(master, batch);
Boolean
valueOne = (Boolean) batchResults.getValue(0);
Boolean valueTwo = (Boolean)
batchResults.getValue(1);
Boolean valueThree =
(Boolean) batchResults.getValue(2);
这里批量读取的配置来自配置文件
4、业务示例
在定时任务中批量读取指定slaveId和offset的线圈状态数据
import com.badao.demo.config.LocatorConfig;
import com.badao.demo.entity.Locator;
import com.badao.demo.utils.Modbus4jUtils;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import
com.serotonin.modbus4j.exception.ErrorResponseException;
import
com.serotonin.modbus4j.exception.ModbusInitException;
import
com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.beans.factory.annotation.Value;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.scheduling.annotation.EnableScheduling;
import
org.springframework.scheduling.annotation.Scheduled;
import java.time.LocalDateTime;
import java.util.List;
@Configuration
@EnableScheduling
public class
GetModbusTCPDataTask {
@Value("${modebustcp.ip}")
private String modebustcpIp;
@Value("${modebustcp.port}")
private Integer port;
@Autowired
private
LocatorConfig locatorConfig;
@Scheduled(fixedRateString = "2000")
public void getData() throws ModbusInitException {
ModbusMaster master =
Modbus4jUtils.getMaster(modebustcpIp, port);
BatchRead<Integer> batch = new BatchRead<>();
List<Locator> locatorList =
locatorConfig.getLocatorList();
locatorList.forEach(locator -> batch.addLocator(locator.getId(),
BaseLocator.coilStatus(locator.getSlaveId(),locator.getOffset())));
try {
//单个读取
//Boolean aBoolean =
Modbus4jUtils.readCoilStatus(master, 1, 0);
//批量读取
BatchResults<Integer>
batchResults = Modbus4jUtils.batchRead(master, batch);
Boolean valueOne = (Boolean)
batchResults.getValue(0);
Boolean valueTwo =
(Boolean) batchResults.getValue(1);
Boolean
valueThree = (Boolean) batchResults.getValue(2);
System.out.println(LocalDateTime.now());
System.out.println("valueOne:"+valueOne);
System.out.println("valueTwo:"+valueTwo);
System.out.println("valueThree:"+valueThree);
} catch (ModbusTransportException e) {
e.printStackTrace();
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (ModbusInitException e) {
e.printStackTrace();
}
}
}
5、本地测试效果
6、线上测试效果
线上使用RS485转以太网模块,会将其转换成MODBUS TCP服务端,端口为默认502,slaveid为2
通讯测试效果
7、SpringBoot中进行ModbusTCP通讯时提示:
com.serotonin.modbus4j.exception.ErrorResponseException:Illegal function
这是因为功能码不对应,使用Modbus Slave Definition定义的功能码为03 Holding Register(4x),而在代码中连接后执行
的是读取线圈状态的功能码
所以将功能码修改对应即可
标签:modbus4j,读取数据,serotonin,master,import,com,throws,SpringBoot From: https://www.cnblogs.com/badaoliumangqizhi/p/17935025.html