如何停止一台pymodbus Async Modbus TcpServer?

2022-04-18 00:00:00 python python-3.x twisted reactor pymodbus

问题描述

我要停止pymodbus异步ModbusTcpServer,然后启动新服务器。因此,我尝试使用以下简化的代码片段,但收到错误:

from pymodbus.server.async import StartTcpServer, StopServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from time import sleep

import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def main(name='Pymodbus'):
    store = ModbusSlaveContext(hr=ModbusSequentialDataBlock(0, [17]*100))
    context = ModbusServerContext(slaves=store, single=True)

    identity = ModbusDeviceIdentification()
    identity.VendorName = name
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = '1.0'

    StartTcpServer(
        context,
        identity=identity,
        address=("localhost", 5020),
        defer_reactor_run=True
    )
    sleep(3)
    name += 'stuff'

    StopServer()
    sleep(3)
    main(name)  # Recursive

main()

输出:

INFO:pymodbus.server.async:Starting Modbus TCP Server on localhost:5020
DEBUG:pymodbus.server.async:Running in Main thread
Traceback (most recent call last):
  File "stack.py", line 42, in <module>
    main()
  File "stack.py", line 38, in main
    StopServer()
  File "/usr/local/lib/python3.6/dist-packages/pymodbus/server/async.py", line 328, in StopServer
    reactor.stop()
  File "/usr/local/lib/python3.6/dist-packages/twisted/internet/base.py", line 630, in stop
    "Can't stop reactor that isn't running.")
twisted.internet.error.ReactorNotRunning: Can't stop reactor that isn't running.

[更新]

此外,我还尝试使用另一个线程在ModbusTcpServer中使用defer_reactor_run=False参数(作为默认)停止ModbusTcpServer,但尽管如此,行为仍然是相同的:

import threading
import logging
from pymodbus.server.async import StartTcpServer, StopServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def stop():
    StopServer()

def main(name='Pymodbus'):
    store = ModbusSlaveContext(hr=ModbusSequentialDataBlock(0, [17]*100))
    context = ModbusServerContext(slaves=store, single=True)

    identity = ModbusDeviceIdentification()
    identity.VendorName = name
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = '1.0'

    t = threading.Timer(5, stop)
    t.daemon = True
    t.start()

    StartTcpServer(
        context,
        identity=identity,
        address=("localhost", 5020),
        defer_reactor_run=False
    )
    name += 'stuff'

    main(name)  # Recursive

main()

输出:

INFO:pymodbus.server.async:Starting Modbus TCP Server on localhost:5020
DEBUG:pymodbus.server.async:Running in Main thread
DEBUG:pymodbus.server.async:Running in spawned thread
DEBUG:pymodbus.server.async:Stopping Server from another thread
INFO:pymodbus.server.async:Starting Modbus TCP Server on localhost:5020
DEBUG:pymodbus.server.async:Running in Main thread
Traceback (most recent call last):
  File "stack.py", line 41, in <module>
    main()
  File "stack.py", line 39, in main
    main()  # Recursive
  File "stack.py", line 35, in main
    defer_reactor_run=False
  File "/usr/local/lib/python3.6/dist-packages/pymodbus/server/async.py", line 257, in StartTcpServer
    reactor.run(installSignalHandlers=_is_main_thread())
  File "/usr/local/lib/python3.6/dist-packages/twisted/internet/base.py", line 1260, in run
    self.startRunning(installSignalHandlers=installSignalHandlers)
  File "/usr/local/lib/python3.6/dist-packages/twisted/internet/base.py", line 1240, in startRunning
    ReactorBase.startRunning(self)
  File "/usr/local/lib/python3.6/dist-packages/twisted/internet/base.py", line 748, in startRunning
    raise error.ReactorNotRestartable()
twisted.internet.error.ReactorNotRestartable

解决方案

我找到了另一个解决方案,可以通过另一个Python代码停止和启动异步Modbus TcpServer,因为很明显,我们无法重新启动reactor事件循环。


这是runner.py代码:

import subprocess

python_version = '3'
path_to_run = './'
py_name = 'async_server.py'

def run():
    args = [f"python{python_version}", f"{path_to_run}{py_name}"]
    sub_process = subprocess.Popen(args, stdout=subprocess.PIPE)
    output, error_ = sub_process.communicate()

    if not error_:
        print(output)
    else:
        print(error_)

    run()  # Recursively.

if __name__ == '__main__':
    run()

这是async_server.py代码片段:

from pymodbus.server.async import StartTcpServer, StopServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

import threading
import sys
import logging

FORMAT = ('%(asctime)-15s %(threadName)-15s'
          ' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)

def stop():
    print('Process will be down.')
    StopServer()  # Stop server.
    sys.exit(0)  # Kill the server code.

def run_async_server():
    store = ModbusSlaveContext(hr=ModbusSequentialDataBlock(0, [17] * 100))
    slaves = {
        0x01: store,
        0x02: store,
        0x03: store,
    }
    context = ModbusServerContext(slaves=slaves, single=False)

    identity = ModbusDeviceIdentification()
    identity.VendorName = 'Pymodbus'
    identity.ProductCode = 'PM'
    identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
    identity.ProductName = 'Pymodbus Server'
    identity.ModelName = 'Pymodbus Server'
    identity.MajorMinorRevision = '1.5'

    from twisted.internet import reactor
    StartTcpServer(context, identity=identity, address=("localhost", 5020),
                   defer_reactor_run=True)
    print('Start an async server.')
    t = threading.Timer(5, stop)
    t.daemon = True
    t.start()
    reactor.run()
    print('Server was stopped.')

if __name__ == "__main__":
    run_async_server()

输出:

$ python3 runner.py 

2019-01-24 12:45:05,126 MainThread      INFO     async          :254      Starting Modbus TCP Server on localhost:5020
2019-01-24 12:45:10,129 Thread-1        DEBUG    async          :222      Running in spawned thread
2019-01-24 12:45:10,129 Thread-1        DEBUG    async          :332      Stopping Server from another thread
b'Start an async server.
Process will be down.
Server was stopped.
'
2019-01-24 12:45:13,389 MainThread      INFO     async          :254      Starting Modbus TCP Server on localhost:5020
2019-01-24 12:45:18,392 Thread-1        DEBUG    async          :222      Running in spawned thread
2019-01-24 12:45:18,392 Thread-1        DEBUG    async          :332      Stopping Server from another thread
b'Start an async server.
Process will be down.
Server was stopped.
'
2019-01-24 12:45:21,653 MainThread      INFO     async          :254      Starting Modbus TCP Server on localhost:5020
2019-01-24 12:45:26,656 Thread-1        DEBUG    async          :222      Running in spawned thread
2019-01-24 12:45:26,657 Thread-1        DEBUG    async          :332      Stopping Server from another thread
b'Start an async server.
Process will be down.
Server was stopped.
'
.
.
.

相关文章