# Copyright (C) 2018 Avatao.com Innovative Learning Kft. # All Rights Reserved. See LICENSE file for details. from collections import defaultdict from tfw import BroadcastingEventHandler from tfw.config.logs import logging LOG = logging.getLogger(__name__) class FSMManagingEventHandler(BroadcastingEventHandler): def __init__(self, key, fsm_type): super().__init__(key) self.fsm = fsm_type() self.fsm_manager = FSMManager(self.fsm) self._fsm_updater = FSMUpdater(self.fsm) self.command_handlers = { 'trigger': self.handle_trigger, 'update': self.handle_update } def handle_event(self, message): try: data = message['data'] message['data'] = self.command_handlers[data['command']](data) return message except KeyError: LOG.error('IGNORING MESSAGE: Invalid message received: %s', message) def handle_trigger(self, data): self.fsm_manager.trigger(data['value']) return self.with_fsm_update(data) def with_fsm_update(self, data): return { **data, **self._fsm_updater.get_fsm_state_and_transitions() } def handle_update(self, data): return self.with_fsm_update(data) class FSMManager: def __init__(self, fsm): self.fsm = fsm self.trigger_predicates = defaultdict(list) def trigger(self, trigger): predicate_results = [ predicate() for predicate in self.trigger_predicates[trigger] ] # TODO: think about what could we do when this prevents triggering if all(predicate_results): try: self.fsm.trigger(trigger) except AttributeError: LOG.debug('FSM failed to execute nonexistent trigger: "%s"', trigger) def subscribe_predicate(self, trigger, *predicates): self.trigger_predicates[trigger].extend(predicates) def unsubscribe_predicate(self, trigger, *predicates): self.trigger_predicates[trigger] = [ predicate for predicate in self.trigger_predicates[trigger] not in predicates ] class FSMUpdater: def __init__(self, fsm): self.fsm = fsm def get_fsm_state_and_transitions(self): state = self.fsm.state valid_transitions = [ {'trigger': trigger} for trigger in self.fsm.machine.get_triggers(self.fsm.state) ] return { 'current_state': state, 'valid_transitions': valid_transitions }