-
Notifications
You must be signed in to change notification settings - Fork 46
Qos mqtt compatibility #238
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| from time import sleep | ||
| from wirepas_gateway.protocol.message_cache import MessageCache | ||
|
|
||
| CACHE_TIME_WINDOW_S = 0.1 | ||
| CACHE_UPDATE_S = 0.025 | ||
| REQ_ID = 160 | ||
| REQ_ID2 = 161 | ||
|
|
||
|
|
||
| def test_adding_msg(): | ||
| """ | ||
| Tests the adding of message in the cache. | ||
| """ | ||
| message_cache = MessageCache(CACHE_TIME_WINDOW_S, CACHE_UPDATE_S) | ||
| assert message_cache.get_size() == 0 | ||
|
|
||
| assert message_cache.add_msg(REQ_ID) is True | ||
| assert message_cache.get_size() == 1 | ||
|
|
||
| assert message_cache.add_msg(REQ_ID2) is True | ||
| assert message_cache.get_size() == 2 | ||
|
|
||
| # Test the mapping in the cache | ||
| assert message_cache.is_in_cache(REQ_ID) | ||
| assert message_cache.is_in_cache(REQ_ID2) | ||
|
|
||
|
|
||
| def test_duplicate(): | ||
| """ | ||
| Tests if the duplicate messages are not stored | ||
| """ | ||
| message_cache = MessageCache(CACHE_TIME_WINDOW_S, CACHE_UPDATE_S) | ||
| assert message_cache.get_size() == 0 | ||
|
|
||
| assert message_cache.add_msg(REQ_ID) is True | ||
| assert message_cache.get_size() == 1 | ||
|
|
||
| # Test if a redundant message is dropped | ||
| assert message_cache.add_msg(REQ_ID) is False | ||
| assert message_cache.get_size() == 1 | ||
|
|
||
|
|
||
| def test_cache_time_window(): | ||
| """ | ||
| Tests the well functionning of cache time window. | ||
| """ | ||
| message_cache = MessageCache(CACHE_TIME_WINDOW_S, CACHE_UPDATE_S) | ||
| assert message_cache.add_msg(REQ_ID) is True | ||
|
|
||
| # Test if the cache has reset eventually | ||
| sleep(CACHE_TIME_WINDOW_S/2) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Black would make changes. |
||
| assert message_cache.is_in_cache(REQ_ID) is True | ||
| sleep(CACHE_TIME_WINDOW_S) | ||
| assert message_cache.is_in_cache(REQ_ID) is False | ||
|
|
||
| # And that a message with the same id can be added after the time window | ||
| assert message_cache.add_msg(REQ_ID) is True | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # Copyright 2022 Wirepas Ltd licensed under Apache License, Version 2.0 | ||
| # | ||
| # See file LICENSE for full license details. | ||
| # | ||
| from time import time, sleep | ||
| from threading import Lock, Thread | ||
|
|
||
|
|
||
| class MessageCache: | ||
| """ | ||
| Class to cache all recent messages to avoid sending duplicates in MQTT. | ||
| """ | ||
| def __init__( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Black would make changes. |
||
| self, | ||
| cache_time_window_s, | ||
| cache_update_s | ||
| ): | ||
| """ | ||
| Args: | ||
| cache_time_window_s: time in seconds after which a message is removed from the cache. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (97 > 79 characters) |
||
| cache_update_s: period in seconds to update the list of received messages. | ||
| Must be shorter than cache_time_window_s | ||
| """ | ||
| self._lock = Lock() | ||
| # Dictionary of received messages id mapped to their timestamps | ||
| self._cache = dict() | ||
| # last time an update of the list of received messages was done | ||
| self._last_update_time = 0 | ||
|
|
||
| self.cache_time_window_s = cache_time_window_s | ||
| self.cache_update_s = min(cache_update_s, cache_time_window_s) | ||
|
|
||
| # Start a thread that cleans periodically the cache. | ||
| self._clean_cache_thread = Thread(target=self._clean_cache_thread, daemon=True) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. line too long (87 > 79 characters) |
||
| self._clean_cache_thread.start() | ||
|
|
||
| def add_msg(self, msg_id): | ||
| """ | ||
| Adds a received message to the cache. If the message is a duplicate, | ||
| it only updates its last timestamp to the current time. | ||
|
|
||
| Args: | ||
| msg_id: the id of a message to be added to the cache. | ||
| """ | ||
| current_time = time() | ||
| is_duplicate = self.is_in_cache(msg_id) | ||
| with self._lock: | ||
| self._cache[msg_id] = current_time | ||
| return not is_duplicate | ||
|
|
||
| def get_size(self): | ||
| """ | ||
| Returns the number of messages in the cache. | ||
| """ | ||
| with self._lock: | ||
| return len(self._cache) | ||
|
|
||
| def _clean_cache(self): | ||
| """ | ||
| Clean the old messages in the cache | ||
| based on the time window cache_time_window_s attribute. | ||
| """ | ||
| with self._lock: | ||
| current_time = time() | ||
| self._cache = { | ||
| msg_id: msg_time for (msg_id, msg_time) in self._cache.items() | ||
| if current_time - msg_time <= self.cache_time_window_s | ||
| } | ||
| self._last_update_time = current_time | ||
|
|
||
| def _clean_cache_thread(self): | ||
| """ | ||
| Clean the cache and sleep cache_update_s seconds in loop. | ||
| """ | ||
| while True: | ||
| self._clean_cache() | ||
| sleep(self.cache_update_s) | ||
|
|
||
| def is_in_cache(self, msg_id): | ||
| """ | ||
| Return True if the id of a message is already in the cache, False otherwise. | ||
|
|
||
| Args: | ||
| msg_id: id of the message | ||
| """ | ||
| with self._lock: | ||
| return msg_id in self._cache | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Black would make changes.