pymodbus icon indicating copy to clipboard operation
pymodbus copied to clipboard

Cannot update context from inside a multiprocessing.Process

Open Luctins opened this issue 4 years ago • 1 comments

Versions

  • Python: 3.8.6
  • OS: Pop!_OS 20.10
  • Pymodbus: 2.5.1
  • Modbus Hardware (if used): -

Pymodbus Specific

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: image image log: image

Luctins avatar Apr 23 '21 21:04 Luctins

note: It seems to be related to some sort of memory isolation that multiprocessing.Process does

Luctins avatar May 05 '21 20:05 Luctins

Please have a look at the server_updating.py example in version 3.0.2, that works.

janiversen avatar Nov 12 '22 12:11 janiversen