Как остановить асинхронный ModbusTcpServer на 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=Flase аргумент (по умолчанию) в 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

2 ответа

Я нашел альтернативное решение, чтобы остановить и запустить Async ModbusTcpServer с помощью другого кода Python, потому что, очевидно, мы не можем перезапустить 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'
.
.
.

Вы можете использовать пример из папок библиотеки: 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 

Привет из Колумбии

В первом примере сервер 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()
Другие вопросы по тегам