pythonpython-asynciopymodbus

Asyncio - endless running tasks - modifying pymodbus example


I am working with asynchronous PyModbus server with the refreshing task, everything is asyncio-based and based on pymodbus example code, which can be found here: https://pymodbus.readthedocs.io/en/latest/source/examples.html#updating-server-example

I am not very experienced with asyncio (just a few tutorial and simple experiments which were working correctly but this is my first attempt in creating anything more complicated) and I think I'm missing something.

In the example there is the asyncio.run(...) called in the __main__ part. However, I want to modify this code and therefore I would like to have the server started outside of __main__, something like this:

async def myFunction(args):
    # do some other stuff
    asyncio.create_task(run_updating_server(run_args))

if __name__ == "__main__":
    cmd_args = get_commandline(
        server=True,
        description="Run asynchronous server.",
    )
    run_args = setup_updating_server(cmd_args)
    asyncio.run(myFunction(run_args), debug=True)

However, this doesn't create a nice, endless running task as in the example, everything is performed just once and that's all, the program finishes.
I don't understand what is the difference and why was the server running endlessly in the example but runs only once in my modification - is there something in create_task() vs run() functionalities that I'm missing?

I have found this topic and tried implementing it with explicit call of the event loop like this:

async def new_main(args):
    asyncio.Task(run_updating_server(args))

if __name__ == "__main__":
    cmd_args = get_commandline(
        server=True,
        description="Run asynchronous server.",
    )
    run_args = setup_updating_server(cmd_args)
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    try:
        loop.run_until_complete(new_main(run_args))
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())
        loop.close()

However, in such case I just got Task was destroyed but it is pending! errors...

My second question is: how should I properly implement task to have it endlessly running - the server, the updating function and some other which I want to implement (not related to the modbus server, just running along it and doing their things)? I want to add some tasks to the event loop and have them run endlessly, let's say - one should be executed every 1 second, another one should be triggered by some lock, etc. I thought that the updating task() in the example was changing the values on the server once every second but after investigating I see that it doesn't - it is just executed once. How can it be modified to behave as mentioned - increment the server values every second?

I guess that's something obvious but lack of experience with asyncio and two days of brainstorming over the whole application made me too dumb to understand what am I missing... I hope you will be albo to guide me to right direction - TIA!


Solution

  • What you are missing is the await keyword. An asyncio task without an await expression is almost always an error.

    async def myFunction(args):
        # do some other stuff
        t = asyncio.create_task(run_updating_server(run_args))
        await t
    

    There is a huge difference between this function and the one in your code. Both functions create a task. Your code is then finished. It immediately exits and your program ends. The function given here awaits completion of the newly created task. It doesn't progress past the await expression until the task run_updating_server is complete. The program will not exit until myFunction ends, so the program keeps running until the task finishes. If the task is an infinite loop, the program will run forever.

    You say you want to do other things in addition to running this server. Probably each of those other things should be another task, created before the server. You don't have to await on these tasks unless you want one (or more) of them to finish before the server starts. I'm not sure of what else you want to do, but the point of my answer is that your main task has to await something to keep the program from exiting immediately.