Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/riptideio/pymodbus into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
dhoomakethu committed Aug 29, 2018
2 parents 75951f0 + 815e62a commit c303e33
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 103 deletions.
11 changes: 6 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
.. image:: https://travis-ci.org/riptideio/pymodbus.svg?branch=master
:target: https://travis-ci.org/riptideio/pymodbus

.. image:: https://badges.gitter.im/Join%20Chat.svg
:target: https://gitter.im/pymodbus_dev/Lobby

:target: https://gitter.im/pymodbus_dev/Lobby
.. image:: https://readthedocs.org/projects/pymodbus/badge/?version=latest
:target: http://pymodbus.readthedocs.io/en/async/?badge=latest
:target: http://pymodbus.readthedocs.io/en/async/?badge=latest
:alt: Documentation Status

.. image:: http://pepy.tech/badge/pymodbus
:target: http://pepy.tech/project/pymodbus
:alt: Downloads

.. important::
**Note This is a Major release and might affect your existing Async client implementation. Refer examples on how to use the latest async clients.**

Expand Down
32 changes: 16 additions & 16 deletions examples/common/synchronous_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
The synchronous server is implemented in pure python without any third
party libraries (unless you need to use the serial protocols which require
pyserial). This is helpful in constrained or old environments where using
twisted just is not feasable. What follows is an examle of its use:
twisted is just not feasible. What follows is an example of its use:
"""
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
# import the various server implementations
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
from pymodbus.server.sync import StartTcpServer
from pymodbus.server.sync import StartUdpServer
from pymodbus.server.sync import StartSerialServer
Expand All @@ -20,9 +20,9 @@
from pymodbus.datastore import ModbusSlaveContext, ModbusServerContext

from pymodbus.transaction import ModbusRtuFramer, ModbusBinaryFramer
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
# configure the service logging
# --------------------------------------------------------------------------- #
# --------------------------------------------------------------------------- #
import logging
FORMAT = ('%(asctime)-15s %(threadName)-15s'
' %(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
Expand All @@ -32,9 +32,9 @@


def run_server():
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
# initialize your data store
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
# The datastores only respond to the addresses that they are initialized to
# Therefore, if you initialize a DataBlock to addresses of 0x00 to 0xFF, a
# request to 0x100 will respond with an invalid address exception. This is
Expand All @@ -58,7 +58,7 @@ def run_server():
# store = ModbusSlaveContext()
#
# Finally, you are allowed to use the same DataBlock reference for every
# table or you you may use a seperate DataBlock for each table.
# table or you may use a separate DataBlock for each table.
# This depends if you would like functions to be able to access and modify
# the same data or not::
#
Expand All @@ -85,20 +85,20 @@ def run_server():
# will map to (1-8)::
#
# store = ModbusSlaveContext(..., zero_mode=True)
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
store = ModbusSlaveContext(
di=ModbusSequentialDataBlock(0, [17]*100),
co=ModbusSequentialDataBlock(0, [17]*100),
hr=ModbusSequentialDataBlock(0, [17]*100),
ir=ModbusSequentialDataBlock(0, [17]*100))

context = ModbusServerContext(slaves=store, single=True)
# ----------------------------------------------------------------------- #

# ----------------------------------------------------------------------- #
# initialize the server information
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
# If you don't set this or any fields, they are defaulted to empty strings.
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
identity = ModbusDeviceIdentification()
identity.VendorName = 'Pymodbus'
identity.ProductCode = 'PM'
Expand All @@ -109,7 +109,7 @@ def run_server():

# ----------------------------------------------------------------------- #
# run the server you want
# ----------------------------------------------------------------------- #
# ----------------------------------------------------------------------- #
# Tcp:
StartTcpServer(context, identity=identity, address=("localhost", 5020))

Expand All @@ -119,11 +119,11 @@ def run_server():

# Udp:
# StartUdpServer(context, identity=identity, address=("0.0.0.0", 5020))

# Ascii:
# StartSerialServer(context, identity=identity,
# port='/dev/ttyp0', timeout=1)

# RTU:
# StartSerialServer(context, framer=ModbusRtuFramer, identity=identity,
# port='/dev/ttyp0', timeout=.005, baudrate=9600)
Expand Down
11 changes: 6 additions & 5 deletions examples/contrib/remote_server_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def validate(self, fx, address, count=1):
return not result.isError()

def getValues(self, fx, address, count=1):
""" Validates the request to make sure it is in range
""" Get `count` values from datastore
:param fx: The function we are working with
:param address: The starting address
Expand Down Expand Up @@ -118,7 +118,8 @@ def __extract_result(self, fx, result):
return result.bits
if fx in ['h', 'i']:
return result.registers
else: return result
else:
return result

# -------------------------------------------------------------------------- #
# Server Context
Expand Down Expand Up @@ -152,7 +153,7 @@ def __init__(self, client):
'i': lambda a, v, s: client.write_registers(a, v, s),
}
self._client = client
self.slaves = {} # simply a cache
self.slaves = {} # simply a cache

def __str__(self):
""" Returns a string representation of the context
Expand Down Expand Up @@ -187,14 +188,14 @@ def __setitem__(self, slave, context):
:param slave: The slave context to set
:param context: The new context to set for this slave
"""
raise NotImplementedException() # doesn't make sense here
raise NotImplementedException() # doesn't make sense here

def __delitem__(self, slave):
""" Wrapper used to access the slave context
:param slave: The slave context to remove
"""
raise NotImplementedException() # doesn't make sense here
raise NotImplementedException() # doesn't make sense here

def __getitem__(self, slave):
""" Used to get access to a slave context
Expand Down
60 changes: 35 additions & 25 deletions pymodbus/client/sync.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import socket
import select
import serial
import time
import sys
Expand Down Expand Up @@ -230,34 +231,43 @@ def _recv(self, size):
"""
if not self.socket:
raise ConnectionException(self.__str__())
# socket.recv(size) waits until it gets some data from the host but
# not necessarily the entire response that can be fragmented in
# many packets.
# To avoid the splitted responses to be recognized as invalid
# messages and to be discarded, loops socket.recv until full data
# is received or timeout is expired.
# If timeout expires returns the read data, also if its length is
# less than the expected size.

# socket.recv(size) waits until it gets some data from the host but
# not necessarily the entire response that can be fragmented in
# many packets.
# To avoid the splitted responses to be recognized as invalid
# messages and to be discarded, loops socket.recv until full data
# is received or timeout is expired.
# If timeout expires returns the read data, also if its length is
# less than the expected size.
self.socket.setblocking(0)
begin = time.time()

data = b''
if size is not None:
while len(data) < size:
try:
data += self.socket.recv(size - len(data))
except socket.error:
pass
if not self.timeout or (time.time() - begin > self.timeout):
break
timeout = self.timeout

# If size isn't specified read 1 byte at a time.
if size is None:
recv_size = 1
else:
while True:
try:
data += self.socket.recv(1)
except socket.error:
pass
if not self.timeout or (time.time() - begin > self.timeout):
break
recv_size = size

data = b''
begin = time.time()
while recv_size > 0:
ready = select.select([self.socket], [], [], timeout)
if ready[0]:
data += self.socket.recv(recv_size)

# If size isn't specified continue to read until timeout expires.
if size:
recv_size = size - len(data)

# Timeout is reduced also if some data has been received in order
# to avoid infinite loops when there isn't an expected response size
# and the slave sends noisy data continuosly.
timeout -= time.time() - begin
if timeout <= 0:
break

return data

def is_socket_open(self):
Expand Down
13 changes: 8 additions & 5 deletions pymodbus/datastore/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self, *args, **kwargs):
self.store['c'] = kwargs.get('co', ModbusSequentialDataBlock.create())
self.store['i'] = kwargs.get('ir', ModbusSequentialDataBlock.create())
self.store['h'] = kwargs.get('hr', ModbusSequentialDataBlock.create())
self.zero_mode = kwargs.get('zero_mode', Defaults.ZeroMode)
self.zero_mode = kwargs.get('zero_mode', Defaults.ZeroMode)

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

def getValues(self, fx, address, count=1):
''' Validates the request to make sure it is in range
''' Get `count` values from datastore
:param fx: The function we are working with
:param address: The starting address
:param count: The number of values to retrieve
:returns: The requested values from a:a+c
'''
if not self.zero_mode: address = address + 1
if not self.zero_mode:
address = address + 1
_logger.debug("getValues[%d] %d:%d" % (fx, address, count))
return self.store[self.decode(fx)].getValues(address, count)

Expand All @@ -81,7 +83,8 @@ def setValues(self, fx, address, values):
:param address: The starting address
:param values: The new values to be set
'''
if not self.zero_mode: address = address + 1
if not self.zero_mode:
address = address + 1
_logger.debug("setValues[%d] %d:%d" % (fx, address, len(values)))
self.store[self.decode(fx)].setValues(address, values)

Expand Down
38 changes: 19 additions & 19 deletions pymodbus/datastore/database/redis_datastore.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#---------------------------------------------------------------------------#
# Logging
#---------------------------------------------------------------------------#
import logging;
import logging
_logger = logging.getLogger(__name__)


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

def getValues(self, fx, address, count=1):
''' Validates the request to make sure it is in range
''' Get `count` values from datastore
:param fx: The function we are working with
:param address: The starting address
Expand Down Expand Up @@ -94,28 +94,28 @@ def _build_mapping(self):
code mapper.
'''
self._val_callbacks = {
'd' : lambda o, c: self._val_bit('d', o, c),
'c' : lambda o, c: self._val_bit('c', o, c),
'h' : lambda o, c: self._val_reg('h', o, c),
'i' : lambda o, c: self._val_reg('i', o, c),
'd': lambda o, c: self._val_bit('d', o, c),
'c': lambda o, c: self._val_bit('c', o, c),
'h': lambda o, c: self._val_reg('h', o, c),
'i': lambda o, c: self._val_reg('i', o, c),
}
self._get_callbacks = {
'd' : lambda o, c: self._get_bit('d', o, c),
'c' : lambda o, c: self._get_bit('c', o, c),
'h' : lambda o, c: self._get_reg('h', o, c),
'i' : lambda o, c: self._get_reg('i', o, c),
'd': lambda o, c: self._get_bit('d', o, c),
'c': lambda o, c: self._get_bit('c', o, c),
'h': lambda o, c: self._get_reg('h', o, c),
'i': lambda o, c: self._get_reg('i', o, c),
}
self._set_callbacks = {
'd' : lambda o, v: self._set_bit('d', o, v),
'c' : lambda o, v: self._set_bit('c', o, v),
'h' : lambda o, v: self._set_reg('h', o, v),
'i' : lambda o, v: self._set_reg('i', o, v),
'd': lambda o, v: self._set_bit('d', o, v),
'c': lambda o, v: self._set_bit('c', o, v),
'h': lambda o, v: self._set_reg('h', o, v),
'i': lambda o, v: self._set_reg('i', o, v),
}

#--------------------------------------------------------------------------#
# Redis discrete implementation
#--------------------------------------------------------------------------#
_bit_size = 16
_bit_size = 16
_bit_default = '\x00' * (_bit_size % 8)

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

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

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

key = self._get_prefix(key)
request = ('%s:%s' % (key, v) for v in range(s, e + 1))
Expand All @@ -183,7 +183,7 @@ def _set_bit(self, key, offset, values):
#--------------------------------------------------------------------------#
# Redis register implementation
#--------------------------------------------------------------------------#
_reg_size = 16
_reg_size = 16
_reg_default = '\x00' * (_reg_size % 8)

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

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

Expand Down
Loading

0 comments on commit c303e33

Please sign in to comment.