Skip to content

Commit

Permalink
Merge pull request #8 from pymodbus-dev/repl-server-startup
Browse files Browse the repository at this point in the history
Fix repl failing to start on python 3.8, address review comments
  • Loading branch information
dhoomakethu authored Feb 21, 2024
2 parents 7b7d2f0 + 1130fb2 commit 1a6a740
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
.idea/
76 changes: 75 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Pymodbus-REPL is a REPL (Read-Eval-Print Loop) tool for working with Modbus devi
- Python 3.8+
- Poetry (installed globally or within a virtual environment)

### Installation
### Dev instructions

1. Clone the repository:

Expand All @@ -23,6 +23,80 @@ Pymodbus-REPL is a REPL (Read-Eval-Print Loop) tool for working with Modbus devi

`poetry install`

**NOTE** This repo is meant to be an helper for [pymodbus](https://github.com/pymodbus-dev/pymodbus) and the usage requires
a working version of pymodbus.

The installed `pymodbus` library for local development can also have impact on the path resolution while working locally on this repo.
To overcome that problem, please make sure to run the [client](./pymodbus/client/main.py) and [server](./pymodbus/server/main.py) files
from with in the respective working directories.

For .e.g

#### Run Server
```
(pymodbus3.8)
pymodbus/repl/server on  repl-server-startup [!?] via 🐍 v3.8.13 (pymodbus3.8)
❯ python3 main.py --host 0.0.0.0 --verbose run --modbus-config default_config.json --modbus-server tcp --modbus-framer socket --modbus-port 5020 --unit-id 1 --unit-id 2 -u 4 -r 1 --timeout 2
2024-02-17 13:27:17,218 INFO logging:97 Modbus server started
2024-02-17 13:27:17,219 DEBUG logging:103 Awaiting connections server_listener
2024-02-17 13:27:17,219 INFO logging:97 Server listening.
__________ .______. _________
\______ \___.__. _____ ____ __| _/\_ |__ __ __ ______ / _____/ ______________ __ ___________
| ___< | |/ \ / _ \ / __ | | __ \| | \/ ___/ \_____ \_/ __ \_ __ \ \/ // __ \_ __ \\
| | \___ | Y Y ( <_> ) /_/ | | \_\ \ | /\___ \ / \ ___/| | \/\ /\ ___/| | \/
|____| / ____|__|_| /\____/\____ | |___ /____//____ > /_______ /\___ >__| \_/ \___ >__|
\/ \/ \/ \/ \/ \/ \/ \/
SERVER >
```

#### Run client
```
pymodbus/repl/client on  repl-server-startup [!?] via 🐍 v3.8.13 (pymodbus3.8)
❯ python3 main.py tcp --port 5020 --framer tcp
----------------------------------------------------------------------------
__________ _____ .___ __________ .__
\______ \___.__. / \ ____ __| _/ \______ \ ____ ______ | |
| ___< | |/ \ / \ / _ \ / __ | | _// __ \\\____ \| |
| | \___ / Y ( <_> ) /_/ | | | \ ___/| |_> > |__
|____| / ____\____|__ /\____/\____ | /\ |____|_ /\___ > __/|____/
\/ \/ \/ \/ \/ \/|__|
v1.3.1 - 3.6.4
----------------------------------------------------------------------------
> client.read_input_registers address 1 count 1 slave 4
{
"registers": [
34518
]
}
> client.read_input_registers address 1 count 1 slave 1
{
"registers": [
32198
]
}
> client.read_input_registers address 1 count 1 slave 2
{
"registers": [
51557
]
}
> client.read_input_registers address 1 count 1 slave 3
{
"original_function_code": "4 (0x4)",
"error": "[Input/Output] Modbus Error: [Invalid Message] No response received, expected at least 8 bytes (0 received)"
}
```


### Running Tests
To run tests, use the following command:

Expand Down
4 changes: 2 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pymodbus_repl/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
"""REPL (Read-Eval-Print Loop) tool for working with Modbus devices using the Pymodbus library."""
__VERSION__ = "2.0.0"

3 changes: 2 additions & 1 deletion pymodbus_repl/client/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from prompt_toolkit.styles import Style
from pygments.lexers.python import PythonLexer
from pymodbus import __version__ as pymodbus_version
from pymodbus_repl import __VERSION__ as repl_version
from pymodbus.exceptions import ParameterException
from pymodbus.transaction import (
ModbusAsciiFramer,
Expand All @@ -38,7 +39,7 @@
| | \___ / Y ( <_> ) /_/ | | | \ ___/| |_> > |__
|____| / ____\____|__ /\____/\____ | /\ |____|_ /\___ > __/|____/
\/ \/ \/ \/ \/ \/|__|
v1.3.1 - {pymodbus_version}
v{repl_version} - Pymodbus-{pymodbus_version}
----------------------------------------------------------------------------
"""

Expand Down
6 changes: 5 additions & 1 deletion pymodbus_repl/server/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from prompt_toolkit.shortcuts import clear
from prompt_toolkit.shortcuts.progress_bar import formatters
from prompt_toolkit.styles import Style
from pymodbus import __version__ as pymodbus_version
from pymodbus_repl import __VERSION__ as repl_version


TITLE = r"""
Expand All @@ -17,6 +19,7 @@
| | \___ | Y Y ( <_> ) /_/ | | \_\ \ | /\___ \ / \ ___/| | \/\ /\ ___/| | \/
|____| / ____|__|_| /\____/\____ | |___ /____//____ > /_______ /\___ >__| \_/ \___ >__|
\/ \/ \/ \/ \/ \/ \/ \/
v{}-Pymodbus{}
"""


Expand Down Expand Up @@ -111,8 +114,9 @@ def print_title():
max_len = max( # pylint: disable=consider-using-generator
[len(t) for t in TITLE.split("\n")]
)
title = TITLE.format(repl_version, pymodbus_version)
if col > max_len:
info(TITLE)
info(title)
else:
print_formatted_text(
HTML(f'<u><b><style color="green">{SMALL_TITLE}</style></b></u>')
Expand Down
26 changes: 14 additions & 12 deletions pymodbus_repl/server/main.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"""Repl server main."""
from __future__ import annotations
# from __future__ import annotations

import asyncio
import contextlib
import json
import logging
import sys
from enum import Enum
from pathlib import Path

from typing import List, Optional
from pathlib import Path
import typer
from typing_extensions import Annotated
from pymodbus import pymodbus_apply_logging_config
from pymodbus.framer.socket_framer import ModbusSocketFramer
from pymodbus.logging import Log
Expand Down Expand Up @@ -50,7 +52,7 @@ class ModbusFramerTypes(str, Enum):
binary = "binary" # pylint: disable=invalid-name


def _completer(incomplete: str, valid_values: list[str]) -> list[str]:
def _completer(incomplete: str, valid_values: List[str]) -> List[str]:
"""Complete value."""
completion = []
for name in valid_values:
Expand All @@ -59,19 +61,19 @@ def _completer(incomplete: str, valid_values: list[str]) -> list[str]:
return completion


def framers(incomplete: str) -> list[str]:
def framers(incomplete: str) -> List[str]:
"""Return an autocompleted list of supported clouds."""
_framers = ["socket", "rtu", "tls", "ascii", "binary"]
return _completer(incomplete, _framers)


def servers(incomplete: str) -> list[str]:
def servers(incomplete: str) -> List[str]:
"""Return an autocompleted list of supported clouds."""
_servers = ["tcp", "serial", "tls", "udp"]
return _completer(incomplete, _servers)


def process_extra_args(extra_args: list[str], modbus_config: dict) -> dict:
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 = dict(list(zip(options_stripped, extra_args[1::2])))
Expand Down Expand Up @@ -139,12 +141,12 @@ def run(
help="Modbus framer to use",
),
modbus_port: str = typer.Option("5020", "--modbus-port", "-p", help="Modbus port"),
modbus_slave_id: list[int] = typer.Option(
[1], "--slave-id", "-u", help="Supported Modbus slave id's"
),
modbus_config_path: Path = typer.Option(
None, help="Path to additional modbus server config"
),
modbus_slave_id: Annotated[Optional[List[int]], typer.Option(
"--unit-id", "-u", help="Supported Modbus slave id's"
)] = [1],
modbus_config_path: Annotated[Path, typer.Option(
help="Path to additional modbus server config"
)] = None,
randomize: int = typer.Option(
0,
"--random",
Expand Down
7 changes: 4 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pymodbus_repl"
version = "0.1.0"
version = "2.0.0"
description = "REPL (Read-Eval-Print Loop) tool for working with Modbus devices using the Pymodbus library."
authors = ["dhoomakethu <otlasanju@gmail.com>"]
readme = "README.md"
Expand All @@ -11,7 +11,6 @@ python = "^3.8"
typer = {extras = ["all"], version = "^0.9.0"}
prompt-toolkit = "^3.0.43"
pygments = "^2.17.2"
pytest-xdist = "^3.5.0"

[[tool.poetry.dependencies.aiohttp]]
python="3.12"
Expand All @@ -30,6 +29,7 @@ include = ["pymodbus_repl"]
pymodbus = "^3.6.4"
ruff = "^0.2.1"
coverage = "^7.4.1"
pytest-xdist = "^3.5.0"
pytest-cov = "^4.1.0"

[build-system]
Expand All @@ -43,7 +43,7 @@ addopts = "--cov-report html"

[tool.coverage.run]
source = [
"pymodbus/"
"pymodbus_repl/"
]
omit = ["examples/contrib/"]
branch = true
Expand All @@ -56,6 +56,7 @@ exclude_also = [
]
skip_covered = true
fail_under = 70.0
show_missing = true

[tool.coverage.html]
directory = "build/cov"
Expand Down

0 comments on commit 1a6a740

Please sign in to comment.