Skip to content

Commit

Permalink
fritzmonitor in working state and tested.
Browse files Browse the repository at this point in the history
  • Loading branch information
kbr committed Nov 28, 2020
1 parent 23ed69a commit 38d3794
Show file tree
Hide file tree
Showing 3 changed files with 304 additions and 9 deletions.
4 changes: 3 additions & 1 deletion docs/sources/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ Version History
1.4.0_dev
---------

- Library class FritzStatus with additional properties: *attenuation*, *str_attenuation*, *noise_margin* and *str_noise_margin* (#69)
- Library class FritzHost with additional method *get_host_name* (#75)
- Namespace prefix for xml-arguments removed (#66)
- Test extended for Python 3.9 (#73)
- Library class FritzStatus has additional properties: *attenuation*, *str_attenuation*, *noise_margin* and *str_noise_margin* (#69).


1.3.4
Expand Down
71 changes: 68 additions & 3 deletions fritzconnection/core/fritzmonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import queue
import socket
import threading
import time


FRITZ_IP_ADDRESS = "169.254.1.1"
Expand All @@ -24,6 +25,9 @@
FRITZ_MONITOR_CHUNK_SIZE = 1024 * 4
FRITZ_MONITOR_SOCKET_TIMEOUT = 10

RECONNECT_DELAY = 60 # time in seconds to wait for retry after connection lost
RECONNECT_TRIES = 5 # number of tries to reconnect before giving up


class EventReporter:
"""
Expand Down Expand Up @@ -81,10 +85,28 @@ def __init__(
self.monitor_thread = None
self.encoding = encoding

@property
def has_monitor_thread(self):
"""
Returns True if a monitor-thread has been created.
That should be the case after calling start() and before calling stop().
"""
return bool(self.monitor_thread)

@property
def is_alive(self):
"""
Returns True if there is a monitor-thread and the thread is running.
Returns False otherwise.
"""
return self.has_monitor_thread and self.monitor_thread.is_alive()

def start(
self,
queue_size=FRITZ_MONITOR_QUEUE_SIZE,
block_on_filled_queue=False,
reconnect_delay=RECONNECT_DELAY,
reconnect_tries=RECONNECT_TRIES,
sock=None,
):
"""
Expand All @@ -104,6 +126,8 @@ def start(
"monitor_queue": monitor_queue,
"sock": sock,
"block_on_filled_queue": block_on_filled_queue,
"reconnect_delay": reconnect_delay,
"reconnect_tries": reconnect_tries,
}
# clear event object in case the instance gets 'reused':
self.stop_flag.clear()
Expand Down Expand Up @@ -137,7 +161,35 @@ def _get_connected_socket(self, sock=None):
raise OSError(msg)
return sock

def _monitor(self, monitor_queue, sock, block_on_filled_queue):
def _reconnect_socket(
self, sock, reconnect_delay=RECONNECT_DELAY, reconnect_tries=RECONNECT_TRIES
):
"""
Try to reconnect a lost connection on the given socket.
Returns True on success and False otherwise.
"""
while reconnect_tries > 0:
time.sleep(reconnect_delay)
try:
self._get_connected_socket(sock)
except OSError:
reconnect_tries -= 1
else:
return True
return False

def _monitor(
self,
monitor_queue,
sock,
block_on_filled_queue,
reconnect_delay,
reconnect_tries,
):
"""
The internal monitor routine running in a separate thread.
"""
# Instantiat an EventReporter to push event to the event_queue.
event_reporter = EventReporter(
monitor_queue=monitor_queue, block_on_filled_queue=block_on_filled_queue
)
Expand All @@ -147,15 +199,28 @@ def _monitor(self, monitor_queue, sock, block_on_filled_queue):
except socket.timeout:
# without a timeout an open socket will never return from a
# connection closed by a router (may be of limited resources).
# Therefore be sure to set a timeout at socket creation (elsewhere).
# So just try again after timeout.
continue
if not raw_data:
# empty response indicates a lost connection.
# try to reconnect.
...
success = self._reconnect_socket(
sock,
reconnect_delay=reconnect_delay,
reconnect_tries=reconnect_tries,
)
if not success:
# reconnet has failed: terminate the thread
break
else:
# sock.recv returns a bytearray to decode:
response = raw_data.decode(self.encoding)
event_reporter.add(response)
# clean up on terminating thread:
sock.close()
try:
sock.close()
except OSError:
pass
# reset monitor_thread to be able to restart the again
self.monitor_thread = None
Loading

0 comments on commit 38d3794

Please sign in to comment.