pymodbus
pymodbus copied to clipboard
Cannot update context from inside a multiprocessing.Process
Versions
- Python: 3.8.6
- OS: Pop!_OS 20.10
- Pymodbus: 2.5.1
- Modbus Hardware (if used): -
Pymodbus Specific
- Server: tcp - async
- Client: nodered-contrib-modbus - tcp - sync/async (not sure)
Description
Trying to update the server context from inside a thread has no effect. I expected that I could update the context from a separate process.
Using a LoopingCall instead works, but is unfit for my application. (a process that receives messages from a queue and updates the context accordingly)
Code and Logs
Minimal example
#!/usr/bin/python3
#-------------------------------------------------------------------------------
# Library Imports
from pymodbus.version import version
from pymodbus.server.asynchronous import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSparseDataBlock, ModbusSequentialDataBlock
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
import logging
from multiprocessing import Process
from time import sleep, time
import sys
import os
#from twisted.internet.task import LoopingCall
print (sys.argv)
sys.argv = (sys.argv + ["localhost", "5020", "0"] ) if len(sys.argv) <= 1 else sys.argv
#------------------------------------------------------------------------------
# Global
global g_modbus_s_context
global USE_GLOBAL
try:
USE_GLOBAL = sys.argv[3]
except IndexError as e:
USE_GLOBAL = 0
def test_process(context):
t = 0
while True:
values = [t + v for v in range(0, 5)]
if USE_GLOBAL == 1:
g_modbus_s_context[0].setValues(3, 0, values)
print("global registers: {} values: {}"
.format(g_modbus_s_context[0].getValues(3, 0, len(values)),
values))
else:
context[0].setValues(3, 0, values)
print("reg: {} values: {}"
.format(context[0].getValues(3, 0, len(values)),
values))
t+=1
sleep(0.5)
#------------------------------------------------------------------------------
# logging library
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)
#--------------------------------------------------
# Modbus server setup
initval = 1
modbus_store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0,[initval ]*100),
co=ModbusSequentialDataBlock(0,[initval+1]*100),
hr=ModbusSequentialDataBlock(0,[initval+2]*100),
ir=ModbusSequentialDataBlock(0,[initval+3]*100),
)
g_modbus_s_context = ModbusServerContext(slaves=modbus_store, single=True)
modbus_identity = ModbusDeviceIdentification()
modbus_identity.VendorName = 'pymodbus'
modbus_identity.ProductCode = 'PM'
modbus_identity.VendorUrl = 'http://github.com/riptideio/pymodbus/'
modbus_identity.ProductName = 'Test'
modbus_identity.ModelName = 'Test 1.0'
modbus_identity.MajorMinorRevision = version.short()
#------------------------------------------------------------------------------
# Test Process
_proc = Process(target=test_process, name="test", args=(g_modbus_s_context,))
# start processes
_proc.start()
StartTcpServer(g_modbus_s_context, identity=modbus_identity,
address=(sys.argv[1], int(sys.argv[2])))
Server Log:
['test_process_update.py']
INFO:pymodbus.server.asynchronous:Starting Modbus TCP Server on localhost:5020
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [0, 1, 2, 3, 4] values: [0, 1, 2, 3, 4]
DEBUG:pymodbus.server.asynchronous:Running in Main thread
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [1, 2, 3, 4, 5] values: [1, 2, 3, 4, 5]
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [2, 3, 4, 5, 6] values: [2, 3, 4, 5, 6]
DEBUG:pymodbus.server.asynchronous:Client Connected [IPv4Address(type='TCP', host='127.0.0.1', port=5020)]
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [3, 4, 5, 6, 7] values: [3, 4, 5, 6, 7]
DEBUG:pymodbus.server.asynchronous:Data Received: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0 0x5
DEBUG:pymodbus.framer.socket_framer:Processing: 0x0 0x1 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0 0x5
DEBUG:pymodbus.factory:Factory Request[ReadHoldingRegistersRequest: 3]
DEBUG:pymodbus.datastore.context:validate: fc-[3] address-1: count-5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
DEBUG:pymodbus.server.asynchronous:send: b'00010000000d01030a00030003000300030003'
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [4, 5, 6, 7, 8] values: [4, 5, 6, 7, 8]
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [5, 6, 7, 8, 9] values: [5, 6, 7, 8, 9]
DEBUG:pymodbus.server.asynchronous:Data Received: 0x0 0x2 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0 0x5
DEBUG:pymodbus.framer.socket_framer:Processing: 0x0 0x2 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0 0x5
DEBUG:pymodbus.factory:Factory Request[ReadHoldingRegistersRequest: 3]
DEBUG:pymodbus.datastore.context:validate: fc-[3] address-1: count-5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
DEBUG:pymodbus.server.asynchronous:send: b'00020000000d01030a00030003000300030003'
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [6, 7, 8, 9, 10] values: [6, 7, 8, 9, 10]
DEBUG:pymodbus.server.asynchronous:Data Received: 0x0 0x3 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0 0x5
DEBUG:pymodbus.framer.socket_framer:Processing: 0x0 0x3 0x0 0x0 0x0 0x6 0x1 0x3 0x0 0x0 0x0 0x5
DEBUG:pymodbus.factory:Factory Request[ReadHoldingRegistersRequest: 3]
DEBUG:pymodbus.datastore.context:validate: fc-[3] address-1: count-5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
DEBUG:pymodbus.server.asynchronous:send: b'00030000000d01030a00030003000300030003'
DEBUG:pymodbus.datastore.context:setValues[3] 1:5
DEBUG:pymodbus.datastore.context:getValues fc-[3] address-1: count-5
reg: [7, 8, 9, 10, 11] values: [7, 8, 9, 10, 11]
Client config:
Read command:
log:

note: It seems to be related to some sort of memory isolation that multiprocessing.Process does
Please have a look at the server_updating.py example in version 3.0.2, that works.