Pymodbus:使用反应器的 Modbus RTU - 从未调用过回调
Pymodbus: Modbus RTU using reactor - callback never called
我正在尝试创建 Modbus serial/RTU 客户端,它将使用 Pymodbus 库从串口读取数据。
Python: 3.6
Pymodbus: 2.1.0
Platform: Linux/Windows
我的示例代码库如下:
def readDevices(modbusRTUDevice):
deviceIP = modbusRTUDevice["ip"]
devicePort = modbusRTUDevice["port"]
logger.info("Connecting to Modbus RTU device at address {0}".format(deviceIP + ":" + str(devicePort)))
modbusClientFactory = CustomModbusClientFactory()
modbusClientFactory.address = deviceIP
modbusClientFactory.modbusDevice = modbusRTUDevice
SerialModbusClient(modbusClientFactory, devicePort, reactor)
Thread(target=reactor.run, args=(False,)).start()
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
serialport.SerialPort.__init__(self, factory.buildProtocol(), *args, **kwargs)
class CustomModbusClientFactory(protocol.ClientFactory, ModbusClientMixin):
modbusDevice = {}
def buildProtocol(self, addr=None):
modbusClientProtocol = CustomModbusClientProtocol()
modbusClientProtocol.factory = self
modbusClientProtocol.modbusDevice = self.modbusDevice
return modbusClientProtocol
class CustomModbusClientProtocol(ModbusClientProtocol):
def connectionMade(self):
framer = ModbusRtuFramer.__init__(self, ClientDecoder(), client=None)
ModbusClientProtocol.__init__(self, framer, baudrate=9600, parity='E', bytesize=8, stopbits=1, timeout=0.2, retryOnEmpty=True, retries=3)
ModbusClientProtocol.connectionMade(self)
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
logger.info("Modbus RTU device connected at address {0}".format(deviceIP + ":" + str(devicePort)))
self.read()
def read(self):
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
slaveAddress = self.modbusDevice["slaveAddress"]
logger.info("Reading holding registers of Modbus RTU device at address {0}...".format(deviceIP + ":" + str(devicePort)))
deferred = self.read_holding_registers(self.startingAddress, self.registerCount, unit=slaveAddress)
deferred.addCallbacks(self.requestFetched, self.requestNotFetched)
def requestNotFetched(self, error):
logger.info("Error reading registers of Modbus RTU device : {0}".format(error))
sleep(0.5)
def requestFetched(self, response):
logger.info("Inside request fetched...")
#Do some other stuff here
reactor.callLater(0, self.read)
调试后,read()方法的self
表示CustomModbusClientProtocol: Null Transport
。在此之后,我按 F8
& 线程进入阻塞状态 & 延迟的回调从未调用过。
输出:
INFO:__main__: (2019-01-30 15:42:53; Test.py:200 Connecting to Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0)
INFO:__main__: (2019-01-30 15:42:53; Test.py:70 Modbus RTU device connected at address 127.0.0.1:/dev/ttyUSB0)
INFO:__main__: (2019-01-30 15:46:18; Test.py:87 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0...)
根据Serial + Async + RTU: Callback is never fired #160,deferred调用回调的问题已经修复。但是,就我而言,它仍然存在。
我在 Raspberry PI 和 Windows 中都尝试过此代码,在这两种情况下得到相同的响应。
不明白为什么会这样。我是否需要在我的代码中添加一些东西,或者我应该在通过串行端口连接到 Modbus 设备时做错什么?
如有任何帮助,我们将不胜感激。
编辑
更新代码和日志
问题是您在 CustomModbusClientProtocol
class 的 connectionMade
中直接调用 self.read()
,在反应器实际启动之前。您将不得不等待反应堆启动,然后 schedule read
稍后 reactor.callLater(<time-to-wait-in-seconds>, self.read)
。
修改后的 CustomModbusClientProtocol
代码看起来像这样
from twisted.internet import serialport, reactor
from twisted.internet import protocol
from pymodbus.factory import ClientDecoder
from pymodbus.client.async.twisted import ModbusClientProtocol
from pymodbus.transaction import ModbusRtuFramer
from threading import Thread
from time import sleep
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def readDevices(modbusRTUDevice):
deviceIP = modbusRTUDevice["ip"]
devicePort = modbusRTUDevice["port"]
logger.info("Connecting to Modbus RTU device at address {0}".format(deviceIP + ":" + str(devicePort)))
modbusClientFactory = CustomModbusClientFactory()
modbusClientFactory.address = deviceIP
modbusClientFactory.modbusDevice = modbusRTUDevice
SerialModbusClient(modbusClientFactory, devicePort, reactor)
Thread(target=reactor.run, args=(False,)).start()
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
serialport.SerialPort.__init__(self, factory.buildProtocol(), *args, **kwargs)
class CustomModbusClientFactory(protocol.ClientFactory):
modbusDevice = {}
def buildProtocol(self, addr=None):
modbusClientProtocol = CustomModbusClientProtocol()
modbusClientProtocol.factory = self
modbusClientProtocol.modbusDevice = self.modbusDevice
return modbusClientProtocol
class CustomModbusClientProtocol(ModbusClientProtocol):
def connectionMade(self):
framer = ModbusRtuFramer(ClientDecoder(), client=None)
ModbusClientProtocol.__init__(self, framer, baudrate=9600, parity='E', bytesize=8, stopbits=1, timeout=0.2, retryOnEmpty=True, retries=3)
ModbusClientProtocol.connectionMade(self)
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
logger.info("Modbus RTU device connected at address logger{0}".format(deviceIP + ":" + str(devicePort)))
reactor.callLater(5, self.read)
def read(self):
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
slaveAddress = self.modbusDevice["slaveAddress"]
logger.info("Reading holding registers of Modbus RTU device at address {0}...".format(deviceIP + ":" + str(devicePort)))
deferred = self.read_holding_registers(0, 10, unit=slaveAddress)
deferred.addCallbacks(self.requestFetched, self.requestNotFetched)
def requestNotFetched(self, error):
logger.info("Error reading registers of Modbus RTU device : {0}".format(error))
sleep(0.5)
def requestFetched(self, response):
logger.info("Inside request fetched...")
#Do some other stuff here
reactor.callLater(0, self.read)
readDevices({"ip": "127.0.0.1", "port": "/dev/ptyp0", "slaveAddress": 1})
日志:
$ python scratch_118.py
2019-01-31 12:34:56,734 MainThread INFO scratch_118 :21 Connecting to Modbus RTU device at address 127.0.0.1:/dev/ptyp0
2019-01-31 12:34:56,735 MainThread DEBUG __init__ :80 Client connected to modbus server
2019-01-31 12:34:56,735 MainThread INFO scratch_118 :48 Modbus RTU device connected at address logger127.0.0.1:/dev/ptyp0
2019-01-31 12:35:01,737 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:01,738 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:01,738 Thread-1 DEBUG transaction :418 Adding transaction 1
2019-01-31 12:35:02,196 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:02,197 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:02,197 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:02,197 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:02,197 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:02,198 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:02,198 Thread-1 DEBUG transaction :418 Adding transaction 2
2019-01-31 12:35:03,202 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:03,202 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:03,202 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:03,202 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:03,203 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:03,203 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:03,203 Thread-1 DEBUG transaction :418 Adding transaction 3
2019-01-31 12:35:04,207 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:04,207 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:04,208 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:04,208 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:04,208 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:04,208 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:04,209 Thread-1 DEBUG transaction :418 Adding transaction 4
2019-01-31 12:35:05,213 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:05,213 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:05,214 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:05,214 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:05,214 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:05,214 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:05,215 Thread-1 DEBUG transaction :418 Adding transaction 5
2019-01-31 12:35:06,218 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:06,218 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:06,219 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:06,219 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:06,219 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
以下解决方案适用于我。我的设备配置如下:
Device Configuration:
Baudrate=9600, Parity='E', Bytesize=8 Timeout=0.2
Protocol: RTU
PLC is connected to RaspberryPI using serial-to-USB converter.
工作代码:
import logging
from threading import Thread
from time import sleep
from pymodbus.client.async.twisted import ModbusClientProtocol
from pymodbus.constants import Endian
from pymodbus.factory import ClientDecoder
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.transaction import ModbusRtuFramer
from serial import EIGHTBITS
from serial import PARITY_EVEN
from serial import STOPBITS_ONE
from twisted.internet import protocol
from twisted.internet import serialport, reactor
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def readDevices(modbusRTUDevice):
deviceIP = modbusRTUDevice["ip"]
devicePort = modbusRTUDevice["port"]
logger.info("Connecting to Modbus RTU device at address {0}".format(deviceIP + ":" + str(devicePort)))
modbusClientFactory = CustomModbusClientFactory()
modbusClientFactory.address = deviceIP
modbusClientFactory.modbusDevice = modbusRTUDevice
SerialModbusClient(modbusClientFactory, devicePort, reactor, baudrate=9600, bytesize=EIGHTBITS,
parity=PARITY_EVEN, stopbits=STOPBITS_ONE, timeout=0.2, xonxoff=0, rtscts=0)
Thread(target=reactor.run, args=(False,)).start() # @UndefinedVariable
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
serialport.SerialPort.__init__(self, factory.buildProtocol(), *args, **kwargs)
class CustomModbusClientFactory(protocol.ClientFactory):
modbusDevice = {}
def buildProtocol(self, addr=None):
modbusClientProtocol = CustomModbusClientProtocol()
modbusClientProtocol.factory = self
modbusClientProtocol.modbusDevice = self.modbusDevice
return modbusClientProtocol
def clientConnectionLost(self, connector, reason):
modbusTcpDeviceIP = self.modbusDevice["ip"]
modbusTcpDevicePort = self.modbusDevice["port"]
logger.critical("Connection lost with device running on {0}:{1}.".format(modbusTcpDeviceIP, modbusTcpDevicePort))
logger.critical("Root Cause : {0}".format(reason))
connector.connect()
def clientConnectionFailed(self, connector, reason):
modbusTcpDeviceIP = self.modbusDevice["ip"]
modbusTcpDevicePort = self.modbusDevice["port"]
logger.critical("Connection failed with device running on {0}:{1}.".format(modbusTcpDeviceIP, modbusTcpDevicePort))
logger.critical("Root Cause : {0}".format(reason))
connector.connect()
class CustomModbusClientProtocol(ModbusClientProtocol):
def connectionMade(self):
framer = ModbusRtuFramer(ClientDecoder(), client=None)
ModbusClientProtocol.__init__(self, framer)
ModbusClientProtocol.connectionMade(self)
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
logger.info("Modbus RTU device connected at address logger{0}".format(deviceIP + ":" + str(devicePort)))
reactor.callLater(5, self.read) # @UndefinedVariable
def read(self):
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
slaveAddress = self.modbusDevice["slaveAddress"]
logger.info("Reading holding registers of Modbus RTU device at address {0}...".format(deviceIP + ":" + str(devicePort)))
deferred = self.read_holding_registers(0, 1, unit=slaveAddress)
deferred.addCallbacks(self.requestFetched, self.requestNotFetched)
def requestNotFetched(self, error):
logger.info("Error reading registers of Modbus RTU device : {0}".format(error))
sleep(0.5)
def requestFetched(self, response):
logger.info("Inside request fetched...")
decoder = BinaryPayloadDecoder.fromRegisters(response.registers, byteorder=Endian.Big, wordorder=Endian.Big)
skipBytesCount = 0
decoder.skip_bytes(skipBytesCount)
registerValue = decoder.decode_16bit_int()
skipBytesCount += 2
logger.info("Sensor updated to value '{0}'.".format(registerValue))
reactor.callLater(5, self.read) # @UndefinedVariable
readDevices({"ip": "127.0.0.1", "port": "/dev/ttyUSB0", "slaveAddress": 1})
输出:
2019-02-03 00:39:00,623 MainThread INFO TestRTU:26 Connecting to Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0
2019-02-03 00:39:00,628 MainThread INFO TestRTU:73 Modbus RTU device connected at address logger127.0.0.1:/dev/ttyUSB0
2019-02-03 00:39:05,634 Thread-1 INFO TestRTU:80 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0...
2019-02-03 00:39:05,676 Thread-1 INFO TestRTU:89 Inside request fetched...
2019-02-03 00:39:05,677 Thread-1 INFO TestRTU:95 Sensor updated to value '26'.
serialport.SerialPort.init(...) constructor is responsible for initializing all the required parameters for serial communication with device. You just have to update your device setting here & run the code.
SerialModbusClient(modbusClientFactory, devicePort, reactor, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_EVEN, stopbits=STOPBITS_ONE, timeout=0.2, xonxoff=0, rtscts=0)
我正在尝试创建 Modbus serial/RTU 客户端,它将使用 Pymodbus 库从串口读取数据。
Python: 3.6
Pymodbus: 2.1.0
Platform: Linux/Windows
我的示例代码库如下:
def readDevices(modbusRTUDevice):
deviceIP = modbusRTUDevice["ip"]
devicePort = modbusRTUDevice["port"]
logger.info("Connecting to Modbus RTU device at address {0}".format(deviceIP + ":" + str(devicePort)))
modbusClientFactory = CustomModbusClientFactory()
modbusClientFactory.address = deviceIP
modbusClientFactory.modbusDevice = modbusRTUDevice
SerialModbusClient(modbusClientFactory, devicePort, reactor)
Thread(target=reactor.run, args=(False,)).start()
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
serialport.SerialPort.__init__(self, factory.buildProtocol(), *args, **kwargs)
class CustomModbusClientFactory(protocol.ClientFactory, ModbusClientMixin):
modbusDevice = {}
def buildProtocol(self, addr=None):
modbusClientProtocol = CustomModbusClientProtocol()
modbusClientProtocol.factory = self
modbusClientProtocol.modbusDevice = self.modbusDevice
return modbusClientProtocol
class CustomModbusClientProtocol(ModbusClientProtocol):
def connectionMade(self):
framer = ModbusRtuFramer.__init__(self, ClientDecoder(), client=None)
ModbusClientProtocol.__init__(self, framer, baudrate=9600, parity='E', bytesize=8, stopbits=1, timeout=0.2, retryOnEmpty=True, retries=3)
ModbusClientProtocol.connectionMade(self)
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
logger.info("Modbus RTU device connected at address {0}".format(deviceIP + ":" + str(devicePort)))
self.read()
def read(self):
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
slaveAddress = self.modbusDevice["slaveAddress"]
logger.info("Reading holding registers of Modbus RTU device at address {0}...".format(deviceIP + ":" + str(devicePort)))
deferred = self.read_holding_registers(self.startingAddress, self.registerCount, unit=slaveAddress)
deferred.addCallbacks(self.requestFetched, self.requestNotFetched)
def requestNotFetched(self, error):
logger.info("Error reading registers of Modbus RTU device : {0}".format(error))
sleep(0.5)
def requestFetched(self, response):
logger.info("Inside request fetched...")
#Do some other stuff here
reactor.callLater(0, self.read)
调试后,read()方法的self
表示CustomModbusClientProtocol: Null Transport
。在此之后,我按 F8
& 线程进入阻塞状态 & 延迟的回调从未调用过。
输出:
INFO:__main__: (2019-01-30 15:42:53; Test.py:200 Connecting to Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0)
INFO:__main__: (2019-01-30 15:42:53; Test.py:70 Modbus RTU device connected at address 127.0.0.1:/dev/ttyUSB0)
INFO:__main__: (2019-01-30 15:46:18; Test.py:87 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0...)
根据Serial + Async + RTU: Callback is never fired #160,deferred调用回调的问题已经修复。但是,就我而言,它仍然存在。
我在 Raspberry PI 和 Windows 中都尝试过此代码,在这两种情况下得到相同的响应。
不明白为什么会这样。我是否需要在我的代码中添加一些东西,或者我应该在通过串行端口连接到 Modbus 设备时做错什么?
如有任何帮助,我们将不胜感激。
编辑
更新代码和日志
问题是您在 CustomModbusClientProtocol
class 的 connectionMade
中直接调用 self.read()
,在反应器实际启动之前。您将不得不等待反应堆启动,然后 schedule read
稍后 reactor.callLater(<time-to-wait-in-seconds>, self.read)
。
修改后的 CustomModbusClientProtocol
代码看起来像这样
from twisted.internet import serialport, reactor
from twisted.internet import protocol
from pymodbus.factory import ClientDecoder
from pymodbus.client.async.twisted import ModbusClientProtocol
from pymodbus.transaction import ModbusRtuFramer
from threading import Thread
from time import sleep
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
def readDevices(modbusRTUDevice):
deviceIP = modbusRTUDevice["ip"]
devicePort = modbusRTUDevice["port"]
logger.info("Connecting to Modbus RTU device at address {0}".format(deviceIP + ":" + str(devicePort)))
modbusClientFactory = CustomModbusClientFactory()
modbusClientFactory.address = deviceIP
modbusClientFactory.modbusDevice = modbusRTUDevice
SerialModbusClient(modbusClientFactory, devicePort, reactor)
Thread(target=reactor.run, args=(False,)).start()
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
serialport.SerialPort.__init__(self, factory.buildProtocol(), *args, **kwargs)
class CustomModbusClientFactory(protocol.ClientFactory):
modbusDevice = {}
def buildProtocol(self, addr=None):
modbusClientProtocol = CustomModbusClientProtocol()
modbusClientProtocol.factory = self
modbusClientProtocol.modbusDevice = self.modbusDevice
return modbusClientProtocol
class CustomModbusClientProtocol(ModbusClientProtocol):
def connectionMade(self):
framer = ModbusRtuFramer(ClientDecoder(), client=None)
ModbusClientProtocol.__init__(self, framer, baudrate=9600, parity='E', bytesize=8, stopbits=1, timeout=0.2, retryOnEmpty=True, retries=3)
ModbusClientProtocol.connectionMade(self)
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
logger.info("Modbus RTU device connected at address logger{0}".format(deviceIP + ":" + str(devicePort)))
reactor.callLater(5, self.read)
def read(self):
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
slaveAddress = self.modbusDevice["slaveAddress"]
logger.info("Reading holding registers of Modbus RTU device at address {0}...".format(deviceIP + ":" + str(devicePort)))
deferred = self.read_holding_registers(0, 10, unit=slaveAddress)
deferred.addCallbacks(self.requestFetched, self.requestNotFetched)
def requestNotFetched(self, error):
logger.info("Error reading registers of Modbus RTU device : {0}".format(error))
sleep(0.5)
def requestFetched(self, response):
logger.info("Inside request fetched...")
#Do some other stuff here
reactor.callLater(0, self.read)
readDevices({"ip": "127.0.0.1", "port": "/dev/ptyp0", "slaveAddress": 1})
日志:
$ python scratch_118.py
2019-01-31 12:34:56,734 MainThread INFO scratch_118 :21 Connecting to Modbus RTU device at address 127.0.0.1:/dev/ptyp0
2019-01-31 12:34:56,735 MainThread DEBUG __init__ :80 Client connected to modbus server
2019-01-31 12:34:56,735 MainThread INFO scratch_118 :48 Modbus RTU device connected at address logger127.0.0.1:/dev/ptyp0
2019-01-31 12:35:01,737 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:01,738 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:01,738 Thread-1 DEBUG transaction :418 Adding transaction 1
2019-01-31 12:35:02,196 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:02,197 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:02,197 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:02,197 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:02,197 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:02,198 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:02,198 Thread-1 DEBUG transaction :418 Adding transaction 2
2019-01-31 12:35:03,202 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:03,202 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:03,202 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:03,202 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:03,203 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:03,203 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:03,203 Thread-1 DEBUG transaction :418 Adding transaction 3
2019-01-31 12:35:04,207 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:04,207 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:04,208 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:04,208 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:04,208 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:04,208 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:04,209 Thread-1 DEBUG transaction :418 Adding transaction 4
2019-01-31 12:35:05,213 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:05,213 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:05,214 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:05,214 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:05,214 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
2019-01-31 12:35:05,214 Thread-1 DEBUG __init__ :112 send: 0x1 0x3 0x0 0x0 0x0 0xa 0xc5 0xcd
2019-01-31 12:35:05,215 Thread-1 DEBUG transaction :418 Adding transaction 5
2019-01-31 12:35:06,218 Thread-1 DEBUG rtu_framer :175 Getting Frame - 0x3 0x14 0x0 0x2d 0x0 0x2c 0x0 0x2e 0x0 0x2d 0x0 0x2d 0x0 0x2e 0x0 0x29 0x0 0x2d 0x0 0x2d 0x0 0x2c
2019-01-31 12:35:06,218 Thread-1 DEBUG factory :246 Factory Response[ReadHoldingRegistersResponse: 3]
2019-01-31 12:35:06,219 Thread-1 DEBUG rtu_framer :110 Frame advanced, resetting header!!
2019-01-31 12:35:06,219 Thread-1 INFO scratch_118 :64 Inside request fetched...
2019-01-31 12:35:06,219 Thread-1 INFO scratch_118 :55 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ptyp0...
以下解决方案适用于我。我的设备配置如下:
Device Configuration:
Baudrate=9600, Parity='E', Bytesize=8 Timeout=0.2
Protocol: RTU
PLC is connected to RaspberryPI using serial-to-USB converter.
工作代码:
import logging
from threading import Thread
from time import sleep
from pymodbus.client.async.twisted import ModbusClientProtocol
from pymodbus.constants import Endian
from pymodbus.factory import ClientDecoder
from pymodbus.payload import BinaryPayloadDecoder
from pymodbus.transaction import ModbusRtuFramer
from serial import EIGHTBITS
from serial import PARITY_EVEN
from serial import STOPBITS_ONE
from twisted.internet import protocol
from twisted.internet import serialport, reactor
FORMAT = ('%(asctime)-15s %(threadName)-15s '
'%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def readDevices(modbusRTUDevice):
deviceIP = modbusRTUDevice["ip"]
devicePort = modbusRTUDevice["port"]
logger.info("Connecting to Modbus RTU device at address {0}".format(deviceIP + ":" + str(devicePort)))
modbusClientFactory = CustomModbusClientFactory()
modbusClientFactory.address = deviceIP
modbusClientFactory.modbusDevice = modbusRTUDevice
SerialModbusClient(modbusClientFactory, devicePort, reactor, baudrate=9600, bytesize=EIGHTBITS,
parity=PARITY_EVEN, stopbits=STOPBITS_ONE, timeout=0.2, xonxoff=0, rtscts=0)
Thread(target=reactor.run, args=(False,)).start() # @UndefinedVariable
class SerialModbusClient(serialport.SerialPort):
def __init__(self, factory, *args, **kwargs):
serialport.SerialPort.__init__(self, factory.buildProtocol(), *args, **kwargs)
class CustomModbusClientFactory(protocol.ClientFactory):
modbusDevice = {}
def buildProtocol(self, addr=None):
modbusClientProtocol = CustomModbusClientProtocol()
modbusClientProtocol.factory = self
modbusClientProtocol.modbusDevice = self.modbusDevice
return modbusClientProtocol
def clientConnectionLost(self, connector, reason):
modbusTcpDeviceIP = self.modbusDevice["ip"]
modbusTcpDevicePort = self.modbusDevice["port"]
logger.critical("Connection lost with device running on {0}:{1}.".format(modbusTcpDeviceIP, modbusTcpDevicePort))
logger.critical("Root Cause : {0}".format(reason))
connector.connect()
def clientConnectionFailed(self, connector, reason):
modbusTcpDeviceIP = self.modbusDevice["ip"]
modbusTcpDevicePort = self.modbusDevice["port"]
logger.critical("Connection failed with device running on {0}:{1}.".format(modbusTcpDeviceIP, modbusTcpDevicePort))
logger.critical("Root Cause : {0}".format(reason))
connector.connect()
class CustomModbusClientProtocol(ModbusClientProtocol):
def connectionMade(self):
framer = ModbusRtuFramer(ClientDecoder(), client=None)
ModbusClientProtocol.__init__(self, framer)
ModbusClientProtocol.connectionMade(self)
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
logger.info("Modbus RTU device connected at address logger{0}".format(deviceIP + ":" + str(devicePort)))
reactor.callLater(5, self.read) # @UndefinedVariable
def read(self):
deviceIP = self.modbusDevice["ip"]
devicePort = self.modbusDevice["port"]
slaveAddress = self.modbusDevice["slaveAddress"]
logger.info("Reading holding registers of Modbus RTU device at address {0}...".format(deviceIP + ":" + str(devicePort)))
deferred = self.read_holding_registers(0, 1, unit=slaveAddress)
deferred.addCallbacks(self.requestFetched, self.requestNotFetched)
def requestNotFetched(self, error):
logger.info("Error reading registers of Modbus RTU device : {0}".format(error))
sleep(0.5)
def requestFetched(self, response):
logger.info("Inside request fetched...")
decoder = BinaryPayloadDecoder.fromRegisters(response.registers, byteorder=Endian.Big, wordorder=Endian.Big)
skipBytesCount = 0
decoder.skip_bytes(skipBytesCount)
registerValue = decoder.decode_16bit_int()
skipBytesCount += 2
logger.info("Sensor updated to value '{0}'.".format(registerValue))
reactor.callLater(5, self.read) # @UndefinedVariable
readDevices({"ip": "127.0.0.1", "port": "/dev/ttyUSB0", "slaveAddress": 1})
输出:
2019-02-03 00:39:00,623 MainThread INFO TestRTU:26 Connecting to Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0
2019-02-03 00:39:00,628 MainThread INFO TestRTU:73 Modbus RTU device connected at address logger127.0.0.1:/dev/ttyUSB0
2019-02-03 00:39:05,634 Thread-1 INFO TestRTU:80 Reading holding registers of Modbus RTU device at address 127.0.0.1:/dev/ttyUSB0...
2019-02-03 00:39:05,676 Thread-1 INFO TestRTU:89 Inside request fetched...
2019-02-03 00:39:05,677 Thread-1 INFO TestRTU:95 Sensor updated to value '26'.
serialport.SerialPort.init(...) constructor is responsible for initializing all the required parameters for serial communication with device. You just have to update your device setting here & run the code.
SerialModbusClient(modbusClientFactory, devicePort, reactor, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_EVEN, stopbits=STOPBITS_ONE, timeout=0.2, xonxoff=0, rtscts=0)