使用java通过modbus读取前端设备数据
背景
中频炉改造项目,因现场没有一级plc、点位采集设备支撑,故采用modbus485协议进行采集。项目中使用到了redis,主要是对数据采集的临时存储,用于业务触发。点位数据存储到iotdb时序数据库,用于过程质量的追溯和展示。
点位数据管理
使用modbus485读取点位数据,需要配置点位的以下参数。
1、IP地址:设备所在ip段;
2、端口:每款设备需要在集线器分配端口;
3、从机地址:Modbus协议中,每个从设备都有一个唯一的地址,范围通常是1到247;
4、寄存器读取开始位置:寄存器的起始地址;
5、读取寄存器的数量:需要读取的寄存器数量;
6、功能码:根据设备不同,所选功能码不同。
7、点位名称:业务自己命名,便于管理;
8、点位编码:触发系统业务模块使用,与读取数据无关;
引入modbus依赖
<dependency><groupId>com.intelligt.modbus</groupId><artifactId>jlibmodbus</artifactId><version>1.2.9.7</version></dependency>
读取数据
@Component
public class ModbusTcpListener {@Resourceprivate RedisTemplate redisTemplate;@Resourceprivate IotDbServer iotDbServer;@Resourceprivate ModbusApiService modbusApiService;private static final ObjectMapper objectMapper = new ObjectMapper();public static void main(String[] args) {}
// @Scheduled(cron = "0/30 * * * * ?")public void getData() throws UnknownHostException, JsonProcessingException {String json = (String) redisTemplate.opsForValue().get("modbus");List<ModBusRequest> lis2t = objectMapper.readValue(json, new TypeReference<List<ModBusRequest>>() {});for (ModBusRequest request : lis2t) {int slaveId = request.getSlaveId();//从机地址int offset = request.getOffset();//寄存器读取开始地址int quantity = request.getQuantity();//读取的寄存器数量String pointCode=request.getPointCode();//设备codeint port=request.getPort();//端口List<JSONObject> jsonObjectList = new ArrayList<>();// 设置主机TCP参数TcpParameters tcpParameters = new TcpParameters();// 设置TCP的ip地址InetAddress adress = InetAddress.getByName(request.getIp());// TCP参数设置ip地址tcpParameters.setHost(adress);// TCP设置长连接tcpParameters.setKeepAlive(true);// TCP设置端口,这里设置是默认端口502tcpParameters.setPort(request.getPort());// 创建一个主机ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);Modbus.setAutoIncrementTransactionId(true);try {// 读取对应从机的数据,readInputRegisters读取的写寄存器,功能码04// 读取对应从机的数据,readHoldingRegisters,功能码03if (!master.isConnected()) {master.connect();// 开启连接}System.out.printf("=========>"+request);//根据寄存器的功能码判断switch (request.getDeviceId()) {case "1":getBooleanData(master.readCoils(slaveId, offset, quantity), offset,pointCode,request.getIp(),port);break;case "2":getBooleanData(master.readDiscreteInputs(slaveId, offset, quantity), offset,pointCode,request.getIp(),port);break;case "3":getIntData(master.readHoldingRegisters(slaveId, offset, quantity), offset,pointCode,request.getIp(),port);break;case "4":getIntData(master.readInputRegisters(slaveId, offset, quantity), offset,pointCode,request.getIp(),port);break;default:getIntData(master.readHoldingRegisters(slaveId, offset, quantity), offset,pointCode,request.getIp(),port);break;}
// Thread.sleep(1000); // 每隔1000毫秒读取一次数据 getIntData(registerValues,offset);} catch (ModbusProtocolException | ModbusIOException | ModbusNumberException e) {e.printStackTrace();} finally {try {master.disconnect();} catch (ModbusIOException e) {e.printStackTrace();}}}}/*** int类型数据方法** @param registerValues* @param offset*/public void getIntData(int[] registerValues, int offset,String pointCode,String ip,int port) {for (int value : registerValues) {System.out.println(" offset: " + offset);System.out.println(" Value: " + value);IotDbParam iotDbParam=new IotDbParam();iotDbParam.setPk(pointCode);iotDbParam.setTime(System.currentTimeMillis());iotDbParam.setBreath(String.valueOf(value));iotDbParam.setHeart(String.valueOf(offset));try {iotDbServer.insertData(iotDbParam);redisTemplate.opsForValue().set(pointCode+"-"+offset,String.valueOf(value));ModbusApiDto modbusApiDto = new ModbusApiDto();modbusApiDto.setPointCode(pointCode);modbusApiDto.setTriggerTime(new Date());modbusApiDto.setValue(String.valueOf(value));modbusApiDto.setOffset(offset);//192.168.0.7 炉后天车称| 192.168.0.233 三项电表| 192.168.0.7 炉后小车位置激光测距| 192.168.0.7 炉后大车位置激光测距if (("192.168.0.7").equals(ip) && port == 23 && offset == 6 ||("192.168.0.233").equals(ip) && port == 502 && offset == 11 ||("192.168.0.7").equals(ip) && port == 35 && offset == 3 ||("192.168.0.7").equals(ip) && port == 35 && offset == 4) {modbusApiService.dealModbusData(JSON.toJSONString(modbusApiDto));}} catch (StatementExecutionException e) {e.printStackTrace();} catch (ServerException e) {e.printStackTrace();} catch (IoTDBConnectionException e) {e.printStackTrace();}offset++;}}/*** boolean类型数据方法** @param readDataBoolean* @param offset*/public void getBooleanData(boolean[] readDataBoolean, int offset,String pointCode,String ip,int port) {for (boolean value : readDataBoolean) {System.out.println(" offset: " + offset);System.out.println(" Value: " + value);IotDbParam iotDbParam=new IotDbParam();iotDbParam.setPk(pointCode);iotDbParam.setTime(System.currentTimeMillis());iotDbParam.setBreath(value==true?"1":"0");iotDbParam.setHeart(String.valueOf(offset));try {iotDbServer.insertData(iotDbParam);redisTemplate.opsForValue().set(pointCode+"-"+offset,String.valueOf(value));ModbusApiDto modbusApiDto=new ModbusApiDto();//小车振动信号if(("192.168.0.7").equals(ip) && port==29 &&(offset==200 || offset==201)){modbusApiDto.setPointCode(pointCode);modbusApiDto.setTriggerTime(new Date());modbusApiDto.setValue(iotDbParam.getBreath());modbusApiDto.setOffset(offset);modbusApiService.dealModbusData(JSON.toJSONString(modbusApiDto));}} catch (StatementExecutionException e) {e.printStackTrace();} catch (ServerException e) {e.printStackTrace();} catch (IoTDBConnectionException e) {e.printStackTrace();}offset++;}}}
补充说明
读取offset,是因为设备寄存器读取数量是固定的(可能是多个),里面有无效值,判断offset是为了读取有效值进行存储,可根据前端设备说明书进行相应取值。