【问题标题】:How to stop a pymodbus async ModbusTcpServer?如何停止 pymodbus 异步 ModbusTcpServer?
【发布时间】:2019-06-15 22:18:00
【问题描述】:

我想停止 pymodbus async ModbusTcpServer 然后启动一个新服务器。因此,我尝试使用以下简化代码 sn-p,但出现错误:

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.

[更新]

另外,我尝试在另一个线程中使用defer_reactor_run=False 参数(默认)在ModbusTcpServer 中停止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

【问题讨论】:

  • 您找到解决方法了吗?
  • @kaptan 实际上,我并没有寻求找到解决该问题的原始方法。但是,我记得我用另一种解决方案绕过了它,并将其作为答案发送。

标签: python python-3.x twisted reactor pymodbus


【解决方案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代码sn-p:

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'
.
.
.

【讨论】:

    【解决方案2】:

    在第一个示例中,TCP 服务器没有运行,因为您将 param defer_reactor_run 设置为 True,但您没有明确运行它。因此,当您尝试停止它时,它会因为尚未启动而失败。

    在第二个示例中,您运行它,但下次使用 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()
    

    【讨论】:

    • 在第二个例子中,我使用 pymodbus 中的内置 StopServer() 在 5 秒后通过另一个线程停止反应器
    • 也许可以,但同时您尝试在计时器唤醒之前重新启动已经启动的服务器。
    • 试过这个sn-p代码了吗?此代码卡在StartTcpServe() 中,因为defer_reactor_run 为假。但是,我想停止并重新启动服务器
    • 它没有卡住,它运行一个服务器....如果你想停止它,杀死任务。我更新了如何在延迟模式下运行它
    • 我的问题很明显,这不是我的答案,感谢您的回复。
    【解决方案3】:

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

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

    我分享一个适合我的例子:

    如果您还有任何问题,请发邮件给我:Danielsalazr@hotmail.com

    #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 
    

    来自哥伦比亚的问候

    【讨论】:

    • 您好 Daniel,由于该 pymodbus 库已更新,pymodbus.server.async 已在较新版本中转换为 pymodbus.server.asynchronous,我知道(我的问题被问了一年以前使用 pymodbus 的早期版本)。然而,这不是我的问题。正如我在问题中提到的,问题是StopServer 而不是StartTcpServer
    猜你喜欢
    • 2021-12-23
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-01
    • 2013-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多