【ModBus】modbus之modbus4j的使⽤和流程原理解析(5)hey-girl东拼西凑原创⽂章,若有歧义可留⾔,若需转载需标明出处
前⾔:接上篇继续说说modbus4j的⼏个常⽤⽅法.已经代码分析
项⽬中使⽤MouBus4j
说说实际开发中。⾸先创建⼀个springboot项⽬。pom引⼊依赖如下:
<repositories>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>ias-snapshots</id>
<name>Infinite Automation Snapshot Repository</name>
<url>maven.mangoautomation/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>maven.mangoautomation/repository/ias-release/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.scream3r</groupId>
<artifactId>jssc</artifactId>
<version>2.8.0</version>
</dependency>
</dependencies>
下⾯针对modbus4j⼏个常⽤⽅法进⾏分析,这样才能保证开发起来得⼼应⼿。程序为master(主),slave⼯具模拟从设备。在开始代码分析前:⾸先说说modbus4j这个库常⽤核⼼的类代表啥。
主机Master及其⼦类:主机的⼊⼝,数据流的起点和终点。
数据端⼝类StreamTransport:负责数据的写⼊和读出。
Modbus消息类ModbusMessage及其⼦类:⽀持Modbus定义的各种⽅法(FunctionCode)收发数据控制类MessageControl:⽀持 timeout、retries,默认200ms,1次。
收发等待室WaitingRoom:负责同步收发逻辑。
输出Request消息类:OutgoingRequestMessage 及其⼦类。
收到Response消息类:IncomingResponseMessage 及其⼦类。
解析类MessageParser:负责解析收到的消息。
协议数据类型定义:DataType
协议功能码定义:FunctionCode
协议寄存器范围:RegisterRange
A---- readHoldingRegisters⽅法,读保持寄存器。调⽤这个⽅法readHoldingRegisters(master,1,0,4) 进⾏分析。
public void readHoldingRegisters(ModbusMaster master,int slaveId,int start,int len){
try{
// 关键代码
ReadHoldingRegistersRequest request =new ReadHoldingRegistersRequest(slaveId, start, len);
ReadHoldingRegistersResponse response =(ReadHoldingRegistersResponse) master.send(request);
if(response.isException()){
<("Exception response: message="+ ExceptionMessage());
}else{
log.ShortData()));
}
}
catch(ModbusTransportException e){
e.printStackTrace();
}
finally{
master.destroy();
}
}
DemoA整体流程解析:
ModbusFactory类创建对应的Master对象。这⾥⽤到的是⼦类RtuMaster
构建ReadHoldingRegistersRequest(继承ModbusRequest)请求对象。⾥⾯就是包含从机地址,保持寄存器地址和数量等信息。ModbusRequest有很多⼦类。若我们需求不是读保持寄存器。就对应的⼦类构建就可以
调⽤ModbusMaster(抽象类)的send(ModbusRequest request)⽅法。先校验数据,主要是看看
你给request的参数是不是符合需求。然后会调⽤this.sendImpl(request),这其实就是调⽤的他的实现⼦类RtuMaster的sendImpl(ModbusRequest request)⽅法。这个⽅法的返回就是ModbusResponse。这⽅法做的就是发消息了会调⽤MessageControl的send。⽅法返回的是RtuMessageResponse。
MessageControl的send⽅法就会发送报⽂。并接收报⽂。
这⾥我们收到的数据应该是正常报⽂,就是从机地址,功能码,数据字节数,数据,校验这种格式。
但是MessageControl会做返回数据的处理。
MessageParser接⼝的parseMessage⽅法,是对消息处理的。实现类RtuMessageParser的parseMessageImpl是实际⼲活的会调⽤ateRtuMessageResponse(queue)这⾥queue是原始报⽂数据,RtuMessageResponse的创建需要ModbusResponse对象。所以这⾥createModbusResponse⽅法是关键,它会把从机地址,功能码以及校验去掉。还会调⽤((ModbusResponse)response).read(queue, isException);执⾏的就是readResponse这个抽象⽅法。这个⽅法会把原始数据进⾏处理。
这么描述只是让你⼤概熟悉流程,具体的代码解析,请往下看。
Q1-代码解析ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len)
group by的用法及原理详解A1- ReadHoldingRegistersRequest 继承 ReadNumericRequest 继承 ModbusRequest 继承ModbusMessage。创建⼀个ReadHoldingRegistersRequest对象的过程主要是验证slaveId的值是不是不符合规则的。然后保持寄存器起始位置和寄存器数量赋值的过程。也就是start是保持寄存器的起始位置。len表⽰的是保持寄存器数量。
Q1-(ReadHoldingRegistersResponse) master.send(request)
A1- master就是创建的RtuMaster对象。主要看send⽅法。先执⾏ModbusMaster⾥的send⽅法。
这⾥会调⽤validate⽅法。这个⽅法是ModbusRequest的抽象⽅法。
他的实现有10个。如图:
所以就来到ReadNumericRequest 的validate⽅法。
这个⽅法主要做就是对保持寄存器的这个起始位置值做了校验(取值合法范围0-65535)。对读寄存器的数量也进⾏校验。以及对读取的起始位置+读的数量。判断是否合规。就理解为发请求前对参数的校验。
在看他的第⼆个⽅法sendImpl,接收⼀个ModbusRequest参数。这个⽅法是它⾃⼰本⾝的抽象⽅法。
有5个具体实现
这⾥我们是RTU模式。主要看看rtu
这个⽅法主要是把传⼊的request对象包装成RtuMessageRequest对象。是MessageControl对象。然后调⽤send⽅法。send⽅法主要是把包装的request⾥⾯的参数变成byte[].也就是包装成发送的报⽂。
这串报⽂看着就是我们熟悉的标准rtu报⽂帧,01也就是从机地址,03功能码和data以及校验位2个字节。然后循环write发送。也就是和我们最开始的传参符合,报⽂就是读从机地址01,保持寄存器起始位置0,保持寄存器数量4.
我们看看响应,正常响应的报⽂应该是如下
01 03 08 00 00 00 00 00 16 00 00 74 13 (01从机地址,03功能码,08数据域字节数,00 00 00 00 00 16 00 00数据⾼低位,74 13校验)。正好对应从设备。(注意这⾥16是hex对应dec就是22)