admin管理员组

文章数量:1794759

Apache

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