added support for multiple monitors and handlers

This commit is contained in:
PatchOfScotland
2023-01-13 18:53:02 +01:00
parent 2cd92c04fa
commit ee81b2561e

View File

@ -4,11 +4,12 @@ import sys
import threading import threading
from multiprocessing import Pipe from multiprocessing import Pipe
from random import randrange
from typing import Any, Union from typing import Any, Union
from core.correctness.vars import VALID_RECIPE_NAME_CHARS, \ from core.correctness.vars import VALID_RECIPE_NAME_CHARS, \
VALID_PATTERN_NAME_CHARS, VALID_RULE_NAME_CHARS, VALID_CHANNELS, \ VALID_PATTERN_NAME_CHARS, VALID_RULE_NAME_CHARS, VALID_CHANNELS, \
get_drt_imp_msg, DEBUG_WARNING, DEBUG_INFO get_drt_imp_msg, DEBUG_WARNING, DEBUG_INFO, EVENT_TYPE
from core.correctness.validation import valid_string, check_type, \ from core.correctness.validation import valid_string, check_type, \
check_implementation, valid_list, valid_dict, setup_debugging check_implementation, valid_list, valid_dict, setup_debugging
from core.functionality import print_debug, wait, generate_id from core.functionality import print_debug, wait, generate_id
@ -175,41 +176,73 @@ class BaseHandler:
pass pass
# TODO reformat to allow for updated monitor / handler interaction
# TODO expand this to allow for lists of monitors / handlers
class MeowRunner: class MeowRunner:
monitor:BaseMonitor monitors:list[BaseMonitor]
handler:BaseHandler handlers:dict[str:BaseHandler]
from_monitor: VALID_CHANNELS from_monitor: list[VALID_CHANNELS]
def __init__(self, monitor:BaseMonitor, handler:BaseHandler, def __init__(self, monitors:Union[BaseMonitor,list[BaseMonitor]],
handlers:Union[BaseHandler,list[BaseHandler]],
print:Any=sys.stdout, logging:int=0) -> None: print:Any=sys.stdout, logging:int=0) -> None:
self._is_valid_monitor(monitor) self._is_valid_handlers(handlers)
self.monitor = monitor if not type(handlers) == list:
monitor_to_runner_reader, monitor_to_runner_writer = Pipe() handlers = [handlers]
self.monitor.to_runner = monitor_to_runner_writer self.handlers = {}
self.from_monitor = monitor_to_runner_reader for handler in handlers:
self._is_valid_handler(handler) handler_events = handler.valid_event_types()
self.handler = handler for event in handler_events:
if event in self.handlers.keys():
self.handlers[event].append(handler)
else:
self.handlers[event] = [handler]
self._is_valid_monitors(monitors)
if not type(monitors) == list:
monitors = [monitors]
self.monitors = monitors
self.from_monitors = []
for monitor in self.monitors:
monitor_to_runner_reader, monitor_to_runner_writer = Pipe()
monitor.to_runner = monitor_to_runner_writer
self.from_monitors.append(monitor_to_runner_reader)
self._stop_pipe = Pipe() self._stop_pipe = Pipe()
self._worker = None self._worker = None
self._print_target, self.debug_level = setup_debugging(print, logging) self._print_target, self.debug_level = setup_debugging(print, logging)
def run(self)->None: def run(self)->None:
all_inputs = [self.from_monitor, self._stop_pipe[0]] all_inputs = self.from_monitors + [self._stop_pipe[0]]
while True: while True:
ready = wait(all_inputs) ready = wait(all_inputs)
if self._stop_pipe[0] in ready: if self._stop_pipe[0] in ready:
return return
else: else:
message = self.from_monitor.recv() for from_monitor in self.from_monitors:
event = message if from_monitor in ready:
self.handler.handle(event) message = from_monitor.recv()
event = message
if not self.handlers[event[EVENT_TYPE]]:
print_debug(self._print_target, self.debug_level,
"Could not process event as no relevent "
f"handler for '{EVENT_TYPE}'", DEBUG_INFO)
return
if len(self.handlers[event[EVENT_TYPE]]) == 1:
self.handlers[event[EVENT_TYPE]][0].handle(event)
else:
self.handlers[event[EVENT_TYPE]][
randrange(len(self.handlers[event[EVENT_TYPE]]))
].handle(event)
def start(self)->None: def start(self)->None:
self.monitor.start() for monitor in self.monitors:
if hasattr(self.handler, "start"): monitor.start()
self.handler.start() startable = []
for handler_list in self.handlers.values():
for handler in handler_list:
if hasattr(handler, "start") and handler not in startable:
startable.append()
for handler in startable:
handler.start()
if self._worker is None: if self._worker is None:
self._worker = threading.Thread( self._worker = threading.Thread(
@ -227,9 +260,16 @@ class MeowRunner:
def stop(self)->None: def stop(self)->None:
self.monitor.stop() for monitor in self.monitors:
if hasattr(self.handler, "stop"): monitor.stop()
self.handler.stop()
stopable = []
for handler_list in self.handlers.values():
for handler in handler_list:
if hasattr(handler, "stop") and handler not in stopable:
stopable.append()
for handler in stopable:
handler.stop()
if self._worker is None: if self._worker is None:
msg = "Cannot stop thread that is not started." msg = "Cannot stop thread that is not started."
@ -243,11 +283,17 @@ class MeowRunner:
"Worker thread stopped", DEBUG_INFO) "Worker thread stopped", DEBUG_INFO)
def _is_valid_monitor(self, monitor:BaseMonitor)->None: def _is_valid_monitors(self,
check_type(monitor, BaseMonitor) monitors:Union[BaseMonitor,list[BaseMonitor]])->None:
check_type(monitors, BaseMonitor, alt_types=[list[BaseMonitor]])
if type(monitors) == list:
valid_list(monitors, BaseMonitor, min_length=1)
def _is_valid_handler(self, handler:BaseHandler)->None: def _is_valid_handlers(self,
check_type(handler, BaseHandler) handlers:Union[BaseHandler,list[BaseHandler]])->None:
check_type(handlers, BaseHandler, alt_types=[list[BaseHandler]])
if type(handlers) == list:
valid_list(handlers, BaseHandler, min_length=1)
def create_rules(patterns:Union[dict[str,BasePattern],list[BasePattern]], def create_rules(patterns:Union[dict[str,BasePattern],list[BasePattern]],
recipes:Union[dict[str,BaseRecipe],list[BaseRecipe]], recipes:Union[dict[str,BaseRecipe],list[BaseRecipe]],