Repository: pymodbus-dev/pymodbus

Issue:
REPL server missing serial configuration
<!--
Please use the Pymodbus gitter channel at https://gitter.im/pymodbus_dev/Lobby or Stack Overflow(tag [pymodbus](https://stackoverflow.com/questions/tagged/pymodbus) for
support questions.

Before opening a new issue, make sure you do the following:
    * check that your issue isn't already filed: https://github.com/riptideio/pymodbus/issues
    * check the discussions forum https://github.com/riptideio/pymodbus/discussions
    * prepare a short, runnable example that reproduce the issue with the latest development version of Pymodbus
-->

### Versions

* Python: Python 3.9.2 (default, Mar 12 2021, 04:06:34)
[GCC 10.2.1 20210110] on linux
* OS:  PRETTY_NAME="Raspbian GNU/Linux 11 (bullseye)"
NAME="Raspbian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=raspbian
ID_LIKE=debian
* Pymodbus: 3.0.2 REPL
* Modbus Hardware (if used): Serial USB Adapter

### Pymodbus Specific
* Server: rtu - sync/async
* Client: rtu - sync/async

### Description

I am using pymodbus REPL server and client for modbus rtu communication with two usb adapters. Unfortunately I wasn't able to configure another baudrate, parity, stop bits and other serial settings for the server. I checked the config of the serial port with the command stty. In my example the server wasn't set to 38400 baud. I was able to set it manually while running the server. The client settings are fine.

Are there command line parameters to set those properties? I haven't found them in the example videos, docs or code. 

### Code and Logs

```python
pymodbus.server --verbose run -s serial -f rtu -p /dev/ttyUSB1 --baudrate 38400 -u 1 -r 2

pymodbus.console serial --method rtu --port /dev/ttyUSB0 --baudrate 38400
```
```
#serial settings and logs
pi@pi1:~/pymodbus-dev/pymodbus $ stty -F /dev/ttyUSB1 -a
#speed 9600 baud; rows 0; columns 0; line = 0;
#intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; #swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = #^V; discard = ^O; min = 0; time = 0;
#-parenb -parodd -cmspar cs8 hupcl -cstopb cread clocal -crtscts
#-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff -iuclc -ixany #-imaxbel -iutf8
#-opost -olcuc -ocrnl -onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
#-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt -#echoctl -echoke -flusho -extproc

#manual edit interface while running the server
stty -F /dev/ttyUSB1 38400
```



Discussion:
Will take a look and revert.
Thanks for the bug, the serial args are being dropped silently. I will have a fix in a day or two.

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
@@ -4,6 +4,7 @@
 import asyncio
 import json
 import logging
+import sys
 from enum import Enum
 from pathlib import Path
 from typing import List
@@ -26,6 +27,7 @@
 
 CONTEXT_SETTING = {"allow_extra_args": True, "ignore_unknown_options": True}
 
+
 # TBD class ModbusServerConfig:
 
 
@@ -71,6 +73,23 @@ def servers(incomplete: str) -> List[str]:
     return _completer(incomplete, _servers)
 
 
+def process_extra_args(extra_args: list[str], modbus_config: dict) -> dict:
+    """Process extra args passed to server."""
+    options_stripped = [x.strip().replace("--", "") for x in extra_args[::2]]
+    extra_args = dict(list(zip(options_stripped, extra_args[1::2])))
+    for option, value in extra_args.items():
+        if option in modbus_config:
+            try:
+                modbus_config[option] = type(modbus_config[option])(value)
+            except ValueError as err:
+                msg = (
+                    f"Error parsing extra arg {option}' " f"with value '{value}'. {err}"
+                )
+                _logger.error(msg)
+                sys.exit(1)
+    return modbus_config
+
+
 app = typer.Typer(
     no_args_is_help=True,
     context_settings=CONTEXT_SETTING,
@@ -163,8 +182,10 @@ def run(
     else:
         modbus_config = DEFAULT_CONFIG
 
+    extra_args = ctx.args
     data_block_settings = modbus_config.pop("data_block_settings", {})
     modbus_config = modbus_config.get(modbus_server, {})
+    modbus_config = process_extra_args(extra_args, modbus_config)
     if modbus_server != "serial":
         modbus_port = int(modbus_port)
         handler = modbus_config.pop("handler", "ModbusConnectedRequestHandler")


Test patch: