admin管理员组文章数量:1794759
Apache
Apache-Plc4x-Modbus-Tcp学习记录(三)
- Apache-Plc4x-Modbus指令发送/接收配合源码学习(2)
- 发送
- ModbusTcpADUIO
- ModbusPDUIO
- ModbusPDUReadHoldingRegistersRequestIO
- 接收
- ModbusTcpADUIO
- ModbusPDUIO
- ModbusPDUReadHoldingRegistersResponseIO
- 后记
Apache-Plc4x-Modbus指令发送/接收配合源码学习(2)
本文承接上文 Apache-Plc4x-Modbus指令发送/接收配合源码学习(1),对Apache-Plc4x-Modbus的指令发送、接收IO数据流处理,结合源码学习(以read读取数据为例,memory-Area以HoldingRegisters为例)。
发送
上文我们已经将发送指令所需的参数整理好了,就是ModbusTcpADU参数包括:协议标识符、事务标识符、单位标识符和ModbusPDU。
接下来我们就需要将所有参数,转为二进制流发送给设备。
参数的需要经过:ModbusTcpADUIO的serialize、ModbusPDUIO的staticSerialize最后经过ModbusPDUReadHoldingRegistersRequestIO的staticSerialize指令发送设备完成,这些工作主要在readwrite包内的io包进行。
ModbusTcpADUIO
IO总方法,每次发送指令首先进入ModbusTcpADUIO的serialize。
将事务标识符、协议标识符、剩余长度、单位标识符转为二进制流发送;然后进入ModbusPDUIO的staticSerialize。
write部分:00 01 00 00 00 06 01 03 00 00 00 01
/*** 对象转换二进制流发送*/public static void staticSerialize(WriteBuffer io, ModbusRewriteTcpADU _value) throws ParseException {int transactionIdentifier = (int) _value.getTransactionIdentifier(); // 事务标识符io.writeUnsignedInt(16, ((Number) (transactionIdentifier)).intValue());io.writeUnsignedInt(16, ((Number) 0x0000).intValue()); // 协议标识符, 固定格式int length = (int) ((_value.getPdu().getLengthInBytes()) + (1)); // 剩余长度io.writeUnsignedInt(16, ((Number) (length)).intValue());short unitIdentifier = (short) _value.getUnitIdentifier(); // 单位标识符io.writeUnsignedShort(8, ((Number) (unitIdentifier)).shortValue());ModbusRewritePDU pdu = (ModbusRewritePDU) _value.getPdu(); // ModbusRewritePDUModbusRewritePDUIO.staticSerialize(io, pdu);}
ModbusPDUIO
ModbusPDUIO的staticSerialize负责将ModbusPDU中的错误标识符、功能码标识符转为二进制流发送;然后根据请求的类型进入不同staticSerialize方法。如:ModbusPDUReadHoldingRegistersRequest进入ModbusPDUReadHoldingRegistersRequestIO的staticSerialize。
write部分:00 01 00 00 00 06 01 03 00 00 00 01
/*** 根据ModbusRewritePDU类型判断是哪种请求,进入对应封装方法* @param io* @param _value* @throws ParseException*/public static void staticSerialize(WriteBuffer io, ModbusRewritePDU _value) throws ParseException {int startPos = io.getPos();boolean errorFlag = (boolean) _value.getErrorFlag(); // 错误标志io.writeBit((boolean) (errorFlag));short functionFlag = (short) _value.getFunctionFlag(); // 功能码io.writeUnsignedShort(7, ((Number) (functionFlag)).shortValue());// 判断数值类型if(_value instanceof ModbusRewritePDUError) {ModbusRewritePDUErrorIO.staticSerialize(io, (ModbusRewritePDUError) _value);} else if(_value instanceof ModbusRewritePDUReadHoldingRegistersRequest) {ModbusRewritePDUReadHoldingRegistersRequestIO.staticSerialize(io, (ModbusRewritePDUReadHoldingRegistersRequest) _value);} else if(_value instanceof ModbusRewritePDUReadHoldingRegistersResponse) {ModbusRewritePDUReadHoldingRegistersResponseIO.staticSerialize(io, (ModbusRewritePDUReadHoldingRegistersResponse) _value);} elseif(_value instanceof ModbusRewritePDUWriteMultipleHoldingRegistersRequest) {ModbusRewritePDUWriteMultipleHoldingRegistersRequestIO.staticSerialize(io, (ModbusRewritePDUWriteMultipleHoldingRegistersRequest) _value);} else if(_value instanceof ModbusRewritePDUWriteMultipleHoldingRegistersResponse) {ModbusRewritePDUWriteMultipleHoldingRegistersResponseIO.staticSerialize(io, (ModbusRewritePDUWriteMultipleHoldingRegistersResponse) _value);}}
ModbusPDUReadHoldingRegistersRequestIO
ModbusPDUReadHoldingRegistersRequestIO将ModbusPDUReadHoldingRegistersRequest中的寄存器起始地址、寄存器长度转为二进制流发送。
write部分:00 01 00 00 00 06 01 03 00 00 00 01
/*** 封装方法* @param io* @param _value* @throws ParseException*/public static void staticSerialize(WriteBuffer io, ModbusRewritePDUReadHoldingRegistersRequest _value) throws ParseException {int startingAddress = (int) _value.getStartingAddress(); // 寄存器起始地址io.writeUnsignedInt(16, ((Number) (startingAddress)).intValue());int quantity = (int) _value.getQuantity(); // 寄存器长度io.writeUnsignedInt(16, ((Number) (quantity)).intValue());}
至此发送指令二进制流结束,发送的指令格式为:00 01 00 00 00 06 01 03 00 00 00 01
00 01 :事务标识符
00 00 :协议标识符,固定格式
00 06 :长度
01 :单位标识符
03 :功能码
00 00 :寄存器起始地址
00 01 :寄存器长度
接收
接收与发送相反,是将二进制流转为实体类参数的过程。
二进制流经过:ModbusTcpADUIO的parse、ModbusTcpADUIO的staticParse最后经过ModbusPDUReadHoldingRegistersResponseIO的staticParse拿出设备返回的数据组。这些工作主要在readwrite包内的io包进行。
ModbusTcpADUIO
IO总方法,每次接收指令首先进入ModbusTcpADUIO的parse,效验收到指令的合法性,如果效验合格进入staticParse解析事务标识符、协议标识符、剩余长度、单位标识符、ModbusPDU。
在解析ModbusPDU时会调用ModbusPDUIO的staticParse。
read部分:00 01 00 00 00 05 01 03 02 00 01
/*** 返回二进制流转为对象*/@Overridepublic ModbusRewriteTcpADU parse(ReadBuffer io, Object... args) throws ParseException {if((args == null) || (args.length != 1)) {throw new PlcRuntimeException("参数数目错误,应为1:" + args.length);}Boolean response; // 响应是布尔类型if(args[0] instanceof Boolean) {response = (Boolean) args[0];} else if (args[0] instanceof String) {response = Boolean.valueOf((String) args[0]);} else {throw new PlcRuntimeException("参数0应为Boolean类型或字符串:" + args[0].getClass().getName());}return ModbusRewriteTcpADUIO.staticParse(io, response);}
/*** 解析事务标识符、协议标识符、剩余长度、单位标识符、ModbusPDU* @param io* @param response* @return* @throws ParseException*/public static ModbusRewriteTcpADU staticParse(ReadBuffer io, Boolean response) throws ParseException {int transactionIdentifier = io.readUnsignedInt(16); // 事务标识符int protocolIdentifier = io.readUnsignedInt(16); // 协议标识符if(protocolIdentifier != ModbusRewriteTcpADU.PROTOCOLIDENTIFIER) {throw new ParseException("期望常数值:" + ModbusRewriteTcpADU.PROTOCOLIDENTIFIER + "但是得到:" + protocolIdentifier);}int length = io.readUnsignedInt(16); // 剩余长度short unitIdentifier = io.readUnsignedShort(8); // 单位标识符ModbusRewritePDU pdu = ModbusRewritePDUIO.staticParse(io, (boolean) (response));// 创建实例return new ModbusRewriteTcpADU(transactionIdentifier, unitIdentifier, pdu);}
ModbusPDUIO
ModbusPDUIO的staticParse负责根据错误标识符、功能码标识符进入不同staticParse方法。如:错误标识符为true、功能码为03进入ModbusPDUReadHoldingRegistersResponseIO的staticParse。
read部分:00 01 00 00 00 05 01 03 02 00 01
/*** 根据ModbusRewritePDU类型判断是哪种请求,进入对应解包方法* @param io* @param response* @return* @throws ParseException*/public static ModbusRewritePDU staticParse(ReadBuffer io, Boolean response) throws ParseException {boolean errorFlag = io.readBit(); // 错误标识符short functionFlag = io.readUnsignedShort(7); // 功能码标识符// 根据errorFlag回复信息是否正确标识、functionFlag功能码标识、response true/false来进入对应的解析方法ModbusPDUBuilder builder = null;if(EvaluationHelper.equals(errorFlag, true)) {builder = ModbusRewritePDUErrorIO.staticParse(io);} else if(EvaluationHelper.equals(errorFlag, false) && EvaluationHelper.equals(functionFlag, 0x03) && EvaluationHelper.equals(response, false)) {builder = ModbusRewritePDUReadHoldingRegistersRequestIO.staticParse(io);} else if(EvaluationHelper.equals(errorFlag, false) && EvaluationHelper.equals(functionFlag, 0x03) && EvaluationHelper.equals(response, true)) {builder = ModbusRewritePDUReadHoldingRegistersResponseIO.staticParse(io);} elseif(EvaluationHelper.equals(errorFlag, false) && EvaluationHelper.equals(functionFlag, 0x10) && EvaluationHelper.equals(response, false)) {builder = ModbusRewritePDUWriteMultipleHoldingRegistersRequestIO.staticParse(io);} else if(EvaluationHelper.equals(errorFlag, false) && EvaluationHelper.equals(functionFlag, 0x10) && EvaluationHelper.equals(response, true)) {builder = ModbusRewritePDUWriteMultipleHoldingRegistersResponseIO.staticParse(io);}if (builder == null) {throw new ParseException("不支持该种组合errorFlag:"+errorFlag+"functionFlag:"+functionFlag+"response:"+response);}// 创建实例return builder.build();}
ModbusPDUReadHoldingRegistersResponseIO
ModbusPDUReadHoldingRegistersResponseIO将ModbusPDUReadHoldingRegistersResponse中的数值组解析封装成实体类。
read部分:00 01 00 00 00 05 01 03 02 00 01
/*** 解包方法* @param io* @return* @throws ParseException*/public static ModbusPDUReadHoldingRegistersResponseBuilder staticParse(ReadBuffer io) throws ParseException {short byteCount = io.readUnsignedShort(8);if(byteCount > Integer.MAX_VALUE) {throw new ParseException("数组计数:" + (byteCount) + "超过允许的最大计数:" + Integer.MAX_VALUE);}byte[] value;{int itemCount = Math.max(0, (int) byteCount);value = new byte[itemCount];for(int curItem = 0; curItem < itemCount; curItem++) {value[curItem] = io.readByte(8);}}return new ModbusPDUReadHoldingRegistersResponseBuilder(value);}
至此接收指令二进制流结束,接收的指令格式为:00 01 00 00 00 05 01 03 02 00 01
00 01 :事务标识符
00 00 :协议标识符,固定格式
00 05 :剩余长度
01 :单位标识符
03 :功能码
02 :数据长度
00 01 :数据值
后记
由此我们可以看出Apache-Plc4x-Modbus指令格式是Modbus-TCP格式和Modbus-RTU格式不同:多了事务标识符、协议标识符、剩余长度;发送没有附加CRC效验并且接收没有效验CRC效验。
如果使用Modbus-RTU协议与设备通讯,需要重写。
附Modbus-RTU协议指令格式:
读发送:
读接收:
写发送:
写接收:
本文只是记录本人学习过程中的理解,可能有部分内容有纰漏,望各位不吝赐教。
本文标签: Apache
版权声明:本文标题:Apache 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1693237214a250319.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论