Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove caching to stop memory leak #129

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 9 additions & 24 deletions umodbus/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
from umodbus.exceptions import (error_code_to_exception_map,
IllegalDataValueError, IllegalFunctionError,
IllegalDataAddressError)
from umodbus.utils import memoize, get_function_code_from_request_pdu
from umodbus.utils import get_function_code_from_request_pdu

# Function related to data access.
READ_COILS = 1
Expand Down Expand Up @@ -111,10 +111,10 @@ def pdu_to_function_code_or_raise_error(resp_pdu):
:return: Subclass of :class:`ModbusFunction` matching the response.
:raises ModbusError: When response contains error code.
"""
function_code = struct.unpack('>B', resp_pdu[0:1])[0]
function_code = resp_pdu[0]

if function_code not in function_code_to_function_map.keys():
error_code = struct.unpack('>B', resp_pdu[1:2])[0]
error_code = resp_pdu[1]
raise error_code_to_exception_map[error_code]

return function_code
Expand All @@ -140,7 +140,6 @@ def create_function_from_response_pdu(resp_pdu, req_pdu=None):
return function.create_from_response_pdu(resp_pdu)


@memoize
def create_function_from_request_pdu(pdu):
""" Return function instance, based on request PDU.

Expand Down Expand Up @@ -317,12 +316,7 @@ def create_response_pdu(self, data):
reduce(lambda a, b: (a << 1) + b, list(reversed(byte)))

log.debug('Reduced single bit data to {0}.'.format(bytes_))
# The first 2 B's of the format encode the function code (1 byte) and
# the length (1 byte) of the following byte series. Followed by # a B
# for every byte in the series of bytes. 3 lead to the format '>BBB'
# and 257 lead to the format '>BBBB'.
fmt = '>BB' + self.format_character * len(bytes_)
return struct.pack(fmt, self.function_code, len(bytes_), *bytes_)
return bytes([self.function_code, len(bytes_), *bytes_])

@classmethod
def create_from_response_pdu(cls, resp_pdu, req_pdu):
Expand All @@ -336,10 +330,8 @@ def create_from_response_pdu(cls, resp_pdu, req_pdu):
"""
read_coils = cls()
read_coils.quantity = struct.unpack('>H', req_pdu[-2:])[0]
byte_count = struct.unpack('>B', resp_pdu[1:2])[0]

fmt = '>' + ('B' * byte_count)
bytes_ = struct.unpack(fmt, resp_pdu[2:])
byte_count = resp_pdu[1]
bytes_ = resp_pdu[2:]

data = list()

Expand Down Expand Up @@ -525,12 +517,7 @@ def create_response_pdu(self, data):
reduce(lambda a, b: (a << 1) + b, list(reversed(byte)))

log.debug('Reduced single bit data to {0}.'.format(bytes_))
# The first 2 B's of the format encode the function code (1 byte) and
# the length (1 byte) of the following byte series. Followed by # a B
# for every byte in the series of bytes. 3 lead to the format '>BBB'
# and 257 lead to the format '>BBBB'.
fmt = '>BB' + self.format_character * len(bytes_)
return struct.pack(fmt, self.function_code, len(bytes_), *bytes_)
return bytes([self.function_code, len(bytes_), *bytes_])

@classmethod
def create_from_response_pdu(cls, resp_pdu, req_pdu):
Expand All @@ -544,10 +531,8 @@ def create_from_response_pdu(cls, resp_pdu, req_pdu):
"""
read_discrete_inputs = cls()
read_discrete_inputs.quantity = struct.unpack('>H', req_pdu[-2:])[0]
byte_count = struct.unpack('>B', resp_pdu[1:2])[0]

fmt = '>' + ('B' * byte_count)
bytes_ = struct.unpack(fmt, resp_pdu[2:])
byte_count = resp_pdu[1]
bytes_ = resp_pdu[2:]

data = list()

Expand Down
18 changes: 14 additions & 4 deletions umodbus/route.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ def add_rule(self, endpoint, slave_ids, function_codes, addresses):
addresses))

def match(self, slave_id, function_code, address):
if not self._rules[0].match_slave_id(slave_id):
return False

for rule in self._rules:
if rule.match(slave_id, function_code, address):
if rule.match(function_code, address):
return rule.endpoint


Expand All @@ -19,9 +22,16 @@ def __init__(self, endpoint, slave_ids, function_codes, addresses):
self.function_codes = function_codes
self.addresses = addresses

def match(self, slave_id, function_code, address):
def match(self, function_code, address):
# A constraint of None matches any value
matches = lambda values, v: values is None or v in values
return matches(self.slave_ids, slave_id) and \
matches(self.function_codes, function_code) and \

return matches(self.function_codes, function_code) and \
matches(self.addresses, address)

def match_slave_id(self, slave_id):
matches = lambda values, v: values is None or v in values

if matches(self.slave_ids, slave_id):
return True
return False
5 changes: 5 additions & 0 deletions umodbus/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def process(self, request_adu):
request_pdu = self.get_request_pdu(request_adu)

response_pdu = self.execute_route(meta_data, request_pdu)
if response_pdu is False:
return False
response_adu = self.create_response_adu(meta_data, response_pdu)

return response_adu
Expand All @@ -80,6 +82,9 @@ def execute_route(self, meta_data, request_pdu):
function = create_function_from_request_pdu(request_pdu)
results =\
function.execute(meta_data['unit_id'], self.server.route_map)
# Result is False if request SlaveId do not match modbus server SlaveId
if results is False:
return False

try:
# ReadFunction's use results of callbacks to build response
Expand Down
5 changes: 4 additions & 1 deletion umodbus/server/serial/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ def process(self, request_adu):
request_pdu = self.get_request_pdu(request_adu)

response_pdu = self.execute_route(meta_data, request_pdu)
if response_pdu is False:
return False
response_adu = self.create_response_adu(meta_data, response_pdu)

return response_adu
Expand All @@ -98,7 +100,8 @@ def execute_route(self, meta_data, request_pdu):
function = create_function_from_request_pdu(request_pdu)
results =\
function.execute(meta_data['unit_id'], self.route_map)

if results is False:
return False
try:
# ReadFunction's use results of callbacks to build response
# PDU...
Expand Down
3 changes: 2 additions & 1 deletion umodbus/server/serial/rtu.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def serve_once(self):
raise ValueError

response_adu = self.process(request_adu)
self.respond(response_adu)
if response_adu:
self.respond(response_adu)

def process(self, request_adu):
""" Process request ADU and return response.
Expand Down