Skip to content

Commit 20108a3

Browse files
committed
pymodbus repl based on prompt toolkit - intial version
1 parent 7fc559b commit 20108a3

File tree

13 files changed

+1501
-65
lines changed

13 files changed

+1501
-65
lines changed

.coveragerc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[run]
2+
omit =
3+
pymodbus/repl/*

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ test/__pycache__/
3636
/doc/html/
3737
/doc/_build/
3838
.pytest_cache/
39+
/.pymodhis

CHANGELOG.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1+
Version 2.0.0rc2
2+
-----------------------------------------------------------
3+
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**
4+
5+
* Async client implementation based on Tornado, Twisted and asyncio with backward compatibility support for twisted client.
6+
* Allow reusing existing[running] asyncio loop when creating async client based on asyncio.
7+
* Allow reusing address for Modbus TCP sync server.
8+
* Add support to install tornado as extra requirement while installing pymodbus.
9+
* Support Pymodbus REPL
10+
* Add support to python 3.7.
11+
* Bug fix and enhancements in examples.
12+
13+
114
Version 2.0.0rc1
215
-----------------------------------------------------------
316
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**
417

518
* Async client implementation based on Tornado, Twisted and asyncio
619

20+
721
Version 1.5.2
822
------------------------------------------------------------
923
* Fix serial client `is_socket_open` method

pymodbus/compat.py

Lines changed: 44 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
'''
1+
"""
22
Python 2.x/3.x Compatibility Layer
33
-------------------------------------------------
44
@@ -10,87 +10,68 @@
1010
1111
:copyright: Copyright 2013 by the Jinja team, see AUTHORS.
1212
:license: BSD, see LICENSE for details.
13-
'''
13+
"""
1414
import sys
15-
import struct
15+
import six
1616

17-
#---------------------------------------------------------------------------#
17+
# --------------------------------------------------------------------------- #
1818
# python version checks
19-
#---------------------------------------------------------------------------#
19+
# --------------------------------------------------------------------------- #
2020
PYTHON_VERSION = sys.version_info
21-
IS_PYTHON2 = PYTHON_VERSION[0] == 2
22-
IS_PYTHON3 = PYTHON_VERSION[0] == 3
23-
IS_PYPY = hasattr(sys, 'pypy_translation_info')
24-
IS_JYTHON = sys.platform.startswith('java')
21+
IS_PYTHON2 = six.PY2
22+
IS_PYTHON3 = six.PY3
23+
IS_PYPY = hasattr(sys, 'pypy_translation_info')
24+
IS_JYTHON = sys.platform.startswith('java')
2525

26-
#---------------------------------------------------------------------------#
27-
# python > 3.3 compatability layer
28-
#---------------------------------------------------------------------------#
29-
if not IS_PYTHON2:
30-
#-----------------------------------------------------------------------#
31-
# portable builtins
32-
#-----------------------------------------------------------------------#
33-
int2byte = lambda b: struct.pack('B', b)
34-
byte2int = lambda b: b
35-
unichr = chr
36-
range_type = range
37-
text_type = str
38-
string_types = (str,)
39-
iterkeys = lambda d: iter(d.keys())
40-
itervalues = lambda d: iter(d.values())
41-
iteritems = lambda d: iter(d.items())
42-
get_next = lambda x: x.__next__()
43-
44-
#-----------------------------------------------------------------------#
45-
# module renames
46-
#-----------------------------------------------------------------------#
47-
from io import BytesIO, StringIO
48-
NativeStringIO = StringIO
26+
# --------------------------------------------------------------------------- #
27+
# python > 3.3 compatibility layer
28+
# --------------------------------------------------------------------------- #
29+
# ----------------------------------------------------------------------- #
30+
# portable builtins
31+
# ----------------------------------------------------------------------- #
32+
int2byte = six.int2byte
33+
unichr = six.unichr
34+
range_type = six.moves.range
35+
text_type = six.string_types
36+
string_types = six.string_types
37+
iterkeys = six.iterkeys
38+
itervalues = six.itervalues
39+
iteritems = six.iteritems
40+
get_next = six.next
41+
unicode_string = six.u
4942

50-
ifilter = filter
51-
imap = map
52-
izip = zip
53-
intern = sys.intern
43+
NativeStringIO = six.StringIO
44+
ifilter = six.moves.filter
45+
imap = six.moves.map
46+
izip = six.moves.zip
47+
intern = six.moves.intern
5448

49+
if not IS_PYTHON2:
50+
# ----------------------------------------------------------------------- #
51+
# module renames
52+
# ----------------------------------------------------------------------- #
5553
import socketserver
5654

57-
#-----------------------------------------------------------------------#
55+
# ----------------------------------------------------------------------- #
5856
# decorators
59-
#-----------------------------------------------------------------------#
57+
# ----------------------------------------------------------------------- #
6058
implements_to_string = lambda x: x
6159

62-
#---------------------------------------------------------------------------#
60+
byte2int = lambda b: b
61+
# --------------------------------------------------------------------------- #
6362
# python > 2.5 compatability layer
64-
#---------------------------------------------------------------------------#
63+
# --------------------------------------------------------------------------- #
6564
else:
66-
#-----------------------------------------------------------------------#
67-
# portable builtins
68-
#-----------------------------------------------------------------------#
69-
int2byte = chr
70-
byte2int = ord
71-
unichr = unichr
72-
text_type = unicode
73-
range_type = xrange
74-
string_types = (str, unicode)
75-
iterkeys = lambda d: d.iterkeys()
76-
itervalues = lambda d: d.itervalues()
77-
iteritems = lambda d: d.iteritems()
78-
get_next = lambda x: x.next()
79-
80-
#-----------------------------------------------------------------------#
65+
byte2int = six.byte2int
66+
# ----------------------------------------------------------------------- #
8167
# module renames
82-
#-----------------------------------------------------------------------#
83-
from cStringIO import StringIO as BytesIO, StringIO
84-
NativeStringIO = BytesIO
85-
86-
from itertools import imap, izip, ifilter
87-
intern = intern
8868

69+
# ----------------------------------------------------------------------- #
8970
import SocketServer as socketserver
9071

91-
#-----------------------------------------------------------------------#
72+
# ----------------------------------------------------------------------- #
9273
# decorators
93-
#-----------------------------------------------------------------------#
74+
# ----------------------------------------------------------------------- #
9475
def implements_to_string(klass):
9576
klass.__unicode__ = klass.__str__
9677
klass.__str__ = lambda x: x.__unicode__().encode('utf-8')

pymodbus/payload.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pymodbus.utilities import unpack_bitstring
1515
from pymodbus.utilities import make_byte_string
1616
from pymodbus.exceptions import ParameterException
17-
17+
from pymodbus.compat import unicode_string
1818
# --------------------------------------------------------------------------- #
1919
# Logging
2020
# --------------------------------------------------------------------------- #
@@ -341,7 +341,7 @@ def _unpack_words(self, fstring, handle):
341341
pk = self._byteorder + 'H'
342342
handle = [pack(pk, p) for p in handle]
343343
handle = b''.join(handle)
344-
_logger.debug(handle)
344+
_logger.debug(unicode_string(handle))
345345
return handle
346346

347347
def reset(self):

pymodbus/repl/README.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Pymodbus REPL
2+
3+
## Dependencies
4+
5+
Depends on [prompt_toolkit](https://python-prompt-toolkit.readthedocs.io/en/stable/index.html) and [click](http://click.pocoo.org/6/quickstart/)
6+
7+
Install dependencies
8+
```
9+
$ pip install click prompt_toolkit --upgarde
10+
```
11+
12+
Or
13+
Install pymodbus with repl support
14+
```
15+
$ pip install pymodbus[repl] --upgrade
16+
```
17+
18+
## Usage Instructions
19+
RTU and TCP are supported as of now
20+
```
21+
bash-3.2$ pymodbus.console
22+
Usage: pymodbus.console [OPTIONS] COMMAND [ARGS]...
23+
24+
Options:
25+
--version Show the version and exit.
26+
--verbose Verbose logs
27+
--support-diag Support Diagnostic messages
28+
--help Show this message and exit.
29+
30+
Commands:
31+
rtu
32+
tcp
33+
34+
35+
```
36+
TCP Options
37+
```
38+
bash-3.2$ pymodbus.console tcp --help
39+
Usage: pymodbus.console tcp [OPTIONS]
40+
41+
Options:
42+
--host TEXT Modbus TCP IP
43+
--port INTEGER Modbus TCP port
44+
--help Show this message and exit.
45+
46+
47+
48+
49+
```
50+
51+
RTU Options
52+
```
53+
bash-3.2$ pymodbus.console rtu --help
54+
Usage: pymodbus.console rtu [OPTIONS]
55+
56+
Options:
57+
--method TEXT Modbus Serial Mode (rtu/ascii)
58+
--port TEXT Modbus RTU port
59+
--baudrate INTEGER Modbus RTU serial baudrate to use. Defaults to 9600
60+
--bytesize [5|6|7|8] Modbus RTU serial Number of data bits. Possible
61+
values: FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS.
62+
Defaults to 8
63+
--parity [N|E|O|M|S] Modbus RTU serial parity. Enable parity checking.
64+
Possible values: PARITY_NONE, PARITY_EVEN, PARITY_ODD
65+
PARITY_MARK, PARITY_SPACE. Default to 'N'
66+
--stopbits [1|1.5|2] Modbus RTU serial stop bits. Number of stop bits.
67+
Possible values: STOPBITS_ONE,
68+
STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO. Default to '1'
69+
--xonxoff INTEGER Modbus RTU serial xonxoff. Enable software flow
70+
control.Defaults to 0
71+
--rtscts INTEGER Modbus RTU serial rtscts. Enable hardware (RTS/CTS)
72+
flow control. Defaults to 0
73+
--dsrdtr INTEGER Modbus RTU serial dsrdtr. Enable hardware (DSR/DTR)
74+
flow control. Defaults to 0
75+
--timeout FLOAT Modbus RTU serial read timeout. Defaults to 0.025 sec
76+
--write-timeout FLOAT Modbus RTU serial write timeout. Defaults to 2 sec
77+
--help Show this message and exit.
78+
```
79+
80+
To view all available commands type `help`
81+
82+
```
83+
$ pymodbus.console tcp --host 192.168.128.126 --port 5020
84+
85+
> help
86+
Available commands:
87+
client.change_ascii_input_delimiter Diagnostic sub command, Change message delimiter for future requests
88+
client.clear_counters Diagnostic sub command, Clear all counters and diag registers
89+
client.clear_overrun_count Diagnostic sub command, Clear over run counter
90+
client.force_listen_only_mode Diagnostic sub command, Forces the addressed remote device to its Listen Only Mode
91+
client.get_clear_modbus_plus Diagnostic sub command, Get or clear stats of remote modbus plus device
92+
client.get_com_event_counter Read status word and an event count from the remote device's communication event counter
93+
client.get_com_event_log Read status word, event count, message count, and a field of event bytes from the remote device.
94+
client.mask_write_register Mask content of holding register at `address` with `and_mask` and `or_mask`
95+
client.read_coils Reads `count` coils from a given slave starting at `address`
96+
client.read_device_information Read the identification and additional information of remote slave
97+
client.read_discrete_inputs Reads `count` number of discrete inputs starting at offset `address`
98+
client.read_exception_status Read the contents of eight Exception Status outputs in a remote device.
99+
client.read_holding_registers Read `count` number of holding registers starting at `address`
100+
client.read_input_registers Read `count` number of input registers starting at `address`
101+
client.readwrite_registers Read `read_count` number of holding registers starting at `read_address` and write `write_registers` starting at `write_address`
102+
client.report_slave_id Report information about remote slave ID
103+
client.restart_comm_option Diagnostic sub command, initialize and restart remote devices serial interface and clear all of its communications event counters .
104+
client.return_bus_com_error_count Diagnostic sub command, Return count of CRC errors received by remote slave
105+
client.return_bus_exception_error_count Diagnostic sub command, Return count of Modbus exceptions returned by remote slave
106+
client.return_bus_message_count Diagnostic sub command, Return count of message detected on bus by remote slave
107+
client.return_diagnostic_register Diagnostic sub command, Read 16-bit diagnostic register
108+
client.return_iop_overrun_count Diagnostic sub command, Return count of iop overrun errors by remote slave
109+
client.return_query_data Diagnostic sub command , Loop back data sent in response
110+
client.return_slave_bus_char_overrun_count Diagnostic sub command, Return count of messages not handled by remote slave due to character overrun condition
111+
client.return_slave_busy_count Diagnostic sub command, Return count of server busy exceptions sent by remote slave
112+
client.return_slave_message_count Diagnostic sub command, Return count of messages addressed to remote slave
113+
client.return_slave_no_ack_count Diagnostic sub command, Return count of NO ACK exceptions sent by remote slave
114+
client.return_slave_no_response_count Diagnostic sub command, Return count of No responses by remote slave
115+
client.write_coil Write `value` to coil at `address`
116+
client.write_coils Write `value` to coil at `address`
117+
client.write_register Write `value` to register at `address`
118+
client.write_registers Write list of `values` to registers starting at `address`
119+
result.decode Decode the register response to known formatters
120+
result.raw Return raw result dict
121+
```
122+
123+
Every command has auto suggetion on the arguments supported , supply arg and value are to be supplied in `arg=val` format.
124+
```
125+
126+
> client.read_holding_registers count=4 address=9 unit=1
127+
{
128+
"registers": [
129+
60497,
130+
47134,
131+
34091,
132+
15424
133+
]
134+
}
135+
```
136+
137+
The last result could be accessed with `result.raw` command
138+
```
139+
> result.raw
140+
{
141+
"registers": [
142+
15626,
143+
55203,
144+
28733,
145+
18368
146+
]
147+
}
148+
```
149+
150+
For Holding and Input register reads, the decoded value could be viewed with `result.decode`
151+
```
152+
> result.decode word_order=little byte_order=little formatters=float64
153+
28.17
154+
155+
>
156+
```
157+
158+
#DEMO
159+
160+
[![asciicast](https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o.png)](https://asciinema.org/a/y1xOk7lm59U1bRBE2N1pDIj2o)
161+

pymodbus/repl/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""
2+
Copyright (c) 2018 Riptide IO, Inc. All Rights Reserved.
3+
4+
"""
5+
from __future__ import absolute_import, unicode_literals

0 commit comments

Comments
 (0)