added support for multiple monitors and handlers
This commit is contained in:
102
core/meow.py
102
core/meow.py
@ -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]],
|
||||||
|
Reference in New Issue
Block a user