Repository: pymodbus-dev/pymodbus

Issue:
pymodbus.server does not listen on modbus port
<!--
Before opening a new issue, make sure you do the following:

- Check that your issue isn't already filed: https://github.com/pymodbus-dev/pymodbus/issues
- Check the discussions forum https://github.com/pymodbus-dev/pymodbus/discussions
- Prepare a short, runnable example that reproduce the issue with the latest development version of Pymodbus
-->

### Versions

- Python: 3.11.3
- OS: Fedora 37
- Pymodbus: 3.3.1

### Pymodbus Specific

- Server: tcp

### Description

- start pymodbus server:
```
pymodbus.server --verbose run -u 1


__________                          .______.                    _________
\______   \___.__. _____   ____   __| _/\_ |__  __ __  ______  /   _____/ ______________  __ ___________
 |     ___<   |  |/     \ /  _ \ / __ |  | __ \|  |  \/  ___/  \_____  \_/ __ \_  __ \  \/ // __ \_  __ \\
 |    |    \___  |  Y Y  (  <_> ) /_/ |  | \_\ \  |  /\___ \   /        \  ___/|  | \/\   /\  ___/|  | \/
 |____|    / ____|__|_|  /\____/\____ |  |___  /____//____  > /_______  /\___  >__|    \_/  \___  >__|
           \/          \/            \/      \/           \/          \/     \/                 \/


SERVER >


```
- try to connect to port 5020 or check which process is listening on port 5020
- current result
  - server does not listen on port 5020



Discussion:
How about you read the documentation or at least start it with --help, then you will there is a --port argument (as well as many others like e.g. --framer)
Apart from that what does -u mean ? I am a bit surprised because I thought that was unsupported.
> How about you read the documentation or at least start it with --help, then you will there is a --port argument (as well as many others like e.g. --framer)

I used exactly that command line before when working with pymodbus 3.2.2 and it worked fine:
```
pymodbus.server --verbose run -u 1
2023-06-12 21:48:45,504 INFO  logging:96 Modbus server started
2023-06-12 21:48:45,508 INFO  logging:96 Server(TCP) listening.

__________                          .______.                    _________
\______   \___.__. _____   ____   __| _/\_ |__  __ __  ______  /   _____/ ______________  __ ___________
 |     ___<   |  |/     \ /  _ \ / __ |  | __ \|  |  \/  ___/  \_____  \_/ __ \_  __ \  \/ // __ \_  __ \\
 |    |    \___  |  Y Y  (  <_> ) /_/ |  | \_\ \  |  /\___ \   /        \  ___/|  | \/\   /\  ___/|  | \/
 |____|    / ____|__|_|  /\____/\____ |  |___  /____//____  > /_______  /\___  >__|    \_/  \___  >__|
           \/          \/            \/      \/           \/          \/     \/                 \/

SERVER >
```

In both version, the usage output indicates, that modbus tcp and port 5020 are the default and so I thought I can omit them:

```
pymodbus.server run --help
Usage: pymodbus.server run [OPTIONS]

  Run Reactive Modbus server.

  Exposing REST endpoint for response manipulation.

Options:
  -s, --modbus-server TEXT   Modbus Server  [default: tcp]
  -f, --framer TEXT          Modbus framer to use  [default: socket]
  -p, --modbus-port INTEGER  Modbus port  [default: 5020]
  -u, --slave-id INTEGER     Supported Modbus slave id's  [default: 1]
  --modbus-config-path PATH  Path to additional modbus server config
  -r, --random INTEGER       Randomize every `r` reads. 0=never,
                             1=always,2=every-second-read, and so on.
                             Applicable IR and DI.  [default: 0]
  -c, --change-rate INTEGER  Rate in % registers to change. 0=none, 100=all,
                             12=12% of registers, and so on. Applicable IR and
                             DI.  [default: 0]
  --help                     Show this message and exit.
```

I double-check whether it works by supplying these arguments, but unfortunately it does not help. I tried `pymodbus.server run -s tcp -p 5020`.

> Apart from that what does -u mean ? I am a bit surprised because I thought that was unsupported.

This command line was suggested by @dhoomakethu in a previous issue report: https://github.com/pymodbus-dev/pymodbus/issues/1454#issuecomment-1487970034 . As far as I understand, "-u" specifies the unit IDs the pymodbus server is reacting to.

The only way I got pymodbus.server to work was to supply "-u 1". In my last comment of issue #1454 I suggested to make this the default.
A lot of thing have changed. @dhoomakethu it's your code, between 3.2 and 3.3 I do not think there was changes to asyncio.py
Did you try to specify -p 5020 ?
> A lot of thing have changed. @dhoomakethu it's your code, between 3.2 and 3.3 I do not think there was changes to asyncio.py

I will take a look, I am not up to date with the latest changes.
Apart from that I just tested and it surely does not listen.
@dhoomakethu the real problem is that we do not have test cases that cover  at least the basic of repl server and repl client, so we do not see if something is broken.

I added tests for all examples, that basically just ensures the example can start and communicate, but it really helped in avoiding problems.

> @dhoomakethu the real problem is that we do not have test cases that cover  at least the basic of repl server and repl client, so we do not see if something is broken.
> 
> I added tests for all examples, that basically just ensures the example can start and communicate, but it really helped in avoiding problems.
> 

Understood, I will start on the tests as well. 
I debugged the problem:
- the issue only happens when the pymodbus.server is run in repl mode (default)
- the problematic patch is d6dce903 (from @alexrudd2 for issue #1498) - if I reverse apply it, pymodbus.server runs fine in both modes



Resolving patch:
diff --git a/pymodbus/repl/server/main.py b/pymodbus/repl/server/main.py
--- a/pymodbus/repl/server/main.py
+++ b/pymodbus/repl/server/main.py
@@ -198,10 +198,10 @@ def run(
         **web_app_config,
         **modbus_config,
     )
+    loop.run_until_complete(app.run_async(repl))
     if repl:
         loop.run_until_complete(run_repl(app))
     else:
-        loop.run_until_complete(app.run_async(repl))
         loop.run_forever()
 
 


Test patch: