<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
1.实现类:DeviceOperationsTask
package com.aiswebservice.interf.quartz.task;
import com.aiswebservice.common.mapper.CommonSqlMapper;
import com.aiswebservice.common.modbusapi.ModbusTcpMaster;
import com.aiswebservice.common.scadaapi.ScadaClientApi;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import lombok.extern.slf4j.Slf4j;
import net.wimpi.modbus.Modbus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.aiswebservice.common.modbusapi.Modbus4jWriteUtils.writeHoldingRegister;
/**
* 检测运算JOB,间隔10分钟执行一次
*/
@Slf4j
@Component
public class DeviceOperationsTask {
@Autowired
private CommonSqlMapper commonSqlMapper;
@Autowired
@Qualifier(value = "ModbusTcpMaster")
ModbusTcpMaster masterTcp;
public void run() throws Exception {
try {
String sqlTag ="select operations_id,codesys_ip,last_value, data_type,tag_addr,trueaddr,tag_des from sys_codesys_operations ORDER BY operations_id";
List<Map<String, Object>> itemList=commonSqlMapper.ExecSelectSql(sqlTag);
if(itemList!=null && itemList.size()>0) {
for (Map<String, Object> mapOperations : itemList) {
try{
String data_type=mapOperations.get("data_type").toString();
String codesys_ip=mapOperations.get("codesys_ip").toString();
String trueaddr = mapOperations.get("trueaddr").toString();
String operations_id = mapOperations.get("operations_id").toString();
String tag_addr = mapOperations.get("tag_addr").toString();
int port = 502;
ModbusMaster master = masterTcp.getSlave(codesys_ip, port);
if(data_type.equals("DataType.FOUR_BYTE_INT_SIGNED_SWAPPED")){
int last_value =Integer.parseInt(mapOperations.get("last_value").toString());
int TagValue = Integer.parseInt(ScadaClientApi.ReadScadaTagValue(tag_addr)==null? "0":ScadaClientApi.ReadScadaTagValue(tag_addr));
if(last_value < TagValue && TagValue!=last_value){
String updateTagValueSql = "UPDATE sys_codesys_operations SET last_value ='"+TagValue+"'" +
" WHERE operations_id ='"+operations_id+"' ";
commonSqlMapper.ExecUpdateSql(updateTagValueSql);
log.info("--------------------运行时间-运算点位定时任务记录修改成功---------------------");
}else if(last_value >TagValue){
//DWordH DataType.FOUR_BYTE_INT_SIGNED_SWAPPED 7
writeHoldingRegister(master, 1, Integer.parseInt(trueaddr), last_value,7);//DataType.FOUR_BYTE_INT_SIGNED_SWAPPED 7
log.info("--------------------运行时间-掉电反写成功---------------------");
}
}else if(data_type.equals("DataType.FOUR_BYTE_FLOAT_SWAPPED")){
Float last_value =Float.parseFloat(mapOperations.get("last_value").toString());
Float TagValue = Float.parseFloat(ScadaClientApi.ReadScadaTagValue(tag_addr)==null? "0":ScadaClientApi.ReadScadaTagValue(tag_addr));
if(last_value < TagValue && TagValue!=last_value){
String updateTagValueSql = "UPDATE sys_codesys_operations SET last_value ='"+TagValue+"'" +
" WHERE operations_id ='"+operations_id+"' ";
commonSqlMapper.ExecUpdateSql(updateTagValueSql);
log.info("--------------------累计行程-运算点位定时任务记录修改成功---------------------");
}else if(last_value >TagValue){
//float DataType.FOUR_BYTE_FLOAT_SWAPPED 9
writeHoldingRegister(master, 1, Integer.parseInt(trueaddr), last_value,9);//DataType.FOUR_BYTE_FLOAT_SWAPPED 9
log.info("--------------------累计行程-掉电反写成功---------------------");
}
}
}catch (Exception e){
e.printStackTrace();
}
}
}
} catch (Exception ex) {
throw ex;
}
}
}
2.Modbus4jWriteUtils
package com.aiswebservice.common.modbusapi;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
public class Modbus4jWriteUtils{
/**
* 写单个(线圈)开关量数据
* 功能码为:05,开关量输出点Q置位或复位,写入数据到真机的DO类型的寄存器上面,可以读写的布尔类型(0x)
* @param slaveId slave的ID
* @param writeOffset 位置-预访问的地址-地址范围:0-255
* @param writeValue 值-置位则为1,复位则为0
* @return 是否写入成功
*/
public static boolean writeCoil(ModbusMaster master,int slaveId, int writeOffset, boolean writeValue){
boolean flag = false;
try {
// 创建请求
WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
// 发送请求并获取响应对象
WriteCoilResponse response = (WriteCoilResponse) master.send(request);
flag = !response.isException();
}catch (ModbusTransportException e){
e.printStackTrace();
}
return flag;
}
/**
* 写多个开关量数据(线圈)
* 功能码为:0F,写多个开关量数据(线圈)
* @param slaveId slaveId
* @param startOffset 开始位置
* @param bdata 写入的数据
* @return 是否写入成功
*/
public static boolean writeCoils(ModbusMaster master,int slaveId, int startOffset, boolean[] bdata) {
boolean flag = false;
try {
// 创建请求
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
// 发送请求并获取响应对象
WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);
flag = !response.isException();
}catch (ModbusTransportException e){
e.printStackTrace();
}
return flag;
}
/***
* 保持寄存器写单个
* 功能码为:06,将数据写入至V存储器, 数据到真机,数据类型是Int,可以读写的数字类型(4x)
* @param slaveId slaveId
* @param writeOffset 开始位置
* @param writeValue 写入的数据
*/
public static boolean writeRegister(ModbusMaster master,int slaveId, int writeOffset, short writeValue){
boolean flag = false;
try {
// 创建请求对象
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
// 发送请求并获取响应对象
WriteRegisterResponse response = (WriteRegisterResponse) master.send(request);
flag = !response.isException();
}catch (ModbusTransportException e){
e.printStackTrace();
}
return flag;
}
/**
* 保持寄存器写入多个模拟量数据
* 功能码为:16,将数据写入至多个V存储器,写入数据到真机,数据类型是short[],可以读写的数字类型(4x)
* @param slaveId modbus的slaveID
* @param startOffset 起始位置偏移量值
* @param sdata 写入的数据
* @return 返回是否写入成功
*/
public static boolean writeRegisters(ModbusMaster master,int slaveId, int startOffset, short[] sdata) {
boolean flag = false;
try {
// 创建请求对象
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
// 发送请求并获取响应对象
WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);
flag = !response.isException();
}catch (ModbusTransportException e){
e.printStackTrace();
}
return flag;
}
/**
* 根据类型写数据(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
*
* @param value 写入值
* @param dataType com.serotonin.modbus4j.code.DataType
*/
public static void writeHoldingRegister(ModbusMaster master, int slaveId, int offset, Number value, int dataType) {
try {
// 类型
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
master.setValue(locator, value);
}catch (Exception e){
e.printStackTrace();
}
}
}
3。Modbus4jTypeUtil
package com.aiswebservice.common.modbusapi;
import com.serotonin.modbus4j.code.DataType;
public class Modbus4jTypeUtil {
public static int getValueType(String type) {
switch (type.toLowerCase()) {
case "int":
return DataType.TWO_BYTE_INT_SIGNED;
case "long":
return DataType.FOUR_BYTE_INT_SIGNED;
case "long long":
return DataType.EIGHT_BYTE_INT_SIGNED;
case "float":
return DataType.FOUR_BYTE_FLOAT;
case "double":
return DataType.EIGHT_BYTE_FLOAT;
}
return DataType.TWO_BYTE_INT_SIGNED;
}
}
4.Modbus4jReadUtil
package com.aiswebservice.common.modbusapi;
import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ErrorResponseException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
public class Modbus4jReadUtil {
/**
* 读取[01 Coil Status 0x]类型 开关数据
*
* @param slaveId slaveId
* @param offset 位置
* @return 读取值
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
*/
public static Boolean readCoilStatus(ModbusMaster master, int slaveId, int offset, String dev_code){
// 01 Coil Status
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
try {
return master.getValue(loc);
}catch (Exception e){
if (e.getMessage().equals("java.net.SocketTimeoutException: connect timed out")) System.err.println(dev_code+":"+e.getMessage());
else e.printStackTrace();
return null;
}
}
/**
* 读取[02 Input Status 1x]类型 开关数据
*
* @param slaveId
* @param offset
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
*/
public static Boolean readInputStatus(ModbusMaster master,int slaveId, int offset,String dev_code) {
// 02 Input Status
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
try{
return master.getValue(loc);
}catch (Exception e){
if (e.getMessage().equals("java.net.SocketTimeoutException: connect timed out")) System.err.println(dev_code+":"+e.getMessage());
else e.printStackTrace();
return null;
}
}
/**
* 读取[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,String dev_code) {
// 03 Holding Register类型数据读取
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
try {
return master.getValue(loc);
}catch (Exception e){
if (e.getMessage().equals("java.net.SocketTimeoutException: connect timed out")) System.err.println(dev_code+":"+e.getMessage());
else e.printStackTrace();
return null;
}
}
/**
* 读取[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,String dev_code) {
// 04 Input Registers类型数据读取
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
try{
return master.getValue(loc);
}catch (Exception e){
if (e.getMessage().equals("java.net.SocketTimeoutException: connect timed out")) System.err.println(dev_code+":"+e.getMessage());
else e.printStackTrace();
return null;
}
}
/**
* 批量读取使用方法
*
* @throws ModbusTransportException
* @throws ErrorResponseException
*/
public static void batchRead(ModbusMaster master) throws ModbusTransportException, ErrorResponseException {
BatchRead<Integer> batch = new BatchRead<>();
for (int i = 0; i < 10; i++) {
batch.addLocator(i,BaseLocator.coilStatus(1, i));
}
batch.setContiguousRequests(true);
BatchResults<Integer> results = master.send(batch);
System.out.println(results);
}
}
5.ModbusTcpMaster
package com.aiswebservice.common.modbusapi;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.ip.IpParameters;
import org.springframework.stereotype.Service;
@Service(value = "ModbusTcpMaster")
public class ModbusTcpMaster {
private final ModbusFactory modbusFactory = new ModbusFactory();
/**
* 获取slave
* @return
* @throws ModbusInitException
*/
public ModbusMaster getSlave(String ip, int port) {
ModbusMaster master = null;
try {
IpParameters params = new IpParameters();
params.setHost(ip);
params.setPort(port);
//这个属性确定了协议帧是否是通过tcp封装的RTU结构,采用modbus tcp/ip时,要设为false, 采用modbus rtu over tcp/ip时,要设为true
params.setEncapsulated(false);
// modbusFactory.createRtuMaster(wapper); //RTU 协议
// modbusFactory.createUdpMaster(params);//UDP 协议
// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
master = modbusFactory.createTcpMaster(params, false);
//最大等待时间
master.setTimeout(2000);
//最大连接次数
master.setRetries(5);
master.init();
} catch (ModbusInitException e) {
e.printStackTrace();
}
return master;
}
}