diff --git a/solvable/src/custom_event_handlers.py b/solvable/src/custom_event_handlers.py new file mode 100644 index 0000000..c5d0a33 --- /dev/null +++ b/solvable/src/custom_event_handlers.py @@ -0,0 +1,100 @@ +import logging +from ast import literal_eval + +from tfw.components import MessageSender, TerminalCommands +from tfw.builtins import EventHandler, FSMAwareEventHandler, TFWServerUplinkConnector + +LOG = logging.getLogger(__name__) + + +class TerminalCallbackEventHandler(EventHandler): + """ + Logs commands executed in terminal to messages and invokes an + additional callback function to handle special commands. + !! Please remove from production code. !! + """ + def __init__(self, key, callback): + self.callback = callback + super().__init__(key) + + def handle_event(self, message): + command = message['value'] + self.cenator(command) + self.callback(command) + + def cenator(self, command): + LOG.debug('User executed command: "%s"', command) + MessageSender(self.server_connector).send('JOHN CENA', f'You\'ve executed "{command}"') + + +class TestCommands(TerminalCommands): + """ + Some example commands useful for debugging. + !! Please remove from production code !! and inherit your own + class from TerminalCommands if you need to define custom + commands in your challenge. + """ + # pylint: disable=unused-argument, attribute-defined-outside-init, no-self-use + def command_sendmessage(self, *args): + """ + Insert TFW message template as first argument if executed without args. + + Evaluate first argumen as a dict and send it to the frontend. + This is useful for playing around with frontend APIs. + """ + if not args: + message_template = """'{"key": "", "data": {"command": ""}}'""" + TFWServerUplinkConnector().send_message({ + 'key': 'shell', + 'data': { + 'command': 'write', + 'value': f'sendmessage {message_template}' + } + }) + else: + TFWServerUplinkConnector().send_message(literal_eval(args[0])) + + def command_seppuku_tfw(self, *args): + """ + Restart tfw_server.py and event_handler_main.py. + This can speed up development when combined with mounting + volumes from host to container. + """ + seppuku = ( + 'nohup sh -c "supervisorctl restart tfwserver event_handler_main" &> /dev/null & ' + 'clear && echo "Committed seppuku! :)" && sleep infinity' + ) + uplink = TFWServerUplinkConnector() + uplink.send_message({ + 'key': 'shell', + 'data': { + 'command': 'write', + 'value': f'{seppuku}\n' + } + }) + uplink.send_message({ + 'key': 'dashboard', + 'data': { + 'command': 'reloadFrontend' + } + }) + + +class MessageFSMStepsEventHandler(FSMAwareEventHandler): + """ + This example EventHandler is capable of detecting FSM state. + !! Please remove from production code !! + """ + def handle_event(self, message): + pass + + def handle_fsm_step(self, **kwargs): + """ + When the FSM steps this method is invoked. + Receives a 'data' field from an fsm_update message as kwargs. + """ + MessageSender(self.server_connector).send( + 'FSM info', + f'FSM has stepped from state "{kwargs["last_event"]["from_state"]}" ' + f'to state "{kwargs["current_state"]}" in response to trigger "{kwargs["last_event"]["trigger"]}"' + ) diff --git a/solvable/src/event_handler_main.py b/solvable/src/event_handler_main.py index 2b5ae62..bbde28a 100644 --- a/solvable/src/event_handler_main.py +++ b/solvable/src/event_handler_main.py @@ -1,118 +1,25 @@ import logging from sys import stderr -from ast import literal_eval from functools import partial from signal import signal, SIGTERM, SIGINT from tornado.ioloop import IOLoop from tfw.fsm import YamlFSM -from tfw.builtins import EventHandler, FSMAwareEventHandler, TFWServerUplinkConnector +from tfw.builtins import EventHandler from tfw.builtins import IdeEventHandler, TerminalEventHandler, FrontendEventHandler from tfw.builtins import LogMonitoringEventHandler, ProcessManagingEventHandler -from tfw.builtins import DirectorySnapshottingEventHandler, FSMManagingEventHandler, MessageSender -from tfw.components import TerminalCommands +from tfw.builtins import DirectorySnapshottingEventHandler, FSMManagingEventHandler from tfw.config import TFWENV from tfw.logging import Log, Logger, LogFormatter, VerboseLogFormatter from tao.config import TAOENV +from custom_event_handlers import MessageFSMStepsEventHandler +from custom_event_handlers import TerminalCallbackEventHandler, TestCommands + LOG = logging.getLogger(__name__) -class TerminalCallbackEventHandler(EventHandler): - """ - Logs commands executed in terminal to messages and invokes an - additional callback function to handle special commands. - !! Please remove from production code. !! - """ - def __init__(self, key, callback): - self.callback = callback - super().__init__(key) - - def handle_event(self, message): - command = message['value'] - self.cenator(command) - self.callback(command) - - @staticmethod - def cenator(command): - LOG.debug('User executed command: "%s"', command) - MessageSender().send('JOHN CENA', f'You\'ve executed "{command}"') - - -class TestCommands(TerminalCommands): - """ - Some example commands useful for debugging. - !! Please remove from production code !! and inherit your own - class from TerminalCommands if you need to define custom - commands in your challenge. - """ - # pylint: disable=unused-argument, attribute-defined-outside-init, no-self-use - def command_sendmessage(self, *args): - """ - Insert TFW message template as first argument if executed without args. - - Evaluate first argumen as a dict and send it to the frontend. - This is useful for playing around with frontend APIs. - """ - if not args: - message_template = """'{"key": "", "data": {"command": ""}}'""" - TFWServerUplinkConnector().send_message({ - 'key': 'shell', - 'data': { - 'command': 'write', - 'value': f'sendmessage {message_template}' - } - }) - else: - TFWServerUplinkConnector().send_message(literal_eval(args[0])) - - def command_seppuku_tfw(self, *args): - """ - Restart tfw_server.py and event_handler_main.py. - This can speed up development when combined with mounting - volumes from host to container. - """ - seppuku = ( - 'nohup sh -c "supervisorctl restart tfwserver event_handler_main" &> /dev/null & ' - 'clear && echo "Committed seppuku! :)" && sleep infinity' - ) - uplink = TFWServerUplinkConnector() - uplink.send_message({ - 'key': 'shell', - 'data': { - 'command': 'write', - 'value': f'{seppuku}\n' - } - }) - uplink.send_message({ - 'key': 'dashboard', - 'data': { - 'command': 'reloadFrontend' - } - }) - - -class MessageFSMStepsEventHandler(FSMAwareEventHandler): - """ - This example EventHandler is capable of detecting FSM state. - !! Please remove from production code !! - """ - def handle_event(self, message): - pass - - def handle_fsm_step(self, **kwargs): - """ - When the FSM steps this method is invoked. - Receives a 'data' field from an fsm_update message as kwargs. - """ - MessageSender().send( - 'FSM info', - f'FSM has stepped from state "{kwargs["last_event"]["from_state"]}" ' - f'to state "{kwargs["current_state"]}" in response to trigger "{kwargs["last_event"]["trigger"]}"' - ) - - def main(): # pylint: disable=unused-variable Logger([ @@ -165,7 +72,7 @@ def main(): key='test' ) - def stop(*_): + def stop(*_): EventHandler.stop_all_instances() exit(0) signal(SIGTERM, stop) diff --git a/solvable/src/test_fsm.py b/solvable/src/test_fsm.py index 9170e90..f4f7639 100644 --- a/solvable/src/test_fsm.py +++ b/solvable/src/test_fsm.py @@ -3,7 +3,8 @@ from os.path import exists from tfw.fsm import LinearFSM -from tfw.builtins import MessageSender +from tfw.components import MessageSender +from tfw.builtins import TFWServerUplinkConnector class TestFSM(LinearFSM): @@ -11,7 +12,8 @@ class TestFSM(LinearFSM): def __init__(self): super().__init__(6) - self.message_sender = MessageSender() + self.uplink = TFWServerUplinkConnector() + self.message_sender = MessageSender(self.uplink) self.subscribe_predicate('step_3', self.step_3_allowed) @staticmethod