Skip to content

Commit c04be45

Browse files
feat: Re-start threads after automatically die (#10)
* fix: Solve minor issues and reorder imports * feat: re-start threads after die * feat: update py related files * refactor: apply flake8 suggestions
1 parent b14fba9 commit c04be45

8 files changed

Lines changed: 196 additions & 193 deletions

File tree

.pylintrc

Lines changed: 0 additions & 157 deletions
This file was deleted.

core/observers/subject/eye_subject.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
Concretes a subject for Eye/Camera features.
44
"""
55
import os
6-
from concurrent.futures import ThreadPoolExecutor
6+
from concurrent.futures import Future, ThreadPoolExecutor
77
from datetime import datetime
88
from threading import Lock
9-
from time import sleep
9+
from time import sleep, time
1010
from typing import Optional
1111

1212
import cv2
@@ -37,6 +37,11 @@ def __init__(self, image_path: str = DEFAULT_IMAGE_LOCATIONS):
3737
else os.path.expanduser(image_path)
3838
)
3939

40+
# To run the eye after thread dies.
41+
self.thread: Optional[Future] = None
42+
self._eye_strategy: Optional[BaseEyeStrategy] = None
43+
self._wifi_lock: Optional[Lock] = None
44+
4045
# Create the default image directory if not exists.
4146
os.makedirs(self._image_path, exist_ok=True)
4247

@@ -45,26 +50,21 @@ def get_default_state() -> EyeStates:
4550
"""This method is called when the observer is updated."""
4651
return EyeStates.UNREACHABLE
4752

48-
def _cb_save(self, future) -> None:
49-
"""This method is called when the observer is updated."""
50-
logger.warning("[EyeSubject] The thread died.")
51-
# Create a txt file to indicate the thread died.
52-
file_location = "eyesubject_thread_died.txt"
53-
with open(file_location, "w", encoding="utf-8") as file:
54-
file.write("The thread died.")
55-
logger.warning("[EyeSubject] The thread died. A file is created at %s.",
56-
file_location)
57-
5853
def run(self,
5954
eye_strategy: BaseEyeStrategy,
6055
wifi_lock: Optional[Lock] = None
6156
) -> None:
6257
"""This method is called when the observer is updated."""
63-
thread = ThreadPoolExecutor(
58+
# Update the latest configurations.
59+
self._eye_strategy = eye_strategy
60+
self._wifi_lock = wifi_lock
61+
62+
# Run the thread.
63+
self.thread = ThreadPoolExecutor(
6464
max_workers=1,
6565
thread_name_prefix="eyesubject"
6666
).submit(self._run_in_loop, self, eye_strategy, wifi_lock)
67-
thread.add_done_callback(self._cb_save)
67+
self.thread.add_done_callback(self._cb_done)
6868

6969
@staticmethod
7070
def _run_in_loop(self,
@@ -115,3 +115,14 @@ def _save_image(self, result: EyeStrategyResult) -> None:
115115
cv2.imwrite(file_location, result.image)
116116
logger.debug("[EyeSubject] Image saved to the disk with name: intruder_%s.jpg",
117117
time_now)
118+
119+
def _cb_done(self, future) -> None:
120+
"""This method is called when the observer is updated."""
121+
logger.warning("[EyeSubject] The thread died.")
122+
# Create a txt file to indicate the thread died.
123+
file_location = "thread_die.txt"
124+
with open(file_location, "a", encoding="utf-8") as file:
125+
file.write(f"The EyeSubject thread died. Time: {time()}")
126+
127+
# Start the thread again.
128+
self.run(self._eye_strategy, self._wifi_lock)

core/observers/subject/wifi_subject.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
This class inherits from IBaseSubject.
33
Concretes a subject WiFi features.
44
"""
5-
from concurrent.futures import ThreadPoolExecutor
5+
from concurrent.futures import Future, ThreadPoolExecutor
66
from threading import Lock
7-
from time import sleep
7+
from time import sleep, time
88
from typing import Optional
99

1010
from core.observers.subject.base_subject import BaseSubject
@@ -24,27 +24,28 @@ class WiFiSubject(BaseSubject):
2424
SINGLETON_LOCK: Optional[Lock] = None
2525
CHECK_INTERVAL: int = 5
2626

27+
def __init__(self):
28+
super().__init__()
29+
# To run the WiFi after thread dies.
30+
self.thread: Optional[Future] = None
31+
self._wifi_strategy: Optional[BaseWiFiStrategy] = None
32+
2733
@staticmethod
2834
def get_default_state() -> WiFiStates:
2935
"""This method is called when the observer is updated."""
3036
return WiFiStates.UNREACHABLE
3137

32-
def _cb_save(self, future) -> None:
33-
"""This method is called when the observer is updated."""
34-
logger.warning("[WiFiSubject] The thread died.")
35-
file_location = "wifisubject_thread_died.txt"
36-
with open(file_location, "w", encoding="utf-8") as file:
37-
file.write("The thread died.")
38-
logger.warning("[WiFiSubject] The thread died. A file is created at %s.",
39-
file_location)
40-
4138
def run(self, wifi_strategy: BaseWiFiStrategy) -> None:
4239
"""This method is called when the observer is updated."""
43-
thread = ThreadPoolExecutor(
40+
# Update the latest configurations.
41+
self._wifi_strategy = wifi_strategy
42+
43+
# Start the thread.
44+
self.thread = ThreadPoolExecutor(
4445
max_workers=1,
4546
thread_name_prefix="wifisubject"
4647
).submit(self._run_in_loop, self, wifi_strategy)
47-
thread.add_done_callback(self._cb_save)
48+
self.thread.add_done_callback(self._cb_done)
4849

4950
@classmethod
5051
def get_protector_lock(cls) -> Lock:
@@ -80,3 +81,13 @@ def _run_in_loop(self, wifi_strategy: BaseWiFiStrategy) -> None:
8081
protector_lock.release()
8182
logger.debug("[WiFiSubject] Protector lock is released.")
8283
sleep(self.CHECK_INTERVAL)
84+
85+
def _cb_done(self, future) -> None:
86+
"""This method is called when the observer is updated."""
87+
logger.warning("[WiFiSubject] The thread died.")
88+
file_location = "thread_die.txt"
89+
with open(file_location, "a", encoding="utf-8") as file:
90+
file.write(f"The WiFiSubject thread died. Time: {time()}")
91+
92+
# Run the thread again.
93+
self.run(self._wifi_strategy)

core/utils/logger.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def get_logger(name: str) -> logging.Logger:
1818
log_dir: str = os.path.dirname(LOG_PATH)
1919
if not os.path.exists(log_dir):
2020
os.makedirs(log_dir)
21-
2221
# Configure the log messages.
2322
formatter: logging.Formatter = logging.Formatter(
2423
fmt='[%(asctime)s] -- [%(levelname)s] -- %(name)s (%(funcName)s): %(message)s',

main.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44
import json
55
import sys
6+
from concurrent.futures import wait
67
from typing import Any
78

89
from core.observers.observer.hss_observer import HomeSecuritySystemObserver
@@ -64,6 +65,19 @@ def main():
6465
camera.set_detector(EfficientdetStrategy())
6566
eye_subject.run(camera, wifi_subject.get_protector_lock())
6667

68+
# Notify that the system is running.
69+
whatsapp_notifier.notify_all("Home Security System is started.")
70+
71+
# Wait for the futures.
72+
_, failures = wait([wifi_subject.thread, eye_subject.thread])
73+
for failure in failures:
74+
whatsapp_notifier.notify_all("Home Security System has failed to run. "
75+
"Please check the logs.")
76+
whatsapp_notifier.notify_all("Error: " + str(failure.exception()))
77+
whatsapp_notifier.notify_all("Result: " + str(failure.result()))
78+
# Close the application to let systemd re-start it.
79+
sys.exit(1)
80+
6781

6882
if __name__ == "__main__":
6983
try:

0 commit comments

Comments
 (0)