+-
如何停止pymodbus异步ModbusTcpServer?

我想停止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
1
投票

[我发现了另一种解决方案,可以通过另一个Python代码停止和启动Async ModbusTcpServer,因为显然,我们无法重新启动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.\nProcess will be down.\nServer was stopped.\n'
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.\nProcess will be down.\nServer was stopped.\n'
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.\nProcess will be down.\nServer was stopped.\n'
.
.
.
0
投票

在第一个示例中,由于将param defer_reactor_run设置为True,所以TCP服务器未运行,但是尚未显式运行它。因此,当您尝试停止它时,它由于尚未启动而失败。

在第二个示例中,您运行它,但是下次使用main(name)调用递归调用它!因此,由于它已经启动,因此失败并显示错误!下一个代码应该工作:

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=False
    )

    sleep(3)  # for the fun ?
    # and do your stuff 

    StopServer()

main()

如果要推迟运行,必须致电:

from twisted.internet import reactor
StartTcpServer(context, identity=identity, address=("localhost", 5020),
            defer_reactor_run=True)
reactor.run()
0
投票

您可以使用库文件夹中包含的示例:asynchronous-server.py

在python3.7中async被作为关键字,但是在pymodbus 2.3.0中pymodbus.server.async更改为pymodbus.server.asynchronous

我分享了一个对我有用的示例:

如果还有其他问题,请给我发送电子邮件:[email protected]

#Este codigo funciona ingresando a la direccion esclavo 16 en decimal
#la Ip debe ser la misma que esta asignada en el dispositivo

#los datos se actualizan a patir del registro #15
#Aparentemente la direcion del esclavo no tiene importancia, debido a que
#al realizar la conexion desde el software QModMaster, el numero ingresado
#em el campo SlaveAddr, no afecta la posibilidad de conexion

'''
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------

This is an example of having a background thread updating the
context while the server is operating. This can also be done with
a python thread::

    from threading import Thread

    thread = Thread(target=updating_writer, args=(context,))
    thread.start()
'''
#---------------------------------------------------------------------------# 
# import the modbus libraries we need
#---------------------------------------------------------------------------# 
from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer

#---------------------------------------------------------------------------# 
# import the twisted libraries we need
#---------------------------------------------------------------------------# 
from twisted.internet.task import LoopingCall

#---------------------------------------------------------------------------# 
# configure the service logging
#---------------------------------------------------------------------------# 
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

#---------------------------------------------------------------------------# 
# define your callback process
#---------------------------------------------------------------------------# 
def updating_writer(a):
    ''' A worker process that runs every so often and
    updates live values of the context. It should be noted
    that there is a race condition for the update.

    :param arguments: The input arguments to the call
    '''
    log.debug("updating the context")
    context  = a[0]
    register = 3
    slave_id = 0x01
    address  = 0x10
    values   = context[slave_id].getValues(register, address, count=5)
    #el valor count = 5 modifica los datos de los siguientes 5 registros
    #continuos al registro especificado en la variable address = 0x10, 16 en decimal
    values   = [v + 1 for v in values]
    log.debug("new values: " + str(values))
    context[slave_id].setValues(register, address, values)

#---------------------------------------------------------------------------# 
# initialize your data store
#---------------------------------------------------------------------------# 
store = ModbusSlaveContext(
    di = ModbusSequentialDataBlock(0, [17]*100),
    co = ModbusSequentialDataBlock(0, [17]*100),
    hr = ModbusSequentialDataBlock(0, [17]*100),
    ir = ModbusSequentialDataBlock(0, [17]*100))
context = ModbusServerContext(slaves=store, single=True)

#ModbusSlaveContext.setValues("lol",hr,3,25)
#context = ModbusServerContext(slaves=store, single=True)
#---------------------------------------------------------------------------# 
# initialize the server information
#---------------------------------------------------------------------------# 
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.0'

#---------------------------------------------------------------------------# 
# run the server you want
#---------------------------------------------------------------------------# 
time = 1 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartTcpServer(context, identity= identity, address=("192.168.0.40", 502)) #here you need 
#change the ip for the ip address of your device 

来自哥伦比亚的问候