Skip to content

Commit c303e33

Browse files
committed
Merge branch 'dev' of https://github.com/riptideio/pymodbus into dev
2 parents 75951f0 + 815e62a commit c303e33

File tree

11 files changed

+127
-103
lines changed

11 files changed

+127
-103
lines changed

README.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
.. image:: https://travis-ci.org/riptideio/pymodbus.svg?branch=master
22
:target: https://travis-ci.org/riptideio/pymodbus
3-
43
.. image:: https://badges.gitter.im/Join%20Chat.svg
5-
:target: https://gitter.im/pymodbus_dev/Lobby
6-
4+
:target: https://gitter.im/pymodbus_dev/Lobby
75
.. image:: https://readthedocs.org/projects/pymodbus/badge/?version=latest
8-
:target: http://pymodbus.readthedocs.io/en/async/?badge=latest
6+
:target: http://pymodbus.readthedocs.io/en/async/?badge=latest
97
:alt: Documentation Status
10-
8+
.. image:: http://pepy.tech/badge/pymodbus
9+
:target: http://pepy.tech/project/pymodbus
10+
:alt: Downloads
11+
1112
.. important::
1213
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**
1314

examples/common/synchronous_server.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
The synchronous server is implemented in pure python without any third
77
party libraries (unless you need to use the serial protocols which require
88
pyserial). This is helpful in constrained or old environments where using
9-
twisted just is not feasable. What follows is an examle of its use:
9+
twisted is just not feasible. What follows is an example of its use:
1010
"""
11-
# --------------------------------------------------------------------------- #
11+
# --------------------------------------------------------------------------- #
1212
# import the various server implementations
13-
# --------------------------------------------------------------------------- #
13+
# --------------------------------------------------------------------------- #
1414
from pymodbus.server.sync import StartTcpServer
1515
from pymodbus.server.sync import StartUdpServer
1616
from pymodbus.server.sync import StartSerialServer
@@ -20,9 +20,9 @@
2020
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext
2121

2222
from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer
23-
# --------------------------------------------------------------------------- #
23+
# --------------------------------------------------------------------------- #
2424
# configure the service logging
25-
# --------------------------------------------------------------------------- #
25+
# --------------------------------------------------------------------------- #
2626
import logging
2727
FORMAT = ('%(asctime)-15s %(threadName)-15s'
2828
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
@@ -32,9 +32,9 @@
3232

3333

3434
def run_server():
35-
# ----------------------------------------------------------------------- #
35+
# ----------------------------------------------------------------------- #
3636
# initialize your data store
37-
# ----------------------------------------------------------------------- #
37+
# ----------------------------------------------------------------------- #
3838
# The datastores only respond to the addresses that they are initialized to
3939
# Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a
4040
# request to 0x100 will respond with an invalid address exception. This is
@@ -58,7 +58,7 @@ def run_server():
5858
# store = ModbusSlaveContext()
5959
#
6060
# Finally, you are allowed to use the same DataBlock reference for every
61-
# table or you you may use a seperate DataBlock for each table.
61+
# table or you may use a separate DataBlock for each table.
6262
# This depends if you would like functions to be able to access and modify
6363
# the same data or not::
6464
#
@@ -85,20 +85,20 @@ def run_server():
8585
# will map to (1-8)::
8686
#
8787
# store = ModbusSlaveContext(..., zero_mode=True)
88-
# ----------------------------------------------------------------------- #
88+
# ----------------------------------------------------------------------- #
8989
store = ModbusSlaveContext(
9090
di=ModbusSequentialDataBlock(0, [17]*100),
9191
co=ModbusSequentialDataBlock(0, [17]*100),
9292
hr=ModbusSequentialDataBlock(0, [17]*100),
9393
ir=ModbusSequentialDataBlock(0, [17]*100))
9494

9595
context = ModbusServerContext(slaves=store, single=True)
96-
97-
# ----------------------------------------------------------------------- #
96+
97+
# ----------------------------------------------------------------------- #
9898
# initialize the server information
99-
# ----------------------------------------------------------------------- #
99+
# ----------------------------------------------------------------------- #
100100
# If you don't set this or any fields, they are defaulted to empty strings.
101-
# ----------------------------------------------------------------------- #
101+
# ----------------------------------------------------------------------- #
102102
identity = ModbusDeviceIdentification()
103103
identity.VendorName = 'Pymodbus'
104104
identity.ProductCode = 'PM'
@@ -109,7 +109,7 @@ def run_server():
109109

110110
# ----------------------------------------------------------------------- #
111111
# run the server you want
112-
# ----------------------------------------------------------------------- #
112+
# ----------------------------------------------------------------------- #
113113
# Tcp:
114114
StartTcpServer(context, identity=identity, address=("localhost", 5020))
115115

@@ -119,11 +119,11 @@ def run_server():
119119

120120
# Udp:
121121
# StartUdpServer(context, identity=identity, address=("0.0.0.0", 5020))
122-
122+
123123
# Ascii:
124124
# StartSerialServer(context, identity=identity,
125125
# port='/dev/ttyp0', timeout=1)
126-
126+
127127
# RTU:
128128
# StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
129129
# port='/dev/ttyp0', timeout=.005, baudrate=9600)

examples/contrib/remote_server_context.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def validate(self, fx, address, count=1):
7373
return not result.isError()
7474

7575
def getValues(self, fx, address, count=1):
76-
""" Validates the request to make sure it is in range
76+
""" Get `count` values from datastore
7777
7878
:param fx: The function we are working with
7979
:param address: The starting address
@@ -118,7 +118,8 @@ def __extract_result(self, fx, result):
118118
return result.bits
119119
if fx in ['h', 'i']:
120120
return result.registers
121-
else: return result
121+
else:
122+
return result
122123

123124
# -------------------------------------------------------------------------- #
124125
# Server Context
@@ -152,7 +153,7 @@ def __init__(self, client):
152153
'i': lambda a, v, s: client.write_registers(a, v, s),
153154
}
154155
self._client = client
155-
self.slaves = {} # simply a cache
156+
self.slaves = {} # simply a cache
156157

157158
def __str__(self):
158159
""" Returns a string representation of the context
@@ -187,14 +188,14 @@ def __setitem__(self, slave, context):
187188
:param slave: The slave context to set
188189
:param context: The new context to set for this slave
189190
"""
190-
raise NotImplementedException() # doesn't make sense here
191+
raise NotImplementedException() # doesn't make sense here
191192

192193
def __delitem__(self, slave):
193194
""" Wrapper used to access the slave context
194195
195196
:param slave: The slave context to remove
196197
"""
197-
raise NotImplementedException() # doesn't make sense here
198+
raise NotImplementedException() # doesn't make sense here
198199

199200
def __getitem__(self, slave):
200201
""" Used to get access to a slave context

