Exemple #1
0
class Component(object):
    component_register = []

    def __init__(self, config_dict=default_dict, name=None):
        """
        """
        self.components = ComponentCollection()
        self.graph_proxy = GraphProxy([self])
        self.behaviors = []
        self.signal_handlers = {}
        self.name = name or "Untitled Component"
        self.state = State()

        # Durability/Longevity values
        self.longevity = config_dict['longevity'] or 1000
        self.current_longevity = config_dict['longevity'] or 1000
        self.durability_coefficient = config_dict['durability_coefficient'] or 0
        self.unreliability_threshold = config_dict['unreliability_threshold']
        self.failure_threshold = config_dict['failure_threshold']
        self.next_accuracy_offset = "TODO Insert random value in range of accuracy offset"
        self.inaccuracy_rating = config_dict['inaccuracy_rating'] # [in]Accuracy above reliability threshold
        self.inaccuracy_growth_factor = config_dict['inaccuracy_growth_factor']
        # TODO failure_chance_growth_factor (how does failure change change below failure threshold)

        # Add self to static class list
        Component.component_register.append(self)

    def item_inputs(self):
        "Get all the components with edges ending on this guy"
        return List(self.graph_proxy.predecessors(self))

    def item_outputs(self):
        return List(self.graph_proxy.successors(self))

    def duplexes(self):
        "All components which are both inputs and outputs"
        return List(set(self.item_inputs()).intersection(self.item_outputs()))

    def __deepcopy__(self, memo):
        return self

    def __str__(self):
        return self.name if self.name else "Untitled Component"

    ## POST-EXECUTION STATE MODIFICATION##############################
    def update_durability(self):
        self.current_longevity -= 1 * self.durability_coefficient
        # TODO if beneath unreliability threshold, adjust accuracy
        # TODO if beneath failure threshold, roll for failure and distribute damage

    ## REGISTRATION OF COMPONENTS/BEHAVIORS ##########################

    def register_behavior(self, behavior):
        self.behaviors.append(behavior)
        self.register_signal_handlers(behavior)
        self.register_initial_behavior_state(behavior)
        # TODO modify state depending on the behavior registered here

    def register_component(self, component):
        self.components.append(component)
        component.container = self

        # TODO Aggregate behaviors and state as necessary.
        # TODO Alter state with relevant variables, callbacks,
        # etc

    def register_initial_behavior_state(self, behavior):
        for key in behavior.initial_state:
            if not self.state.specifies(key):
                if hasattr(behavior.initial_state[key], '__call__'):
                    self.state[key] = behavior.initial_state[key](self)
                else:
                    self.state[key] = value

    def register_signal_handlers(self, behavior):
        """
        Register callbacks to be executed when a signal is received
        via direct behavior execution.
        behavior - A behavior class with signal handlers
        """
        for handler in behavior.signal_handlers:
            self.signal_handlers[handler[0]] = getattr(handler[1], 'behave')

    ## TESTING REQUIREMENTS ##########################################
    def test_requirements(self, behavior):
        return False not in [req.recursively_test_component(self, requirement) \
                             for requirement in behavior.requirements]


    ## BEHAVIOR EXECUTION ############################################
    def execute_behavior(self, behavior):
        if self.test_requirements(behavior):
            behavior.behave(self)
            self.update_durability()
            # TODO Change component/item state

    ## BEHAVIOR TESTING ###############################################
    def test_behavior_requirements(self, behavior):
        for requirement in behavior.requirements:
            recursively_test_requirement(self, requirement)

    ## REQUIREMENT TESTING #############################################
    def recursively_test_requirement(self, requirement):
        if requirement(component) == True:
            return True
        else:
            if len(component.components) == 0:
                return False
            else:
                for component in component.components:
                    recursively_test_component(component, requirement)

    def describe_behaviors(self):
        for behavior in self.behaviors:
            print(behavior.name)

    ## INPUTS & OUTPUTS ################################################

    def plug_into(self, other):
        "Add self to other's inputs, add other as output on self"
        self.graph_proxy.add_link(self, other)

    def plug_out_from(self, other):
        "Add self to other's outputs, add other as input on self"
        other.graph_proxy.add_link(other, self)

    def unplug_from(self, other):
        other.graph_proxy.remove_link(other, self)

    def establish_duplex(self, other):
        "Establish bidirectional communication"
        self.plug_into(other)
        self.plug_out_from(other)

    ## DIRECT BEHAVIOR EXECUTION########################################
    def send(self, signal, args={}):
        """
        Receive a signal during the execution of a behavior which does
        not modify the environment directly, instead modifying connected
        components.
        """
        self.signal_handlers[signal](self, args)

    ## ACCURACY METHODS ################################################
    def get_next_inaccuracy(self):
        pass

    def get_inaccuracy_percentage(self):
        if self.current_longevity < ( self.longevity * self.unreliability_threshold ):
            return self.inaccuracy_rating ** ( self.get_pct_unreliable_longevity_remaining()  - self.inaccuracy_growth_factor )
        else:
            return self.inaccuracy_rating

    def get_pct_unreliable_longevity_remaining(self):
        return self.current_longevity / ( self.longevity * self.unreliability_threshold )

    def get_pct_unreliable_longevity_used(self):
        return 1 - self.get_pct_unreliable_longevity_remaining()

    ## DEBUGGING METHODS ###############################################

    def print_inputs_and_outputs(self):
        tab = tt.Texttable()
        tab.header(["INPUTS", "OUTPUTS"])
        tab.add_row([str([component.name + " " for component in self.item_inputs]),str([component.name + " " for component in self.item_outputs])])
        print tab.draw()

    def component_summary(self):
        for component in self.components:
            print component.name, " ", \
                str([x.name for x in component.item_inputs]), \
                str([x.name for x in component.item_outputs])

    def report(self):
        print "{{{COMPONENT: " + self.name + "}}}"
        print "++++++++++++++++COMPONENTS/INPUTS/OUTPUTS+++++++++++"
        self.component_summary()
        print "++++++++++++++++++++++++++++++++++++++++++++++++++++"
        if len(self.behaviors) > 0:
            print "======BEHAVIORS======"
            tab = tt.Texttable()
            tab.header([ "Behavior","Requirements"])
            print self.behaviors
            for item in self.behaviors:
                tab.add_row([item.name,
                             [requirement for requirement in item.requirements]])
                print tab.draw()
        else:
            print "---NO BEHAVIORS---"
        print "++++++++++++++++INPUTS & OUTPUTS++++++++++++++++++++"
        self.print_inputs_and_outputs()
        print "++++++++++++++++++++++++++++++++++++++++++++++++++++"
        if len(self.signal_handlers) > 0:
            print "======SIGNAL HANDLERS======"
            print "TODO Add this to report method"