From 4b56fa3bfa5b7f888fbc2122cdf4ed65cd17275c Mon Sep 17 00:00:00 2001 From: Piergiorgio Pancino Date: Fri, 30 Aug 2019 09:57:05 +0200 Subject: [PATCH 1/4] server: allow discriminating responses upon requests's starting address --- umodbus/functions.py | 18 ++++++++++-------- umodbus/route.py | 16 +++++++++------- umodbus/server/__init__.py | 4 ++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/umodbus/functions.py b/umodbus/functions.py index fff53ac..22b5bf2 100644 --- a/umodbus/functions.py +++ b/umodbus/functions.py @@ -362,7 +362,7 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address) + address, self.starting_address) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -575,7 +575,7 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address) + address, self.starting_address) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -755,7 +755,7 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address) + address, self.starting_address) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -933,7 +933,7 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address) + address, self.starting_address) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -1093,7 +1093,8 @@ def execute(self, slave_id, route_map): :param slave_id: Slave id. :param eindpoint: Instance of modbus.route.Map. """ - endpoint = route_map.match(slave_id, self.function_code, self.address) + starting_address = self.address + endpoint = route_map.match(slave_id, self.function_code, self.address, starting_address) try: endpoint(slave_id=slave_id, address=self.address, value=self.value, function_code=self.function_code) @@ -1237,7 +1238,8 @@ def execute(self, slave_id, route_map): :param slave_id: Slave id. :param eindpoint: Instance of modbus.route.Map. """ - endpoint = route_map.match(slave_id, self.function_code, self.address) + starting_address = self.address + endpoint = route_map.match(slave_id, self.function_code, self.address, starting_address) try: endpoint(slave_id=slave_id, address=self.address, value=self.value, function_code=self.function_code) @@ -1453,7 +1455,7 @@ def execute(self, slave_id, route_map): """ for index, value in enumerate(self.values): address = self.starting_address + index - endpoint = route_map.match(slave_id, self.function_code, address) + endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address) try: endpoint(slave_id=slave_id, address=address, value=value, @@ -1605,7 +1607,7 @@ def execute(self, slave_id, route_map): """ for index, value in enumerate(self.values): address = self.starting_address + index - endpoint = route_map.match(slave_id, self.function_code, address) + endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address) try: endpoint(slave_id=slave_id, address=address, value=value, diff --git a/umodbus/route.py b/umodbus/route.py index b95e6f4..7d204fc 100644 --- a/umodbus/route.py +++ b/umodbus/route.py @@ -2,26 +2,28 @@ class Map: def __init__(self): self._rules = [] - def add_rule(self, endpoint, slave_ids, function_codes, addresses): + def add_rule(self, endpoint, slave_ids, function_codes, addresses, starting_address=None): self._rules.append(DataRule(endpoint, slave_ids, function_codes, - addresses)) + addresses, starting_address)) - def match(self, slave_id, function_code, address): + def match(self, slave_id, function_code, address, starting_address): for rule in self._rules: - if rule.match(slave_id, function_code, address): + if rule.match(slave_id, function_code, address, starting_address): return rule.endpoint class DataRule: - def __init__(self, endpoint, slave_ids, function_codes, addresses): + def __init__(self, endpoint, slave_ids, function_codes, addresses, starting_address): self.endpoint = endpoint self.slave_ids = slave_ids self.function_codes = function_codes self.addresses = addresses + self.starting_address = starting_address - def match(self, slave_id, function_code, address): - if slave_id in self.slave_ids and\ + def match(self, slave_id, function_code, address, starting_address): + if slave_id in self.slave_ids and \ function_code in self.function_codes and \ + (True if self.starting_address is None else starting_address == self.starting_address) and \ address in self.addresses: return True diff --git a/umodbus/server/__init__.py b/umodbus/server/__init__.py index 842e455..88b2a0a 100644 --- a/umodbus/server/__init__.py +++ b/umodbus/server/__init__.py @@ -11,7 +11,7 @@ pack_exception_pdu, recv_exactly) -def route(self, slave_ids=None, function_codes=None, addresses=None): +def route(self, slave_ids=None, function_codes=None, addresses=None, starting_address=None): """ A decorator that is used to register an endpoint for a given rule:: @@ -24,7 +24,7 @@ def read_single_bit_values(slave_id, address): :param addresses: A list or set with addresses. """ def inner(f): - self.route_map.add_rule(f, slave_ids, function_codes, addresses) + self.route_map.add_rule(f, slave_ids, function_codes, addresses, starting_address) return f return inner From 850dfce38891cf0ac4bb9dedb257fc6aa3fd1b76 Mon Sep 17 00:00:00 2001 From: Piergiorgio Pancino Date: Thu, 5 Sep 2019 13:58:56 +0200 Subject: [PATCH 2/4] server: allow discriminating responses upon quantity of requested registers --- umodbus/functions.py | 27 +++++++++++++++++++-------- umodbus/route.py | 14 ++++++++------ umodbus/server/__init__.py | 4 ++-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/umodbus/functions.py b/umodbus/functions.py index 22b5bf2..d45ae47 100644 --- a/umodbus/functions.py +++ b/umodbus/functions.py @@ -362,7 +362,8 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address, self.starting_address) + address, self.starting_address, + self.quantity) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -575,7 +576,8 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address, self.starting_address) + address, self.starting_address, + self.quantity) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -755,7 +757,8 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address, self.starting_address) + address, self.starting_address, + self.quantity) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -933,7 +936,8 @@ def execute(self, slave_id, route_map): for address in range(self.starting_address, self.starting_address + self.quantity): endpoint = route_map.match(slave_id, self.function_code, - address, self.starting_address) + address, self.starting_address, + self.quantity) values.append(endpoint(slave_id=slave_id, address=address, function_code=self.function_code)) @@ -1094,7 +1098,9 @@ def execute(self, slave_id, route_map): :param eindpoint: Instance of modbus.route.Map. """ starting_address = self.address - endpoint = route_map.match(slave_id, self.function_code, self.address, starting_address) + quantity = 1 + endpoint = route_map.match(slave_id, self.function_code, self.address, + starting_address, quantity) try: endpoint(slave_id=slave_id, address=self.address, value=self.value, function_code=self.function_code) @@ -1239,7 +1245,9 @@ def execute(self, slave_id, route_map): :param eindpoint: Instance of modbus.route.Map. """ starting_address = self.address - endpoint = route_map.match(slave_id, self.function_code, self.address, starting_address) + quantity = 1 + endpoint = route_map.match(slave_id, self.function_code, self.address, + starting_address, quantity=1) try: endpoint(slave_id=slave_id, address=self.address, value=self.value, function_code=self.function_code) @@ -1455,7 +1463,8 @@ def execute(self, slave_id, route_map): """ for index, value in enumerate(self.values): address = self.starting_address + index - endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address) + endpoint = route_map.match(slave_id, self.function_code, address, + self.starting_address, self.quantity) try: endpoint(slave_id=slave_id, address=address, value=value, @@ -1607,7 +1616,9 @@ def execute(self, slave_id, route_map): """ for index, value in enumerate(self.values): address = self.starting_address + index - endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address) + quantity = len(self.values) + endpoint = route_map.match(slave_id, self.function_code, address, + self.starting_address, quantity) try: endpoint(slave_id=slave_id, address=address, value=value, diff --git a/umodbus/route.py b/umodbus/route.py index 7d204fc..cc94c16 100644 --- a/umodbus/route.py +++ b/umodbus/route.py @@ -2,28 +2,30 @@ class Map: def __init__(self): self._rules = [] - def add_rule(self, endpoint, slave_ids, function_codes, addresses, starting_address=None): + def add_rule(self, endpoint, slave_ids, function_codes, addresses, starting_address=None, quantity=None): self._rules.append(DataRule(endpoint, slave_ids, function_codes, - addresses, starting_address)) + addresses, starting_address, quantity)) - def match(self, slave_id, function_code, address, starting_address): + def match(self, slave_id, function_code, address, starting_address, quantity): for rule in self._rules: - if rule.match(slave_id, function_code, address, starting_address): + if rule.match(slave_id, function_code, address, starting_address, quantity): return rule.endpoint class DataRule: - def __init__(self, endpoint, slave_ids, function_codes, addresses, starting_address): + def __init__(self, endpoint, slave_ids, function_codes, addresses, starting_address, quantity): self.endpoint = endpoint self.slave_ids = slave_ids self.function_codes = function_codes self.addresses = addresses self.starting_address = starting_address + self.quantity = quantity - def match(self, slave_id, function_code, address, starting_address): + def match(self, slave_id, function_code, address, starting_address, quantity): if slave_id in self.slave_ids and \ function_code in self.function_codes and \ (True if self.starting_address is None else starting_address == self.starting_address) and \ + (True if self.quantity is None else quantity == self.quantity) and \ address in self.addresses: return True diff --git a/umodbus/server/__init__.py b/umodbus/server/__init__.py index 88b2a0a..307ef46 100644 --- a/umodbus/server/__init__.py +++ b/umodbus/server/__init__.py @@ -11,7 +11,7 @@ pack_exception_pdu, recv_exactly) -def route(self, slave_ids=None, function_codes=None, addresses=None, starting_address=None): +def route(self, slave_ids=None, function_codes=None, addresses=None, starting_address=None, quantity=None): """ A decorator that is used to register an endpoint for a given rule:: @@ -24,7 +24,7 @@ def read_single_bit_values(slave_id, address): :param addresses: A list or set with addresses. """ def inner(f): - self.route_map.add_rule(f, slave_ids, function_codes, addresses, starting_address) + self.route_map.add_rule(f, slave_ids, function_codes, addresses, starting_address, quantity) return f return inner From 2f70a09ae2a3a5f3c8fcdcf41f53070358838c02 Mon Sep 17 00:00:00 2001 From: Piergiorgio Pancino Date: Thu, 5 Sep 2019 17:13:03 +0200 Subject: [PATCH 3/4] add fit_kwargs function helper to allow passing partial kwargs to app.route --- umodbus/functions.py | 90 ++++++++++++++++++++++++++++++++++++-------- umodbus/route.py | 12 +++--- 2 files changed, 81 insertions(+), 21 deletions(-) diff --git a/umodbus/functions.py b/umodbus/functions.py index d45ae47..1682daa 100644 --- a/umodbus/functions.py +++ b/umodbus/functions.py @@ -95,6 +95,17 @@ GET_COM_EVENT_LOG = 12 REPORT_SERVER_ID = 17 +def fit_kwargs(func, **kwargs): + """ Runs a function removing not wanted kwargs + :param func: function + :param kwargs: key,value arguments + + :return: what original function is supposed to return + """ + if func == None: + raise TypeError + fitted_kwargs = {k:v for k,v in kwargs.items() if k in func.__code__.co_varnames} + return func(**fitted_kwargs) def pdu_to_function_code_or_raise_error(resp_pdu): """ Parse response PDU and return of :class:`ModbusFunction` or @@ -364,8 +375,13 @@ def execute(self, slave_id, route_map): endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address, self.quantity) - values.append(endpoint(slave_id=slave_id, address=address, - function_code=self.function_code)) + all_kwargs= {"slave_id":slave_id, "address":address, + "function_code": self.function_code, + "starting_address":self.starting_address, + "quantity":self.quantity} + + values.append(fit_kwargs(endpoint, **all_kwargs)) + return values @@ -579,7 +595,16 @@ def execute(self, slave_id, route_map): address, self.starting_address, self.quantity) values.append(endpoint(slave_id=slave_id, address=address, - function_code=self.function_code)) + function_code=self.function_code, + starting_address=self.starting_address, + quantity=self.quantity)) + all_kwargs = {"slave_id":slave_id, "address":address, + "function_code": self.function_code, + "starting_address":self.starting_address, + "quantity":self.quantity} + + values.append(fit_kwargs(endpoint, **all_kwargs)) + return values @@ -759,8 +784,13 @@ def execute(self, slave_id, route_map): endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address, self.quantity) - values.append(endpoint(slave_id=slave_id, address=address, - function_code=self.function_code)) + all_kwargs= {"slave_id":slave_id, "address":address, + "function_code": self.function_code, + "starting_address":self.starting_address, + "quantity":self.quantity} + + values.append(fit_kwargs(endpoint, **all_kwargs)) + return values @@ -938,8 +968,12 @@ def execute(self, slave_id, route_map): endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address, self.quantity) - values.append(endpoint(slave_id=slave_id, address=address, - function_code=self.function_code)) + all_kwargs= {"slave_id":slave_id, "address":address, + "function_code": self.function_code, + "starting_address":self.starting_address, + "quantity":self.quantity} + + values.append(fit_kwargs(endpoint, **all_kwargs)) return values @@ -1102,8 +1136,15 @@ def execute(self, slave_id, route_map): endpoint = route_map.match(slave_id, self.function_code, self.address, starting_address, quantity) try: - endpoint(slave_id=slave_id, address=self.address, value=self.value, - function_code=self.function_code) + all_kwargs= {"slave_id":slave_id, "address":self.address, + "value":self.value, + "function_code": self.function_code, + "starting_address":starting_address, + "quantity":quantity} + + fit_kwargs(endpoint, **all_kwargs) + + # route_map.match() returns None if no match is found. Calling None # results in TypeError. except TypeError: @@ -1249,8 +1290,15 @@ def execute(self, slave_id, route_map): endpoint = route_map.match(slave_id, self.function_code, self.address, starting_address, quantity=1) try: - endpoint(slave_id=slave_id, address=self.address, value=self.value, - function_code=self.function_code) + all_kwargs= {"slave_id":slave_id, "address":self.address, + "value":self.value, + "function_code": self.function_code, + "starting_address":starting_address, + "quantity":quantity} + + fit_kwargs(endpoint, **all_kwargs) + + # route_map.match() returns None if no match is found. Calling None # results in TypeError. except TypeError: @@ -1467,8 +1515,14 @@ def execute(self, slave_id, route_map): self.starting_address, self.quantity) try: - endpoint(slave_id=slave_id, address=address, value=value, - function_code=self.function_code) + all_kwargs= {"slave_id":slave_id, "address":address, + "value":value, + "function_code": self.function_code, + "starting_address":self.starting_address, + "quantity":self.quantity} + + fit_kwargs(endpoint, **all_kwargs) + # route_map.match() returns None if no match is found. Calling None # results in TypeError. except TypeError: @@ -1621,8 +1675,14 @@ def execute(self, slave_id, route_map): self.starting_address, quantity) try: - endpoint(slave_id=slave_id, address=address, value=value, - function_code=self.function_code) + all_kwargs = {"slave_id":slave_id, "address":address, + "value":value, + "function_code": self.function_code, + "starting_address":self.starting_address, + "quantity":quantity} + + fit_kwargs(endpoint, **all_kwargs) + # route_map.match() returns None if no match is found. Calling None # results in TypeError. except TypeError: diff --git a/umodbus/route.py b/umodbus/route.py index cc94c16..8c1483a 100644 --- a/umodbus/route.py +++ b/umodbus/route.py @@ -2,9 +2,9 @@ class Map: def __init__(self): self._rules = [] - def add_rule(self, endpoint, slave_ids, function_codes, addresses, starting_address=None, quantity=None): + def add_rule(self, endpoint, slave_ids, function_codes, addresses, starting_address=None, quantities=None): self._rules.append(DataRule(endpoint, slave_ids, function_codes, - addresses, starting_address, quantity)) + addresses, starting_address, quantities)) def match(self, slave_id, function_code, address, starting_address, quantity): for rule in self._rules: @@ -13,20 +13,20 @@ def match(self, slave_id, function_code, address, starting_address, quantity): class DataRule: - def __init__(self, endpoint, slave_ids, function_codes, addresses, starting_address, quantity): + def __init__(self, endpoint, slave_ids, function_codes, addresses, starting_address, quantities): self.endpoint = endpoint self.slave_ids = slave_ids self.function_codes = function_codes self.addresses = addresses self.starting_address = starting_address - self.quantity = quantity + self.quantities = quantities def match(self, slave_id, function_code, address, starting_address, quantity): if slave_id in self.slave_ids and \ function_code in self.function_codes and \ (True if self.starting_address is None else starting_address == self.starting_address) and \ - (True if self.quantity is None else quantity == self.quantity) and \ + (True if self.quantities is None else quantity in self.quantities) and \ address in self.addresses: - return True + return True return False From 9b2e9df8f6b1949b5d09d79899297c47a053cca1 Mon Sep 17 00:00:00 2001 From: Piergiorgio Pancino Date: Thu, 5 Sep 2019 17:27:34 +0200 Subject: [PATCH 4/4] fix ReadDiscreteInput code repetition --- umodbus/functions.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/umodbus/functions.py b/umodbus/functions.py index 1682daa..1719163 100644 --- a/umodbus/functions.py +++ b/umodbus/functions.py @@ -594,10 +594,6 @@ def execute(self, slave_id, route_map): endpoint = route_map.match(slave_id, self.function_code, address, self.starting_address, self.quantity) - values.append(endpoint(slave_id=slave_id, address=address, - function_code=self.function_code, - starting_address=self.starting_address, - quantity=self.quantity)) all_kwargs = {"slave_id":slave_id, "address":address, "function_code": self.function_code, "starting_address":self.starting_address,