pymodbus/client/sync.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import socket
2+
import select
23
import serial
34
import time
45
import sys
@@ -230,34 +231,43 @@ def _recv(self, size):
230231
"""
231232
if not self.socket:
232233
raise ConnectionException(self.__str__())
233-
# socket.recv(size) waits until it gets some data from the host but
234-
# not necessarily the entire response that can be fragmented in
235-
# many packets.
236-
# To avoid the splitted responses to be recognized as invalid
237-
# messages and to be discarded, loops socket.recv until full data
238-
# is received or timeout is expired.
239-
# If timeout expires returns the read data, also if its length is
240-
# less than the expected size.
234+
235+
# socket.recv(size) waits until it gets some data from the host but
236+
# not necessarily the entire response that can be fragmented in
237+
# many packets.
238+
# To avoid the splitted responses to be recognized as invalid
239+
# messages and to be discarded, loops socket.recv until full data
240+
# is received or timeout is expired.
241+
# If timeout expires returns the read data, also if its length is
242+
# less than the expected size.
241243
self.socket.setblocking(0)
242-
begin = time.time()
243244

244-
data = b''
245-
if size is not None:
246-
while len(data) < size:
247-
try:
248-
data += self.socket.recv(size - len(data))
249-
except socket.error:
250-
pass
251-
if not self.timeout or (time.time() - begin > self.timeout):
252-
break
245+
timeout = self.timeout
246+
247+
# If size isn't specified read 1 byte at a time.
248+
if size is None:
249+
recv_size = 1
253250
else:
254-
while True:
255-
try:
256-
data += self.socket.recv(1)
257-
except socket.error:
258-
pass
259-
if not self.timeout or (time.time() - begin > self.timeout):
260-
break
251+
recv_size = size
252+
253+
data = b''
254+
begin = time.time()
255+
while recv_size > 0:
256+
ready = select.select([self.socket], [], [], timeout)
257+
if ready[0]:
258+
data += self.socket.recv(recv_size)
259+
260+
# If size isn't specified continue to read until timeout expires.
261+
if size:
262+
recv_size = size - len(data)
263+
264+
# Timeout is reduced also if some data has been received in order
265+
# to avoid infinite loops when there isn't an expected response size
266+
# and the slave sends noisy data continuosly.
267+
timeout -= time.time() - begin
268+
if timeout <= 0:
269+
break
270+
261271
return data
262272

263273
def is_socket_open(self):

pymodbus/datastore/context.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, *args, **kwargs):
3636
self.store['c'] = kwargs.get('co', ModbusSequentialDataBlock.create())
3737
self.store['i'] = kwargs.get('ir', ModbusSequentialDataBlock.create())
3838
self.store['h'] = kwargs.get('hr', ModbusSequentialDataBlock.create())
39-
self.zero_mode = kwargs.get('zero_mode', Defaults.ZeroMode)
39+
self.zero_mode = kwargs.get('zero_mode', Defaults.ZeroMode)
4040

4141
def __str__(self):
4242
''' Returns a string representation of the context
@@ -58,19 +58,21 @@ def validate(self, fx, address, count=1):
5858
:param count: The number of values to test
5959
:returns: True if the request in within range, False otherwise
6060
'''
61-
if not self.zero_mode: address = address + 1
61+
if not self.zero_mode:
62+
address = address + 1
6263
_logger.debug("validate[%d] %d:%d" % (fx, address, count))
6364
return self.store[self.decode(fx)].validate(address, count)
6465

6566
def getValues(self, fx, address, count=1):
66-
''' Validates the request to make sure it is in range
67+
''' Get `count` values from datastore
6768
6869
:param fx: The function we are working with
6970
:param address: The starting address
7071
:param count: The number of values to retrieve
7172
:returns: The requested values from a:a+c
7273
'''
73-
if not self.zero_mode: address = address + 1
74+
if not self.zero_mode:
75+
address = address + 1
7476
_logger.debug("getValues[%d] %d:%d" % (fx, address, count))
7577
return self.store[self.decode(fx)].getValues(address, count)
7678

@@ -81,7 +83,8 @@ def setValues(self, fx, address, values):
8183
:param address: The starting address
8284
:param values: The new values to be set
8385
'''
84-
if not self.zero_mode: address = address + 1
86+
if not self.zero_mode:
87+
address = address + 1
8588
_logger.debug("setValues[%d] %d:%d" % (fx, address, len(values)))
8689
self.store[self.decode(fx)].setValues(address, values)
8790

pymodbus/datastore/database/redis_datastore.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#---------------------------------------------------------------------------#
66
# Logging
77
#---------------------------------------------------------------------------#
8-
import logging;
8+
import logging
99
_logger = logging.getLogger(__name__)
1010

1111

@@ -55,7 +55,7 @@ def validate(self, fx, address, count=1):
5555
return self._val_callbacks[self.decode(fx)](address, count)
5656

5757
def getValues(self, fx, address, count=1):
58-
''' Validates the request to make sure it is in range
58+
''' Get `count` values from datastore
5959
6060
:param fx: The function we are working with
6161
:param address: The starting address
@@ -94,28 +94,28 @@ def _build_mapping(self):
9494
code mapper.
9595
'''
9696
self._val_callbacks = {
97-
'd' : lambda o, c: self._val_bit('d', o, c),
98-
'c' : lambda o, c: self._val_bit('c', o, c),
99-
'h' : lambda o, c: self._val_reg('h', o, c),
100-
'i' : lambda o, c: self._val_reg('i', o, c),
97+
'd': lambda o, c: self._val_bit('d', o, c),
98+
'c': lambda o, c: self._val_bit('c', o, c),
99+
'h': lambda o, c: self._val_reg('h', o, c),
100+
'i': lambda o, c: self._val_reg('i', o, c),
101101
}
102102
self._get_callbacks = {
103-
'd' : lambda o, c: self._get_bit('d', o, c),
104-
'c' : lambda o, c: self._get_bit('c', o, c),
105-
'h' : lambda o, c: self._get_reg('h', o, c),
106-
'i' : lambda o, c: self._get_reg('i', o, c),
103+
'd': lambda o, c: self._get_bit('d', o, c),
104+
'c': lambda o, c: self._get_bit('c', o, c),
105+
'h': lambda o, c: self._get_reg('h', o, c),
106+
'i': lambda o, c: self._get_reg('i', o, c),
107107
}
108108
self._set_callbacks = {
109-
'd' : lambda o, v: self._set_bit('d', o, v),
110-
'c' : lambda o, v: self._set_bit('c', o, v),
111-
'h' : lambda o, v: self._set_reg('h', o, v),
112-
'i' : lambda o, v: self._set_reg('i', o, v),
109+
'd': lambda o, v: self._set_bit('d', o, v),
110+
'c': lambda o, v: self._set_bit('c', o, v),
111+
'h': lambda o, v: self._set_reg('h', o, v),
112+
'i': lambda o, v: self._set_reg('i', o, v),
113113
}
114114

115115
#--------------------------------------------------------------------------#
116116
# Redis discrete implementation
117117
#--------------------------------------------------------------------------#
118-
_bit_size = 16
118+
_bit_size = 16
119119
_bit_default = '\x00' * (_bit_size % 8)
120120

121121
def _get_bit_values(self, key, offset, count):
@@ -129,7 +129,7 @@ def _get_bit_values(self, key, offset, count):
129129
s = divmod(offset, self._bit_size)[0]
130130
e = divmod(offset + count, self._bit_size)[0]
131131

132-
request = ('%s:%s' % (key, v) for v in range(s, e + 1))
132+
request = ('%s:%s' % (key, v) for v in range(s, e + 1))
133133
response = self.client.mget(request)
134134
return response
135135

@@ -173,7 +173,7 @@ def _set_bit(self, key, offset, values):
173173
current = (r or self._bit_default for r in current)
174174
current = ''.join(current)
175175
current = current[0:offset] + value.decode('utf-8') + current[offset + count:]
176-
final = (current[s:s + self._bit_size] for s in range(0, count, self._bit_size))
176+
final = (current[s:s + self._bit_size] for s in range(0, count, self._bit_size))
177177

178178
key = self._get_prefix(key)
179179
request = ('%s:%s' % (key, v) for v in range(s, e + 1))
@@ -183,7 +183,7 @@ def _set_bit(self, key, offset, values):
183183
#--------------------------------------------------------------------------#
184184
# Redis register implementation
185185
#--------------------------------------------------------------------------#
186-
_reg_size = 16
186+
_reg_size = 16
187187
_reg_default = '\x00' * (_reg_size % 8)
188188

189189
def _get_reg_values(self, key, offset, count):
@@ -198,7 +198,7 @@ def _get_reg_values(self, key, offset, count):
198198
#e = divmod(offset+count, self.__reg_size)[0]
199199

200200
#request = ('%s:%s' % (key, v) for v in range(s, e + 1))
201-
request = ('%s:%s' % (key, v) for v in range(offset, count + 1))
201+
request = ('%s:%s' % (key, v) for v in range(offset, count + 1))
202202
response = self.client.mget(request)
203203
return response
204204

0 commit comments

Comments
 (0)