我正在尝试从Modbus TCP设备读取String(用例1)和multiple type of data in one request(用例2)数据,但是它无法正确解码。

系统配置:


  Python 3.6.5
  Pymodbus:2.1.0
  平台:Windows 10 64位


Modbus TCP服务器:

import logging

from pymodbus.constants import Endian
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.payload import BinaryPayloadBuilder
from pymodbus.server.sync import StartTcpServer

class ModbusTCPServer(object):
    # initialize your data store:
    hrBuilder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big)
    # Usecase-1
    hrBuilder.add_string("abcdefghij")

    #Uncomment below three lines for usecase-2
    # hrBuilder.add_32bit_float(20.5)
    # hrBuilder.add_32bit_int(45)
    # hrBuilder.add_bits([1, 0, 0, 0, 0, 0, 0, 0])

    hrBlock = ModbusSequentialDataBlock(0, hrBuilder.to_registers() * 100)
    store = ModbusSlaveContext(hr=hrBlock, ir=hrBlock, di=hrBlock, co=hrBlock)
    slaves = {
        1: store,
    }
    context = ModbusServerContext(slaves=slaves, single=False)

    # initialize the server information
    identity = ModbusDeviceIdentification()

    modbusDeviceAddress = "127.0.0.1"
    modbusDevicePort = 501
    # run the TCP server

    # TCP:
    print("Modbus TCP Server started.")
    StartTcpServer(context, identity=identity, address=(modbusDeviceAddress, modbusDevicePort))


if __name__ == "__main__":
    print("Reading application configurations...")
    ModbusTCPServer();


Modbus TCP客户端:

from pymodbus.client.sync import ModbusTcpClient as ModbusClient
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.constants import Endian
from pymodbus.compat import iteritems

if __name__ == '__main__':
    client = ModbusClient('127.0.0.1', port=501)
    client.connect()
    result  = client.read_holding_registers(0, 5,  unit=1)
    print("Result : ",result)
    decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Big, wordorder=Endian.Big)
    # Usecase-1
    decoded = {
        'name': decoder.decode_string(10).decode(),
    }

    # Usecase-2
    # decoded = {
    #    'temp': decoder.decode_32bit_float(),
    #    'rpm': decoder.decode_32bit_int(),
    #    'status': decoder.decode_bits()
    #}

    for name, value in iteritems(decoded):
        print ("%s\t" % name, value)
    client.close()


输出用例1:

Result :  ReadRegisterResponse (5)
name     cdefghijab


Modbus客户端应将字符串解码为abcdefghij,但会将其解码为cdefghijab

输出用例2:

Result :  ReadRegisterResponse (5)
temp     0.0
rpm  2949376
status   [True, False, False, False, False, False, True, False]


看上面读取多个寄存器的输出,输出值与BinaryPayloadBuilder输入中给定的值不匹配。

我已经尝试了byteorderwordorder的所有组合,但是在任何情况下都无法使用。

请帮助我理解为什么这样解码数据?在编码或解码此数据时,我是否错过了要添加的内容?

仅供参考:此解决方案在Pymodbus 1.5.1版本上运行良好。最近,我已经升级了版本,但无法按预期工作。

任何帮助,将不胜感激。

最佳答案

Tl; dr。在zero_mode=True中使用ModbusSlaveContext

如果要映射客户端中的寄存器[0..n],则将其读取到服务器中的[0..n]。默认情况下,pymodbus服务器将地址[0..n]的寄存器读取映射到其内部存储中的寄存器[1..n]。这是要遵守modbus规范。引用pymodbus源代码。


#The slave context can also be initialized in zero_mode which means that a
# request to address(0-7) will map to the address (0-7). The default is
# False which is based on section 4.4 of the specification, so address(0-7)
# will map to (1-8)::



因此,根据您的情况,您可以将ModbusSequentialDataBlock的起始地址设置为1或使用ModbusSlaveContext初始化zero_mode=True

    hrBlock = ModbusSequentialDataBlock(1, hrBuilder.to_registers() * 100)
    # Or
    store = ModbusSlaveContext(hr=hrBlock, ir=hrBlock, di=hrBlock, co=hrBlock, zero_mode=True)

关于python - pymodbus:从Modbus设备读取字符串和多种类型的数据时出现问题,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/54709624/

10-10 03:05