本文介绍通过Java程序向modbus slave模拟器工具读写数据。使用TCP的连接方式,modbus rtu通讯协议。
安装Modbus Slave
安装及使用请看上一篇文章:Modbus通讯协议(一)——测试工具ModbusPoll和ModbusSlave,本篇使用的是Modbus Slave 7.3.1 ,过低版本不支持TCP方式上使用modbus rtu
设置参数并开启连接
点击菜单Setup–>Resd/Write Definition 进行参数设置,Function 选01
点击菜单Connection–>connect F3进行连接,Connection选Modbus RTU Over TCP/IP
项目中添加依赖
<!-- 串口通讯-->
<dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtx</artifactId>
<version>2.1.7</version>
</dependency>
下载一依赖的SDK:net.wimpi.modbus
Java发送和读取工具类
工具类中有不同变量类型的读取和写数据的方法,方法要与modbus slave 寄存器设置的变量类型保持一致
package io.github.talelin.latticy.util;
import net.wimpi.modbus.io.ModbusRTUTCPTransaction;
import net.wimpi.modbus.msg.*;
import net.wimpi.modbus.net.RTUTCPMasterConnection;
import net.wimpi.modbus.procimg.InputRegister;
import net.wimpi.modbus.util.BitVector;
import java.net.InetAddress;
public class ModbusRtuUtil {
String ip="192.168.1.232";
int port=10000;
int slaveID=254;
RTUTCPMasterConnection Conn;
int ErrorCnt=0;
public void Init(String _ip,int _port,int _slaveID)
{
ip = _ip;
port = _port;
slaveID = _slaveID;
}
public boolean BeginConnect()
{
try {
InetAddress addr = InetAddress.getByName(ip);
// creat new tcp
Conn = new RTUTCPMasterConnection(addr,port);
Conn.setTimeout(3000);
Conn.connect();
} catch (Exception e) {
e.printStackTrace();
}
return IsConnect();
}
public boolean IsConnect()
{
if(ErrorCnt>10)
{
ErrorCnt = 0;
if(Conn==null)return false;
Conn.close();
Conn = null;
}
if(Conn==null)return false;
return Conn.isConnected();
}
public boolean DisConnect()
{
if(Conn==null)return false;
Conn.close();
return IsConnect();
}
//read the status of do . the count is the number of do channels
public BitVector readDO(int count) {
BitVector data = null;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return data;
}
if(count==0)return data;
//默认第一个继电器的地址0
ReadCoilsRequest req = new ReadCoilsRequest(0, count);
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
trans.execute();
ReadCoilsResponse res = ((ReadCoilsResponse) trans.getResponse());
data = res.getCoils();
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
//read the coils status? 读取单个继电器状态
public int readSignalDO(int regAddr ) {
int data = 0;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return -1;
}
ReadCoilsRequest req = new ReadCoilsRequest(regAddr, 1);
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
trans.execute();
ReadCoilsResponse res = ((ReadCoilsResponse) trans.getResponse());
if(res.getCoils().getBit(0)){
data = 1;
}
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
/**
* 写入数据到真机的DO类型的寄存器上面
*
* @param regAddr
* @param value
*/
public void writeSignalDO(int regAddr, int value) {
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return ;
}
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
WriteCoilRequest req = new WriteCoilRequest(regAddr, value==0?false:true);
req.setUnitID(slaveID);
trans.setRequest(req);
trans.execute();
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
}
/**
* 查询Function 为Input Status的寄存器
* 读取光耦输入状态
* @param count
*/
public BitVector readDI(int count) {
BitVector data = null;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return data;
}
if(count==0)return data;
// 第一个参数是寄存器的地址,第二个参数是读取多少个
ReadInputDiscretesRequest req = new ReadInputDiscretesRequest(0, count);
// 这里设置的Slave Id, 读取的时候这个很重要
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
// 执行查询
trans.execute();
// 得到结果
ReadInputDiscretesResponse res = (ReadInputDiscretesResponse) trans.getResponse();
data = res.getDiscretes();
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
public int readSignalDI(int regAddr) {
int data = 0;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return -1;
}
// 第一个参数是寄存器的地址,第二个参数时读取多少个
ReadInputDiscretesRequest req = new ReadInputDiscretesRequest(regAddr, 1);
// 这里设置的Slave Id, 读取的时候这个很重要
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
// 执行查询
trans.execute();
// 得到结果
ReadInputDiscretesResponse res = (ReadInputDiscretesResponse) trans.getResponse();
if(res.getDiscretes().getBit(0)){
data = 1;
}
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
public int[] readAI(int count) {
int[] data = null;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return data;
}
if(count==0)return data;
//这里重点说明下,这个地址和数量一定要对应起来
ReadInputRegistersRequest req = new ReadInputRegistersRequest(0, count);
//这个SlaveId一定要正确
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
trans.execute();
ReadInputRegistersResponse res = (ReadInputRegistersResponse) trans.getResponse();
InputRegister[] rst = res.getRegisters();
if(rst.length!=count)return data;
data = new int[count];
for(int i=0;i<count;i++)
data[i] = rst[i].getValue();
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
public int readSignalAI(int regAddr) {
int data = 0;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return -1;
}
//这里重点说明下,这个地址和数量一定要对应起来
ReadInputRegistersRequest req = new ReadInputRegistersRequest(regAddr, 1);
//这个SlaveId一定要正确
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
trans.execute();
ReadInputRegistersResponse res = (ReadInputRegistersResponse) trans.getResponse();
data = res.getRegisterValue(0);
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
public int readRegister(int regAddr) {
int data = 0;
try {
if(IsConnect()==false)
{
if(BeginConnect()==false)return -1;
}
ReadMultipleRegistersRequest req = new ReadMultipleRegistersRequest(regAddr, 1);
req.setUnitID(slaveID);
ModbusRTUTCPTransaction trans = new ModbusRTUTCPTransaction(Conn);
trans.setRequest(req);
trans.execute();
ReadMultipleRegistersResponse res = (ReadMultipleRegistersResponse) trans.getResponse();
data = res.getRegisterValue(0);
ErrorCnt = 0;
} catch (Exception e) {
e.printStackTrace();
ErrorCnt++;
}
return data;
}
}
测试
package io.github.talelin.latticy;
import io.github.talelin.latticy.util.ModbusRtuUtil;
import net.wimpi.modbus.util.BitVector;
import org.junit.Test;
public class ModbusTest {
@Test
public void handleDevice() throws Exception{
ModbusRtuUtil rtuUtil = new ModbusRtuUtil();
int MaxDONum = 4;//继电器数量
int MaxDINum = 4;//光耦输入数量
int MaxAINum = 4;//模拟量输入通道数量
//初始化设备
rtuUtil.Init("127.0.0.1", 502, 1); //ip,端口,slaveID 此处参数要与Modbus Slave中设置的一致
rtuUtil.BeginConnect();
System.out.println("写入数据到真机的DO类型的寄存器上面");
rtuUtil.writeSignalDO(0, 1);//Modbus Slave中Function Code选01
//读取DO 继电器
BitVector DOVal= rtuUtil.readDO(MaxDONum); //Modbus Slave中Function Code选01
// //读取DI
// BitVector DIVal= rtuUtil.readDI(MaxDINum);//Modbus Slave中Function Code选02
// //读取AI
// int[] AIVal =null;
// if(MaxAINum>0) AIVal= rtuUtil.readAI(MaxAINum);//Modbus Slave中Function Code选04
if(MaxDONum>0)PrintfDODIVal("DO Status:",DOVal,MaxDONum);
// if(MaxDINum>0) PrintfDODIVal("DI Status:",DIVal,MaxDINum);
// if(MaxAINum>0)PrintfAIVal("AI Status:",AIVal);
// Thread.sleep(1000);
//equip.DisConnect();
}
public static void PrintfDODIVal(String prev,BitVector val,int size)
{
System.out.println(prev);
if(val==null)return;
for(int i=0;i<size;i++)
System.out.print(" "+(val.getBit(i)?"1":"0"));
System.out.println("");
}
public static void PrintfAIVal(String prev,int[] val)
{
System.out.println(prev);
if(val==null)return;
for(int i=0;i<val.length;i++)
System.out.print(val[i]+" ");
System.out.println("");
}
}
写入成功并接收到响应
Function Code选02,并在Modbus Slave中设置值
//读取DI
BitVector DIVal= rtuUtil.readDI(MaxDINum);
if(MaxDINum>0) PrintfDODIVal("DI Status:",DIVal,MaxDINum);