1

I have a running ModbusRTU server, following this example. I know how to update the context, but I can't update the context of a running server.

When I update the context in the run_updating_server() function (before StartSerialServer()) then it works fine. But when I try to update the running context by calling updating_writer(context) then it doesn't update.

Also calling my own function 'getUpdatedContext()' from withing updating_writer() does not work:

 def updating_writer(a):
    a =(getContext(),)
    """ 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 = 0x41
    address = 0x10
    values = context[slave_id].getValues(register, address, count=5)
    values = [v + 1 for v in values]
    log.debug("new values: " + str(values))
    context[slave_id].setValues(register, address, values)

getContext():

def getContext():
    store = ModbusSlaveContext(
      di=ModbusSequentialDataBlock(0, [17]*100),
      co=ModbusSequentialDataBlock(0, [17]*100),
      hr=ModbusSequentialDataBlock(0, [17]*100),
      ir=ModbusSequentialDataBlock(0, [17]*100))

    store.setValues(5, 1, [0])
    context = ModbusServerContext(slaves=store, single=True)
    return context

my full code:

#!/usr/bin/env python

import os
import sys
"""
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 StartSerialServer
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 = 0x41
    address = 0x10
    values = context[slave_id].getValues(register, address, count=5)
    values = [v + 1 for v in values]
    log.debug("new values: " + str(values))
    context[slave_id].setValues(register, address, values)

def run_updating_server():
    # ----------------------------------------------------------------------- # 
    # 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)

    # ----------------------------------------------------------------------- # 
    # 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 = '2.2.0'

    # ----------------------------------------------------------------------- # 
    # run the server you want
    # ----------------------------------------------------------------------- # 
    time = 5
    loop = LoopingCall(f=updating_writer, a=(context,))
    loop.start(time, now=False)  # initially delay by time

    log.debug("::: Starting Modbus RTU server :::")

    # RTU:
    StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
                      port='/dev/ttyUSB0', 
                      timeout=2, 
                      baudrate=115200, 
                      parity='E',
                      bytesize=8,
                      stopbits=1)

def main():
    pid = str(os.getpid())
    filename = 'modbusUpdatingServer.pid'
    pidfile = str(os.getcwd()) + '/' + filename 
    if os.path.isfile(pidfile):
        print (filename + " already exists \n exiting")
        sys.exit()
    open(pidfile, 'w').write(pid)
    try:
        # run server
        run_updating_server()
    finally:
        os.unlink(pidfile)

if __name__ == "__main__":
    main()

I want to update the context from another python script, i tried:

  • calling updating_writer(a) from that 'other python script', where a = updatedContext.

  • calling getUpdatedContext(), from withing updating_writer(a), which is a function in 'that other python script' which returns the updatedContext

  • make the context global, add a function updateContext(a), call that function from another python script.

It all resulted in compiling code, but the context of the running server didn't get updated.

here the code from the 'other python script':

from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext
from threading import Thread
import os
import subprocess
import sys

from modbusUpdatingServer import updating_writer, run_updating_server

def writeUpdatedContext():
    store = ModbusSlaveContext(
    di=ModbusSequentialDataBlock(0, [17]*100),
    co=ModbusSequentialDataBlock(0, [17]*100),
    hr=ModbusSequentialDataBlock(0, [17]*100),
    ir=ModbusSequentialDataBlock(0, [17]*100))

    store.setValues(5, 1, [0])

    context = ModbusServerContext(slaves=store, single=True)
    updating_writer(a=(context,)   

def main():
    choice = input("press x if you want to update the context")

    if choice == 'x' or choice == 'X':
        writeUpdatedContext()


if __name__ == "__main__":
    main()

How am I supposed to interact with updating_writer? What I want to achieve is my modbusRTU server running, and updating the context from another threat, so my modbus client(master) can read the registers I've filled.

user_cr
  • 110
  • 10
  • what is the content of `a`? – Prudhvi Oct 19 '19 at 08:45
  • Could you push on more complete of your code? – Benyamin Jafari Oct 19 '19 at 13:03
  • Your code works fine removing your own function call `a =(getContext(),)` and its corresponding import. I've tested it on Linux, Python 2.7.16 and 3.7.3 and pyModbus 2.2.0. Do you at least see the debug lines `DEBUG:root:new values: [xx, xx, xx, xx, xx]` with the `xx` changing? – Marcos G. Oct 21 '19 at 11:00
  • Sorry I wasnt clear: this code works so, yes I see the debug lines. but I want to call updating_writer(a) from outside the loop (from another python script). but when I make that call the context (a) doesn't get updated. – user_cr Oct 21 '19 at 14:04
  • I see... I did not understand that from your question. In my opinion, the idea of this example is to update your server values from `updating_writer()`. But you say you want to call `updating_writer()` from a different script? How are you doing that? Can you add your second script? – Marcos G. Oct 22 '19 at 07:27
  • I updated my question. Basically I just call updating_writer(a) where a is my new context – user_cr Oct 22 '19 at 14:22
  • 1
    Now I see what you mean, nice update. The way you are doing it will not work, you have **two** different Modbus contexts and they lived in different places in memory. I think you misunderstood the way `loopingCall` works. You don't need to call `updating_writer` manually, it is already being called periodically by `loopingCall`. What I don't understand is why you want to update your Modbus server from a different script. If that's a hard requirement you might want to try replacing `twisted` with `multiprocessing`, see for instance [here](https://stackoverflow.com/a/43861226/11476836) – Marcos G. Oct 23 '19 at 06:27
  • Thank you @MarcosG. I understand my problem now. I want to update the the context from another script because I want 1 script that is the modbus server, and another script with ui. Is there a major disadvantage if I used twisted, and then pass my context as reference? It's not clear to me why multiprocessing is the way to go in my case, can you elaborate on that? – user_cr Oct 23 '19 at 14:41
  • 1
    You're welcome. I'm not saying you should drop `twisted`. Are you using an object-oriented approach? Have you seen [this example](https://github.com/riptideio/pymodbus/tree/master/examples/gui). You can also take a look at [pyModSlave](https://sourceforge.net/projects/pymodslave/), it does not use pymodbus but it can give you ideas to organize your code, maybe. I thought you were interested in a quick and dirty solution; if you need a GUI I would rather take a look at those examples and maybe other more general (I don't think your problem is with Modbus at all). – Marcos G. Oct 23 '19 at 17:45
  • @user_cr Hi, I would to make the same stuff that you did asked in this question. Will you have an answer (final code working?), I have something on my side, but I'm in the same place where I can't live update the new context. Feel free to answer here, maybe we can discuss somewhere else about it. Regards. – PierreP Sep 19 '22 at 15:24
  • @MarcosG. you can check my question here : https://stackoverflow.com/questions/73702565/update-a-pymodbus-server-context-while-the-server-is-running – PierreP Sep 19 '22 at 17:29

0 Answers0