Пример #1
0
    def configure(self, config=None):
        """Performs the actual configuration of the ball device based on the
        dictionary that was passed to it.

        Args:
            config: Python dictionary which holds the configuration settings.
        """

        # Merge in any new changes that were just passed
        if config:
            self.config.update(config)

        self.log.debug("Configuring device with: %s", self.config)

        # convert delay strings to ms ints
        if self.config['exit_count_delay']:
            self.config['exit_count_delay'] = \
                Timing.string_to_ms(self.config['exit_count_delay'])

        if self.config['entrance_count_delay']:
            self.config['entrance_count_delay'] = \
                Timing.string_to_ms(self.config['entrance_count_delay'])

        # Register for events

        # Look for eject requests for this device
        self.machine.events.add_handler(
            'balldevice_' + self.name + '_ball_eject_request', self.eject)
Пример #2
0
    def configure(self, config=None):
        """Performs the actual configuration of the ball device based on the
        dictionary that was passed to it.

        Args:
            config: Python dictionary which holds the configuration settings.
        """

        # Merge in any new changes that were just passed
        if config:
            self.config.update(config)

        self.log.debug("Configuring device with: %s", self.config)

        # convert delay strings to ms ints
        if self.config['exit_count_delay']:
            self.config['exit_count_delay'] = \
                Timing.string_to_ms(self.config['exit_count_delay'])

        if self.config['entrance_count_delay']:
            self.config['entrance_count_delay'] = \
                Timing.string_to_ms(self.config['entrance_count_delay'])

        # Register for events

        # Look for eject requests for this device
        self.machine.events.add_handler('balldevice_' + self.name +
                                        '_ball_eject_request',
                                        self.eject)
Пример #3
0
    def __init__(self, machine, name, config):
        self.log = logging.getLogger('HitCounter.' + name)
        self.log.debug("Creating HitCounter LogicBlock")

        super(HitCounter, self).__init__(machine, name, config)

        self.delay = DelayManager()

        self.num_hits = 0
        self.ignore_hits = False

        if 'trigger_events' not in self.config:
            return  # Not much point to continue here
        else:
            self.config['trigger_events'] = self.machine.string_to_list(
                self.config['trigger_events'])

        if 'event_when_hit' not in self.config:
            self.config['event_when_hit'] = ('eventtrigger_' + self.name +
                                             '_hit')

        if 'hits_to_complete' not in self.config:
            self.config['hits_to_complete'] = 1

        if 'multiple_hit_window' not in self.config:
            self.config['multiple_hit_window'] = None
        else:
            self.config['multiple_hit_window'] = Timing.string_to_ms(
                self.config['multiple_hit_window'])
        if 'settle_time' not in self.config:
            self.config['settle_time'] = None
        else:
            self.config['settle_time'] = Timing.string_to_ms(
                self.config['settle_time'])
Пример #4
0
 def tick(self):
     """Method that runs as a task """
     while self.active:
         for coil in self.ball_search_coils:
             self.pop_coil(coil)
             yield Timing.secs(self.machine.config['ballsearch']\
                 ['secs between ball search coils'])
         yield Timing.secs(self.machine.config['ballsearch']\
                 ['secs between ball search rounds'])
Пример #5
0
 def tick(self):
     """Method that runs as a task """
     while self.active:
         for coil in self.ball_search_coils:
             self.pop_coil(coil)
             yield Timing.secs(self.machine.config['BallSearch']\
                 ['Secs between ball search coils'])
         yield Timing.secs(self.machine.config['BallSearch']\
                 ['Secs between ball search rounds'])
Пример #6
0
    def _initialize_switches(self):
        self.update_switches_from_hw()

        for switch in self.machine.switches:
            # Populate self.switches
            self.set_state(switch.name, switch.state, reset_time=True)

            # Populate self.registered_switches
            self.registered_switches[switch.name + '-0'] = list()
            self.registered_switches[switch.name + '-1'] = list()

            if self.machine.config['mpf']['auto_create_switch_events']:
                switch.activation_events.add(
                    self.machine.config['mpf']['switch_event_active'].replace(
                        '%', switch.name))

                switch.deactivation_events.add(
                    self.machine.config['mpf'][
                        'switch_event_inactive'].replace(
                        '%', switch.name))

            if 'activation_events' in switch.config:
                for event in Config.string_to_lowercase_list(
                        switch.config['activation_events']):

                    if "|" in event:
                        ev_name, ev_time = event.split("|")
                        self.add_switch_handler(
                            switch_name=switch.name,
                            callback=self.machine.events.post,
                            state=1,
                            ms=Timing.string_to_ms(ev_time),
                            callback_kwargs={'event': ev_name}
                        )
                    else:
                        switch.activation_events.add(event)

            if 'deactivation_events' in switch.config:
                for event in Config.string_to_lowercase_list(
                        switch.config['deactivation_events']):

                    if "|" in event:
                        ev_name, ev_time = event.split("|")
                        self.add_switch_handler(
                            switch_name=switch.name,
                            callback=self.machine.events.post,
                            state=0,
                            ms=Timing.string_to_ms(ev_time),
                            callback_kwargs={'event': ev_name}
                        )
                    else:
                        switch.deactivation_events.add(event)
    def _initialize_switches(self):
        self.update_switches_from_hw()

        for switch in self.machine.switches:
            # Populate self.switches
            self.set_state(switch.name, switch.state, reset_time=True)

            # Populate self.registered_switches
            self.registered_switches[switch.name + '-0'] = list()
            self.registered_switches[switch.name + '-1'] = list()

            if self.machine.config['mpf']['auto_create_switch_events']:
                switch.activation_events.add(
                    self.machine.config['mpf']['switch_event_active'].replace(
                        '%', switch.name))

                switch.deactivation_events.add(
                    self.machine.config['mpf']
                    ['switch_event_inactive'].replace('%', switch.name))

            if 'activation_events' in switch.config:
                for event in Util.string_to_lowercase_list(
                        switch.config['activation_events']):

                    if "|" in event:
                        ev_name, ev_time = event.split("|")
                        self.add_switch_handler(
                            switch_name=switch.name,
                            callback=self.machine.events.post,
                            state=1,
                            ms=Timing.string_to_ms(ev_time),
                            callback_kwargs={'event': ev_name})
                    else:
                        switch.activation_events.add(event)

            if 'deactivation_events' in switch.config:
                for event in Util.string_to_lowercase_list(
                        switch.config['deactivation_events']):

                    if "|" in event:
                        ev_name, ev_time = event.split("|")
                        self.add_switch_handler(
                            switch_name=switch.name,
                            callback=self.machine.events.post,
                            state=0,
                            ms=Timing.string_to_ms(ev_time),
                            callback_kwargs={'event': ev_name})
                    else:
                        switch.deactivation_events.add(event)
Пример #8
0
    def __init__(self,
                 mpfdisplay,
                 machine,
                 priority,
                 mode,
                 slide_a,
                 slide_b,
                 duration='1s',
                 **kwargs):

        super(Transition, self).__init__(mpfdisplay=mpfdisplay,
                                         machine=machine,
                                         priority=priority,
                                         mode=mode)

        self.slide_a = slide_a
        self.slide_b = slide_b
        self.priority = slide_b.priority
        self.duration = Timing.string_to_secs(duration)
        self.active_transition = True
        self.slide_a.active_transition = True
        self.slide_b.active_transition = True
        self.name = str(slide_a.name) + "_transition_" + str(slide_b.name)

        self.start_time = time.time()
        self.end_time = self.start_time + self.duration

        # mark both slides as active
        self.slide_a.active = True
        self.slide_b.active = True

        # Need to set the initial surface of the transition slide to the
        # existing slide's surface since the transition slide will be active
        self.surface.blit(self.slide_a.surface, (0, 0))
Пример #9
0
    def __init__(self, machine, name, config, collection=None):
        self.log = logging.getLogger('Diverter.' + name)
        super(Diverter, self).__init__(machine, name, config, collection)

        self.delay = DelayManager()

        # configure defaults:
        if 'type' not in self.config:
            self.config['type'] = 'pulse'  # default to pulse to not fry coils
        if 'timeout' not in self.config:
            self.config['timeout'] = 0
        if 'activation_switch' not in self.config:
            self.config['activation_switch'] = None
        if 'disable_switch' not in self.config:
            self.config['disable_switch'] = None
        if 'target_when_enabled' not in self.config:
            self.config['target_when_enabled'] = None  # todo
        if 'target_when_disabled' not in self.config:
            self.config['target_when_disabled'] = None  # todo

        # convert the timeout to ms
        self.config['timeout'] = Timing.string_to_ms(self.config['timeout'])

        # register for events
        for event in self.config['enable_events']:
            self.machine.events.add_handler(event, self.enable)

        for event in self.config['disable_events']:
            self.machine.events.add_handler(event, self.disable)
Пример #10
0
    def __init__(self, mpfdisplay, machine, priority,
                 mode, slide_a, slide_b, duration='1s', **kwargs):

        super(Transition, self).__init__(mpfdisplay=mpfdisplay,
                                         machine=machine,
                                         priority=priority,
                                         mode=mode)

        self.slide_a = slide_a
        self.slide_b = slide_b
        self.priority = slide_b.priority
        self.duration = Timing.string_to_secs(duration)
        self.active_transition = True
        self.slide_a.active_transition = True
        self.slide_b.active_transition = True
        self.name = str(slide_a.name) + "_transition_" + str(slide_b.name)

        self.start_time = time.time()
        self.end_time = self.start_time + self.duration

        # mark both slides as active
        self.slide_a.active = True
        self.slide_b.active = True

        # Need to set the initial surface of the transition slide to the
        # existing slide's surface since the transition slide will be active
        self.surface.blit(self.slide_a.surface, (0, 0))
Пример #11
0
    def _do_step(self):

            this_step = self.step_list[self.current_step]

            self.log.info("Switch: %s, Action: %s", this_step['switch'],
                          this_step['action'])

            # send this step's switches
            if this_step['action'] == 'activate':
                self.machine.switch_controller.process_switch(
                    this_step['switch'],
                    state=1,
                    logical=True)
            elif this_step['action'] == 'deactivate':
                self.machine.switch_controller.process_switch(
                    this_step['switch'],
                    state=0,
                    logical=True)
            elif this_step['action'] == 'hit':
                self._hit(this_step['switch'])

            # inc counter
            if self.current_step < len(self.step_list)-1:
                self.current_step += 1

                # schedule next step
                self.delay.add(name='switch_player_next_step',
                               ms=Timing.string_to_ms(self.step_list[self.current_step]['time']),
                               callback=self._do_step)
Пример #12
0
    def __init__(self, machine, name, config, priority):
        """SequenceShot is where you need certain switches to be hit in the
        right order, possibly within a time limit.

        Subclass of `Shot`

        Args:
            machine: The MachineController object
            name: String name of this shot.
            config: Dictionary that holds the configuration for this shot.

        """
        super(SequenceShot, self).__init__(machine, name, config, priority)

        self.delay = DelayManager()

        self.progress_index = 0
        """Tracks how far along through this sequence the current shot is."""

        # convert our switches config to a list
        if 'switches' in self.config:
            self.config['switches'] = \
                Config.string_to_list(self.config['switches'])

        # convert our timout to ms
        if 'time' in self.config:
            self.config['time'] = Timing.string_to_ms(self.config['time'])
        else:
            self.config['time'] = 0

        self.active_delay = False

        self.enable()
Пример #13
0
    def __init__(self, machine, name, config, priority):
        """SequenceShot is where you need certain switches to be hit in the
        right order, possibly within a time limit.

        Subclass of `Shot`

        Args:
            machine: The MachineController object
            name: String name of this shot.
            config: Dictionary that holds the configuration for this shot.

        """
        super(SequenceShot, self).__init__(machine, name, config, priority)

        self.delay = DelayManager()

        self.progress_index = 0
        """Tracks how far along through this sequence the current shot is."""

        # convert our switches config to a list
        if 'switches' in self.config:
            self.config['switches'] = \
                Config.string_to_list(self.config['switches'])

        # convert our timout to ms
        if 'time' in self.config:
            self.config['time'] = Timing.string_to_ms(self.config['time'])
        else:
            self.config['time'] = 0

        self.active_delay = False

        self.enable()
Пример #14
0
    def _do_step(self):

        this_step = self.step_list[self.current_step]

        self.log.debug("Switch: %s, Action: %s", this_step['switch'],
                       this_step['action'])

        # send this step's switches
        if this_step['action'] == 'activate':
            self.machine.switch_controller.process_switch(this_step['switch'],
                                                          state=1,
                                                          logical=True)
        elif this_step['action'] == 'deactivate':
            self.machine.switch_controller.process_switch(this_step['switch'],
                                                          state=0,
                                                          logical=True)
        elif this_step['action'] == 'hit':
            self._hit(this_step['switch'])

        # inc counter
        if self.current_step < len(self.step_list) - 1:
            self.current_step += 1

            # schedule next step
            self.delay.add(name='switch_player_next_step',
                           ms=Timing.string_to_ms(
                               self.step_list[self.current_step]['time']),
                           callback=self._do_step)
Пример #15
0
    def __init__(self,
                 mpfdisplay,
                 machine,
                 slide_a,
                 slide_b,
                 duration='1s',
                 **kwargs):

        super(Transition, self).__init__(mpfdisplay, machine, name=self.name)

        self.slide_a = slide_a
        self.slide_b = slide_b
        self.priority = slide_b.priority
        self.duration = Timing.string_to_secs(duration)

        # Need to make sure both the slides have rendered in case they're new
        self.slide_b.update()
        self.slide_a.update()

        self.start_time = time.time()
        self.end_time = self.start_time + self.duration

        # mark both slides as active
        self.slide_a.active = True
        self.slide_b.active = True
Пример #16
0
    def _start_event_callback(self):

        if ('time' in self.step_list[self.current_step] and
                self.step_list[self.current_step]['time'] > 0):

            self.delay.add(name='switch_player_next_step',
                           ms=Timing.string_to_ms(self.step_list[self.current_step]['time']),
                           callback=self._do_step)
Пример #17
0
    def _create_events(self, ev_name, ev_type, delay, callback):
        self.log.debug("Creating %s_event handler for event '%s' with delay "
                       "'%s'", ev_type, ev_name, delay)

        self.machine.events.add_handler(event=ev_name,
                                    handler=self._action_event_handler,
                                    callback=callback,
                                    ms_delay=Timing.string_to_ms(delay))
Пример #18
0
    def __init__(self, machine, mode, name, config):
        self.machine = machine
        self.mode = mode
        self.name = name
        self.config = config

        self.tick_var = self.mode.name + '_' + self.name + '_tick'
        self.mode.player[self.tick_var] = 0

        self.running = False
        self.start_value = 0
        self.restart_on_complete = False
        self._ticks = 0
        self.end_value = 0
        self.max_value = None
        self.direction = 'up'
        self.tick_secs = 1
        self.timer = None
        self.bcp = False
        self.event_keys = set()
        self.delay = DelayManager()

        if 'start_value' in self.config:
            self.start_value = self.config['start_value']
        else:
            self.start_value = 0

        if 'start_running' in self.config and self.config['start_running']:
            self.running = True

        if 'end_value' in self.config:
            self.end_value = self.config['end_value']

        if 'control_events' in self.config and self.config['control_events']:
            if type(self.config['control_events']) is dict:
                self.config['control_events'] = [self.config['control_events']]
        else:
            self.config['control_events'] = list()

        if 'direction' in self.config and self.config['direction'] == 'down':
            self.direction = 'down'

        if 'tick_interval' in self.config:
            self.tick_secs = Timing.string_to_secs(self.config['tick_interval'])

        if 'max_value' in self.config:
            self.max_value = self.config['max_value']

        if ('restart_on_complete' in self.config and
                self.config['restart_on_complete']):
            self.restart_on_complete = True

        if 'bcp' in self.config and self.config['bcp']:
            self.bcp = True

        self.mode.player[self.tick_var] = self.start_value

        self._setup_control_events(self.config['control_events'])
Пример #19
0
    def _start_event_callback(self):

        if ('time' in self.step_list[self.current_step]
                and self.step_list[self.current_step]['time'] > 0):

            self.delay.add(name='switch_player_next_step',
                           ms=Timing.string_to_ms(
                               self.step_list[self.current_step]['time']),
                           callback=self._do_step)
Пример #20
0
    def _start_event_callback(self):

        if "time" in self.step_list[self.current_step] and self.step_list[self.current_step]["time"] > 0:

            self.delay.add(
                name="switch_player_next_step",
                ms=Timing.string_to_ms(self.step_list[self.current_step]["time"]),
                callback=self._do_step,
            )
Пример #21
0
    def _create_events(self, ev_name, ev_type, delay, callback):
        self.log.debug(
            "Creating %s_event handler for event '%s' with delay "
            "'%s'", ev_type, ev_name, delay)

        self.machine.events.add_handler(event=ev_name,
                                        handler=self._action_event_handler,
                                        callback=callback,
                                        ms_delay=Timing.string_to_ms(delay))
Пример #22
0
    def validate_config_item(spec, item='item not in config!@#'):

        default = 'default required!@#'

        if '|' in spec:
            item_type, default = spec.split('|')
            if type(default) is str and default.lower() == 'none':
                default = None
        else:
            item_type = spec

        if item == 'item not in config!@#':
            if default == 'default required!@#':
                log.error(
                    'Required setting missing from config file. Run with '
                    'verbose logging and look for the last '
                    'ConfigProcessor entry above this line to see where '
                    'the problem is.')
                sys.exit()
            else:
                item = default

        if item_type == 'list':
            return Config.string_to_list(item)
        elif item_type == 'int':
            return int(item)
        elif item_type == 'float':
            return float(item)
        elif item_type == 'string':
            return str(item)
        elif item_type == 'boolean':
            if type(item) is bool:
                return item
            else:
                return item.lower() in ('yes', 'true')
        elif item_type == 'ms':
            return Timing.string_to_ms(item)
        elif item_type == 'secs':
            return Timing.string_to_secs(item)
        elif item_type == 'list_of_lists':
            return Config.list_of_lists(item)
Пример #23
0
    def validate_config_item(spec, item='item not in config!@#'):

        default = 'default required!@#'

        if '|' in spec:
            item_type, default = spec.split('|')
            if type(default) is str and default.lower() == 'none':
                default = None
        else:
            item_type = spec

        if item == 'item not in config!@#':
            if default == 'default required!@#':
                log.error('Required setting missing from config file. Run with '
                          'verbose logging and look for the last '
                          'ConfigProcessor entry above this line to see where '
                          'the problem is.')
                sys.exit()
            else:
                item = default

        if item_type == 'list':
            return Config.string_to_list(item)
        elif item_type == 'int':
            return int(item)
        elif item_type == 'float':
            return float(item)
        elif item_type == 'string':
            return str(item)
        elif item_type == 'boolean':
            if type(item) is bool:
                return item
            else:
                return item.lower() in ('yes', 'true')
        elif item_type == 'ms':
            return Timing.string_to_ms(item)
        elif item_type == 'secs':
            return Timing.string_to_secs(item)
        elif item_type == 'list_of_lists':
            return Config.list_of_lists(item)
Пример #24
0
Файл: shots.py Проект: xsdk/mpf
    def configure(self):
        """Configures the shot."""

        # convert our switches config to a list
        if 'Switches' in self.config:
            self.config['Switches'] = \
                self.machine.string_to_list(self.config['Switches'])

        # convert our timout to ms
        if 'Time' in self.config:
            self.config['Time'] = Timing.string_to_ms(self.config['Time'])
        else:
            self.config['Time'] = 0
Пример #25
0
    def schedule_removal(self, removal_time=None):
        """Schedules this slide to automatically be removed.

        Args:
            removal_time: MPF time string of when this slide should be removed.
                If no time is specified, the slide's existing removal time is
                used. If the slide has no existing time, the slide will not be
                removed.
        """
        if removal_time:
            self.expire_ms = Timing.string_to_ms(removal_time)

        if self.expire_ms:
            self.machine.display.delay.add(name=self.name + "_expiration", ms=self.expire_ms, callback=self.remove)
Пример #26
0
    def schedule_removal(self, removal_time=None):
        """Schedules this slide to automatically be removed.

        Args:
            removal_time: MPF time string of when this slide should be removed.
                If no time is specified, the slide's existing removal time is
                used. If the slide has no existing time, the slide will not be
                removed.
        """
        if removal_time:
            self.expire_ms = Timing.string_to_ms(removal_time)

        if self.expire_ms:
            self.machine.display.delay.add(name=self.name + '_expiration',
                                           ms=self.expire_ms,
                                           callback=self.remove)
Пример #27
0
    def __init__(self, mpfdisplay, machine, slide_a, slide_b, duration="1s", **kwargs):

        super(Transition, self).__init__(mpfdisplay, machine, name=self.name)

        self.slide_a = slide_a
        self.slide_b = slide_b
        self.priority = slide_b.priority
        self.duration = Timing.string_to_secs(duration)

        # Need to make sure both the slides have rendered in case they're new
        self.slide_b.update()
        self.slide_a.update()

        self.start_time = time.time()
        self.end_time = self.start_time + self.duration

        # mark both slides as active
        self.slide_a.active = True
        self.slide_b.active = True
Пример #28
0
    def schedule_deactivation(self, time=None):
        """Schedules a delay to deactivate this diverter.

        Args:
            time: The MPF string time of how long you'd like the delay before
                deactivating the diverter. Default is None which means it uses
                the 'activation_time' setting configured for this diverter. If
                there is no 'activation_time' setting and no delay is passed,
                it will disable the diverter immediately.
        """

        if time is not None:
            delay = Timing.string_to_ms(time)

        elif self.config['activation_time']:
            delay = self.config['activation_time']

        if delay:
            self.delay.add('disable_held_coil', delay, self.disable_held_coil)
        else:
            self.disable_held_coil()
Пример #29
0
    def schedule_deactivation(self, time=None):
        """Schedules a delay to deactivate this diverter.

        Args:
            time: The MPF string time of how long you'd like the delay before
                deactivating the diverter. Default is None which means it uses
                the 'activation_time' setting configured for this diverter. If
                there is no 'activation_time' setting and no delay is passed,
                it will disable the diverter immediately.
        """

        if time is not None:
            delay = Timing.string_to_ms(time)

        elif self.config['activation_time']:
            delay = self.config['activation_time']

        if delay:
            self.delay.add('disable_held_coil', delay, self.disable_held_coil)
        else:
            self.disable_held_coil()
Пример #30
0
    def _do_step(self):

        this_step = self.step_list[self.current_step]

        self.log.debug("Switch: %s, Action: %s", this_step["switch"], this_step["action"])

        # send this step's switches
        if this_step["action"] == "activate":
            self.machine.switch_controller.process_switch(this_step["switch"], state=1, logical=True)
        elif this_step["action"] == "deactivate":
            self.machine.switch_controller.process_switch(this_step["switch"], state=0, logical=True)
        elif this_step["action"] == "hit":
            self._hit(this_step["switch"])

        # inc counter
        if self.current_step < len(self.step_list) - 1:
            self.current_step += 1

            # schedule next step
            self.delay.add(
                name="switch_player_next_step",
                ms=Timing.string_to_ms(self.step_list[self.current_step]["time"]),
                callback=self._do_step,
            )
Пример #31
0
    def validate_item(self, item, validator, validation_failure_info):

        try:
            if item.lower() == 'none':
                item = None
        except AttributeError:
            pass

        if ':' in validator:
            validator = validator.split(':')
            # item could be str, list, or list of dicts
            item = Util.event_config_to_dict(item)

            return_dict = dict()

            for k, v in item.iteritems():
                return_dict[self.validate_item(k, validator[0],
                                               validation_failure_info)] = (
                    self.validate_item(v, validator[1], validation_failure_info)
                    )

            item = return_dict

        elif '%' in validator:

            if type(item) is str:

                try:
                    item = eval(validator.replace('%', "'" + item + "'"))
                except KeyError:
                    self.validation_error(item, validation_failure_info)
            else:
                item = None

        elif validator == 'str':
            if item is not None:
                item = str(item)
            else:
                item = None

        elif validator == 'float':
            try:
                item = float(item)
            except (TypeError, ValueError):
                # TODO error
                pass

        elif validator == 'int':
            try:
                item = int(item)
            except (TypeError, ValueError):
                # TODO error
                pass

        elif validator in ('bool', 'boolean'):
            if type(item) is str:
                if item.lower() in ['false', 'f', 'no', 'disable', 'off']:
                    item = False

            elif not item:
                item = False

            else:
                item = True

        elif validator == 'ms':
            item = Timing.string_to_ms(item)

        elif validator == 'secs':
            item = Timing.string_to_secs(item)

        elif validator == 'ticks':
            item = Timing.string_to_ticks(item)

        elif validator == 'ticks_int':
            item = int(Timing.string_to_ticks(item))

        else:
            self.log.error("Invalid Validator '%s' in config spec %s:%s",
                           validator,
                           validation_failure_info[0][0],
                           validation_failure_info[1])
            sys.exit()

        return item
Пример #32
0
class MediaController(object):

    def __init__(self, options):
        self.options = options

        self.log = logging.getLogger("MediaController")
        self.log.info("Media Controller Version %s", version.__version__)
        self.log.info("Backbox Control Protocol Version %s",
                      version.__bcp_version__)
        self.log.info("Config File Version %s",
                      version.__config_version__)

        python_version = sys.version_info
        self.log.info("Python version: %s.%s.%s", python_version[0],
                      python_version[1], python_version[2])
        self.log.info("Platform: %s", sys.platform)
        self.log.info("Python executable location: %s", sys.executable)
        self.log.info("32-bit Python? %s", sys.maxsize < 2**32)

        self.config = dict()
        self.done = False  # todo
        self.machine_path = None
        self.asset_managers = dict()
        self.num_assets_to_load = 0
        self.window = None
        self.window_manager = None
        self.pygame = False
        self.pygame_requested = False
        self.registered_pygame_handlers = dict()
        self.pygame_allowed_events = list()
        self.socket_thread = None
        self.receive_queue = Queue.Queue()
        self.sending_queue = Queue.Queue()
        self.crash_queue = Queue.Queue()
        self.game_modes = CaseInsensitiveDict()
        self.player_list = list()
        self.player = None
        self.HZ = 0
        self.next_tick_time = 0
        self.secs_per_tick = 0

        Task.Create(self._check_crash_queue)

        self.bcp_commands = {'hello': self.bcp_hello,
                             'goodbye': self.bcp_goodbye,
                             'reset': self.reset,
                             'mode_start': self.bcp_mode_start,
                             'mode_stop': self.bcp_mode_stop,
                             'error': self.bcp_error,
                             'ball_start': self.bcp_ball_start,
                             'ball_end': self.bcp_ball_end,
                             'game_start': self.bcp_game_start,
                             'game_end': self.bcp_game_end,
                             'player_added': self.bcp_player_add,
                             'player_variable': self.bcp_player_variable,
                             'player_score': self.bcp_player_score,
                             'player_turn_start': self.bcp_player_turn_start,
                             'attract_start': self.bcp_attract_start,
                             'attract_stop': self.bcp_attract_stop,
                             'trigger': self.bcp_trigger,
                             'switch': self.bcp_switch,
                             'get': self.bcp_get,
                             'set': self.bcp_set,
                             'config': self.bcp_config,
                             'timer': self.bcp_timer
                            }

        # load the MPF config & machine defaults
        self.config = (
            Config.load_config_yaml(config=self.config,
                                    yaml_file=self.options['mcconfigfile']))

        # Find the machine_files location. If it starts with a forward or
        # backward slash, then we assume it's from the mpf root. Otherwise we
        # assume it's from the subfolder location specified in the
        # mpfconfigfile location

        if (options['machinepath'].startswith('/') or
                options['machinepath'].startswith('\\')):
            machine_path = options['machinepath']
        else:
            machine_path = os.path.join(self.config['mediacontroller']['paths']
                                        ['machine_files'],
                                        options['machinepath'])

        self.machine_path = os.path.abspath(machine_path)

        # Add the machine folder to our path so we can import modules from it
        sys.path.append(self.machine_path)

        self.log.info("Machine folder: %s", machine_path)

        # Now find the config file location. Same as machine_file with the
        # slash uses to specify an absolute path

        if (options['configfile'].startswith('/') or
                options['configfile'].startswith('\\')):
            config_file = options['configfile']
        else:

            if not options['configfile'].endswith('.yaml'):
                options['configfile'] += '.yaml'

            config_file = os.path.join(self.machine_path,
                                       self.config['mediacontroller']['paths']
                                       ['config'],
                                       options['configfile'])

        self.log.info("Base machine config file: %s", config_file)

        # Load the machine-specific config
        self.config = Config.load_config_yaml(config=self.config,
                                              yaml_file=config_file)

        mediacontroller_config_spec = '''
                        exit_on_disconnect: boolean|True
                        port: int|5050
                        '''

        self.config['mediacontroller'] = (
            Config.process_config(mediacontroller_config_spec,
                                  self.config['mediacontroller']))

        self.events = EventManager(self)
        self.timing = Timing(self)

        # Load the media controller modules
        self.config['mediacontroller']['modules'] = (
            self.config['mediacontroller']['modules'].split(' '))
        for module in self.config['mediacontroller']['modules']:
            self.log.info("Loading module: %s", module)
            module_parts = module.split('.')
            exec('self.' + module_parts[0] + '=' + module + '(self)')

            # todo there's probably a more pythonic way to do this, and I know
            # exec() is supposedly unsafe, but meh, if you have access to put
            # malicious files in the system folder then you have access to this
            # code too.

        self.start_socket_thread()

        self.events.post("init_phase_1")
        self.events.post("init_phase_2")
        self.events.post("init_phase_3")
        self.events.post("init_phase_4")
        self.events.post("init_phase_5")

        self.reset()

    def _check_crash_queue(self):
        try:
            crash = self.crash_queue.get(block=False)
        except Queue.Empty:
            yield 1000
        else:
            self.log.critical("MPF Shutting down due to child thread crash")
            self.log.critical("Crash details: %s", crash)
            self.done = True

    def reset(self, **kwargs):
        """Processes an incoming BCP 'reset' command."""
        self.player = None
        self.player_list = list()

        self.events.post('mc_reset_phase_1')
        self.events.post('mc_reset_phase_2')
        self.events.post('mc_reset_phase_3')

    def get_window(self):
        """ Returns a reference to the onscreen display window.

        This method will set up a window if one doesn't exist yet. This method
        exists because there are several different modules and plugins which
        may want to use a window, but we don't know which combinations might
        be used, so we centralize the creation and management of an onscreen
        window here.
        """

        if not self.window:
            self.window_manager = window.WindowManager(self)
            self.window = self.window_manager.window

        return self.window

    def request_pygame(self):
        """Called by a module to let the system know it would like to use
        Pygame. We centralize the requests instead of letting each module do
        their own pygame.init() so we get it in one place and can get everthing
        initialized in the right order.

        Returns: True or False, depending on whether pygame is available or not.
        """

        if pygame and not self.pygame_requested:
            self.events.add_handler('init_phase_3', self._pygame_init)
            self.pygame_requested = True
            return True

        else:
            return False

    def _pygame_init(self):
        # performs the actual pygame initialization

        if not pygame:
            self.log.critical("Pygame is needed but not available. Please "
                              "install Pygame and try again.")
            raise Exception("Pygame is needed but not available. Please install"
                            " Pygame and try again.")

        if not self.pygame:
            self.log.debug("Initializing Pygame, version %s",
                           pygame.version.ver)

            pygame.init()
            self.pygame = True

            self.events.add_handler('timer_tick', self.get_pygame_events,
                                    priority=1000)

            self.events.post('pygame_initialized')

    def register_pygame_handler(self, event, handler):
        """Registers a method to be a handler for a certain type of Pygame
        event.

        Args:
            event: A string of the Pygame event name you're registering this
            handler for.
            handler: A method that will be called when this Pygame event is
            posted.
        """
        if event not in self.registered_pygame_handlers:
            self.registered_pygame_handlers[event] = set()

        self.registered_pygame_handlers[event].add(handler)
        self.pygame_allowed_events.append(event)

        self.log.debug("Adding Window event handler. Event:%s, Handler:%s",
                       event, handler)

        pygame.event.set_allowed(self.pygame_allowed_events)

    def get_pygame_events(self):
        """Gets (and dispatches) Pygame events. Automatically called every
        machine loop via the timer_tick event.
        """
        for event in pygame.event.get():
            if event.type in self.registered_pygame_handlers:
                for handler in self.registered_pygame_handlers[event.type]:

                    if (event.type == pygame.KEYDOWN or
                            event.type == pygame.KEYUP):
                        handler(event.key, event.mod)
                    else:
                        handler()

    def _process_command(self, bcp_command, **kwargs):
        self.log.debug("Processing command: %s %s", bcp_command, kwargs)

        try:
            self.bcp_commands[bcp_command](**kwargs)
        except KeyError:
            self.log.warning("Received invalid BCP command: %s", bcp_command)
            self.send('error', message='invalid command', command=bcp_command)

    def send(self, bcp_command, callback=None, **kwargs):
        """Sends a BCP command to the connected pinball controller.

        Args:
            bcp_command: String of the BCP command name.
            callback: Optional callback method that will be called when the
                command is sent.
            **kwargs: Optional additional kwargs will be added to the BCP
                command string.

        """
        self.sending_queue.put(bcp.encode_command_string(bcp_command,
                                                          **kwargs))
        if callback:
            callback()

    def send_dmd_frame(self, data):
        """Sends a DMD frame to the BCP client.

        Args:
            data: A 4096-length raw byte string.
        """

        dmd_string = 'dmd_frame?' + data
        self.sending_queue.put(dmd_string)

    def _timer_init(self):
        self.HZ = 30
        self.next_tick_time = time.time()
        self.secs_per_tick = 1.0 / self.HZ

    def timer_tick(self):
        """Called by the platform each machine tick based on self.HZ"""
        self.timing.timer_tick()  # notifies the timing module
        self.events.post('timer_tick')  # sends the timer_tick system event
        Task.timer_tick()  # notifies tasks
        DelayManager.timer_tick()

    def run(self):
        """Main run loop."""
        self._timer_init()

        self.log.info("Starting the run loop at %sHz", self.HZ)

        start_time = time.time()
        loops = 0

        secs_per_tick = self.secs_per_tick

        self.next_tick_time = time.time()

        try:
            while self.done is False:
                time.sleep(0.001)

                self.get_from_queue()

                if self.next_tick_time <= time.time():  # todo change this
                    self.timer_tick()
                    self.next_tick_time += secs_per_tick
                    loops += 1

            self._do_shutdown()
            self.log.info("Target loop rate: %s Hz", self.HZ)
            self.log.info("Actual loop rate: %s Hz",
                          loops / (time.time() - start_time))

        except KeyboardInterrupt:
            self.shutdown()

    def shutdown(self):
        """Shuts down and exits the media controller.

        This method will also send the BCP 'goodbye' command to any connected
        clients.
        """
        self.socket_thread.stop()

    def _do_shutdown(self):
        if self.pygame:
            pygame.quit()

    def socket_thread_stopped(self):
        """Notifies the media controller that the socket thread has stopped."""
        self.done = True

    def start_socket_thread(self):
        """Starts the BCPServer socket thread."""
        self.socket_thread = BCPServer(self, self.receive_queue,
                                       self.sending_queue)
        self.socket_thread.daemon = True
        self.socket_thread.start()

    def get_from_queue(self):
        """Gets and processes all queued up incoming BCP commands."""
        while not self.receive_queue.empty():
            cmd, kwargs = bcp.decode_command_string(
                self.receive_queue.get(False))
            self._process_command(cmd, **kwargs)

    def bcp_hello(self, **kwargs):
        """Processes an incoming BCP 'hello' command."""
        try:
            if LooseVersion(kwargs['version']) == (
                    LooseVersion(version.__bcp_version__)):
                self.send('hello', version=version.__bcp_version__)
            else:
                self.send('hello', version='unknown protocol version')
        except:
            self.log.warning("Received invalid 'version' parameter with "
                             "'hello'")

    def bcp_goodbye(self, **kwargs):
        """Processes an incoming BCP 'goodbye' command."""
        if self.config['mediacontroller']['exit_on_disconnect']:
            self.socket_thread.sending_thread.stop()
            sys.exit()

    def bcp_mode_start(self, name=None, priority=0, **kwargs):
        """Processes an incoming BCP 'mode_start' command."""
        if not name:
            return
            #todo raise error

        if name in self.game_modes:
            self.game_modes[name].start(priority=priority)

    def bcp_mode_stop(self, name, **kwargs):
        """Processes an incoming BCP 'mode_stop' command."""
        if not name:
            return
            #todo raise error

        if name in self.game_modes:
            self.game_modes[name].stop()

    def bcp_error(self, **kwargs):
        """Processes an incoming BCP 'error' command."""
        self.log.warning('Received error command from client')

    def bcp_ball_start(self, **kwargs):
        """Processes an incoming BCP 'ball_start' command."""
        self.events.post('ball_started', **kwargs)

    def bcp_ball_end(self, **kwargs):
        """Processes an incoming BCP 'ball_end' command."""
        self.events.post('ball_ended', **kwargs)

    def bcp_game_start(self, **kargs):
        """Processes an incoming BCP 'game_start' command."""
        self.bcp_player_add(number=1)
        self.bcp_player_turn_start(player=1)
        self.events.post('game_started', **kargs)

    def bcp_game_end(self, **kwargs):
        """Processes an incoming BCP 'game_end' command."""
        self.player = None
        self.events.post('game_ended', **kwargs)

    def bcp_player_add(self, number, **kwargs):
        """Processes an incoming BCP 'player_add' command."""

        if number > len(self.player_list):
            new_player = Player(self)
            self.player_list.append(new_player)
            new_player.score = 0

            self.events.post('player_add_success', num=number)

    def bcp_player_variable(self, name, value, prev_value, change, **kwargs):
        """Processes an incoming BCP 'player_variable' command."""

        if self.player:
            self.player[name] = value

    def bcp_player_score(self, value, prev_value, change, **kwargs):
        """Processes an incoming BCP 'player_score' command."""

        if self.player:
            self.player['score'] = int(value)

    def bcp_attract_start(self, **kwargs):
        """Processes an incoming BCP 'attract_start' command."""
        self.events.post('machineflow_Attract_start')

    def bcp_attract_stop(self, **kwargs):
        self.events.post('machineflow_Attract_stop')
        """Processes an incoming BCP 'attract_stop' command."""

    def bcp_player_turn_start(self, player, **kwargs):
        """Processes an incoming BCP 'player_turn_start' command."""

        if ((self.player and self.player.number != player) or
                not self.player):

            self.player = self.player_list[int(player)-1]

    def bcp_trigger(self, name, **kwargs):
        """Processes an incoming BCP 'trigger' command."""

        blocked_event_prefixes = ('player_',
                                  'machinemode_',
                                 )

        blocked_events = ('ball_started',
                          'ball_ended',
                          'game_started',
                          'game_ended',
                         )

        if not (name.startswith(blocked_event_prefixes) and
                name in blocked_events):

            self.events.post(name, **kwargs)

    def bcp_switch(self, name, state, **kwargs):
        """Processes an incoming BCP 'switch' command."""
        if int(state):
            self.events.post('switch_' + name + '_active')
        else:
            self.events.post('switch_' + name + '_inactive')

    def bcp_get(self, **kwargs):
        """Processes an incoming BCP 'get' command.

        Note that this media controller doesn't implement the 'get' command at
        this time, but it's included here for completeness since the 'get'
        command is part of the BCP 1.0 specification so we don't want to return
        an error if we receive an incoming 'get' command.

        """
        pass

    def bcp_set(self, **kwargs):
        """Processes an incoming BCP 'set' command.

        Note that this media controller doesn't implement the 'set' command at
        this time, but it's included here for completeness since the 'set'
        command is part of the BCP 1.0 specification so we don't want to return
        an error if we receive an incoming 'set' command.

        """
        pass

    def bcp_config(self, **kwargs):
        """Processes an incoming BCP 'config' command."""
        for k, v in kwargs.iteritems():
            if k.startswith('volume_'):
                self.bcp_set_volume(track=k.split('volume_')[1], value=v)

    def bcp_timer(self, name, action, **kwargs):
        """Processes an incoming BCP 'config' command."""

        self.events.post('timer_' + name + '_' + action, **kwargs)

    def bcp_set_volume(self, track, value):
        """Sets the volume based on an incoming BCP 'config' command.

        Args:
            track: String name of the track the volume will set.
            value: Float between 0 and 1 which represents the volume level to
                set.

        Note: At this time only the master volume can be set with this method.

        """

        if track == 'master':
            self.sound.set_volume(value)
Пример #33
0
    def validate_item(self, item, validator, validation_failure_info):

        try:
            if item.lower() == 'none':
                item = None
        except AttributeError:
            pass

        if ':' in validator:
            validator = validator.split(':')
            # item could be str, list, or list of dicts
            item = Util.event_config_to_dict(item)

            return_dict = dict()

            for k, v in item.iteritems():
                return_dict[self.validate_item(
                    k, validator[0],
                    validation_failure_info)] = (self.validate_item(
                        v, validator[1], validation_failure_info))

            item = return_dict

        elif '%' in validator:

            if type(item) is str:

                try:
                    item = eval(validator.replace('%', "'" + item + "'"))
                except KeyError:
                    self.validation_error(item, validation_failure_info)
            else:
                item = None

        elif validator == 'str':
            if item is not None:
                item = str(item)
            else:
                item = None

        elif validator == 'float':
            try:
                item = float(item)
            except (TypeError, ValueError):
                # TODO error
                pass

        elif validator == 'int':
            try:
                item = int(item)
            except (TypeError, ValueError):
                # TODO error
                pass

        elif validator in ('bool', 'boolean'):
            if type(item) is str:
                if item.lower() in ['false', 'f', 'no', 'disable', 'off']:
                    item = False

            elif not item:
                item = False

            else:
                item = True

        elif validator == 'ms':
            item = Timing.string_to_ms(item)

        elif validator == 'secs':
            item = Timing.string_to_secs(item)

        elif validator == 'ticks':
            item = Timing.string_to_ticks(item)

        elif validator == 'ticks_int':
            item = int(Timing.string_to_ticks(item))

        elif validator == 'list':
            item = Util.string_to_list(item)

        else:
            self.log.error("Invalid Validator '%s' in config spec %s:%s",
                           validator, validation_failure_info[0][0],
                           validation_failure_info[1])
            sys.exit()

        return item
Пример #34
0
    def __init__(self, machine, mode, name, config):
        self.machine = machine
        self.mode = mode
        self.name = name
        self.config = config

        self.tick_var = self.mode.name + '_' + self.name + '_tick'
        self.mode.player[self.tick_var] = 0

        self.running = False
        self.start_value = 0
        self.restart_on_complete = False
        self._ticks = 0
        self.end_value = None
        self.ticks_remaining = 0
        self.max_value = None
        self.direction = 'up'
        self.tick_secs = 1
        self.timer = None
        self.bcp = False
        self.event_keys = set()
        self.delay = DelayManager()
        self.log = None
        self.debug = False

        if 'start_value' in self.config:
            self.start_value = self.config['start_value']
        else:
            self.start_value = 0

        if 'start_running' in self.config and self.config['start_running']:
            self.running = True

        if 'end_value' in self.config:
            self.end_value = self.config['end_value']

        if 'control_events' in self.config and self.config['control_events']:
            if type(self.config['control_events']) is dict:
                self.config['control_events'] = [self.config['control_events']]
        else:
            self.config['control_events'] = list()

        if ('direction' in self.config and
                self.config['direction'].lower() == 'down'):
            self.direction = 'down'

            if not self.end_value:
                self.end_value = 0  # need it to be 0 not None

        if 'tick_interval' in self.config:
            self.tick_secs = Timing.string_to_secs(self.config[
                                                       'tick_interval'])

        if 'max_value' in self.config:
            self.max_value = self.config['max_value']

        if ('restart_on_complete' in self.config and
                self.config['restart_on_complete']):
            self.restart_on_complete = True

        if 'bcp' in self.config and self.config['bcp']:
            self.bcp = True

        if 'debug' in self.config and self.config['debug']:
            self.debug = True
            self.log.debug("Enabling Debug Logging")

        self.mode.player[self.tick_var] = self.start_value

        if self.log:
            self.log.debug("----------- Initial Values -----------")
            self.log.debug("running: %s", self.running)
            self.log.debug("start_value: %s", self.start_value)
            self.log.debug("restart_on_complete: %s", self.restart_on_complete)
            self.log.debug("_ticks: %s", self._ticks)
            self.log.debug("end_value: %s", self.end_value)
            self.log.debug("ticks_remaining: %s", self.ticks_remaining)
            self.log.debug("max_value: %s", self.max_value)
            self.log.debug("direction: %s", self.direction)
            self.log.debug("tick_secs: %s", self.tick_secs)
            self.log.debug("--------------------------------------")

        self._setup_control_events(self.config['control_events'])
Пример #35
0
    def _initialize(self):
        # convert names to objects

        # make sure the eject timeouts list matches the length of the eject targets
        if (len(self.config['eject_timeouts']) <
                len(self.config['eject_targets'])):
            self.config['eject_timeouts'] += [None] * (
                len(self.config['eject_targets']) -
                len(self.config['eject_timeouts']))

        timeouts_list = self.config['eject_timeouts']
        self.config['eject_timeouts'] = dict()

        for i in range(len(self.config['eject_targets'])):
            self.config['eject_timeouts'][self.config['eject_targets'][i]] = (
                Timing.string_to_ms(timeouts_list[i]))
        # End code to create timeouts list -------------------------------------

        # Register switch handlers with delays for entrance & exit counts
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=1,
                ms=self.config['entrance_count_delay'],
                callback=self.count_balls)
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=0,
                ms=self.config['exit_count_delay'],
                callback=self.count_balls)
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=1,
                ms=0,
                callback=self._invalidate)
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=0,
                ms=0,
                callback=self._invalidate)

        # Configure switch handlers for jam switch activity
        if self.config['jam_switch']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=self.config['jam_switch'].name, state=1, ms=0,
                callback=self._jam_switch_handler)
            # todo do we also need to add inactive and make a smarter
            # handler?

        # Configure switch handlers for entrance switch activity
        if self.config['entrance_switch']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=self.config['entrance_switch'].name, state=1, ms=0,
                callback=self._entrance_switch_handler)
            # todo do we also need to add inactive and make a smarter
            # handler?

        # handle hold_coil activation when a ball hits a switch
        for switch in self.config['hold_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=1,
                ms=0,
                callback=self._enable_hold_coil)



        # Configure event handlers to watch for target device status changes
        for target in self.config['eject_targets']:
            # Target device is requesting a ball
            self.machine.events.add_handler('balldevice_' +
                                            target.name
                                            + '_ball_request',
                                            self.eject,
                                            target=target,
                                            get_ball=True)

            # Target device is now able to receive a ball
            self.machine.events.add_handler('balldevice_' +
                                            target.name
                                            + '_ok_to_receive',
                                            self._do_eject)

        # Get an initial ball count
        self.count_balls(stealth=True)
Пример #36
0
Файл: mode.py Проект: qcapen/mpf
    def __init__(self, machine, mode, name, config):
        self.machine = machine
        self.mode = mode
        self.name = name
        self.config = config

        self.tick_var = self.mode.name + "_" + self.name + "_tick"
        self.mode.player[self.tick_var] = 0

        self.running = False
        self.start_value = 0
        self.restart_on_complete = False
        self._ticks = 0
        self.end_value = None
        self.ticks_remaining = 0
        self.max_value = None
        self.direction = "up"
        self.tick_secs = 1
        self.timer = None
        self.bcp = False
        self.event_keys = set()
        self.delay = DelayManager()
        self.log = None
        self.debug = False

        if "start_value" in self.config:
            self.start_value = self.config["start_value"]
        else:
            self.start_value = 0

        if "start_running" in self.config and self.config["start_running"]:
            self.running = True

        if "end_value" in self.config:
            self.end_value = self.config["end_value"]

        if "control_events" in self.config and self.config["control_events"]:
            if type(self.config["control_events"]) is dict:
                self.config["control_events"] = [self.config["control_events"]]
        else:
            self.config["control_events"] = list()

        if "direction" in self.config and self.config["direction"].lower() == "down":
            self.direction = "down"

            if not self.end_value:
                self.end_value = 0  # need it to be 0 not None

        if "tick_interval" in self.config:
            self.tick_secs = Timing.string_to_secs(self.config["tick_interval"])

        if "max_value" in self.config:
            self.max_value = self.config["max_value"]

        if "restart_on_complete" in self.config and self.config["restart_on_complete"]:
            self.restart_on_complete = True

        if "bcp" in self.config and self.config["bcp"]:
            self.bcp = True

        if "debug" in self.config and self.config["debug"]:
            self.debug = True
            self.log.debug("Enabling Debug Logging")

        self.mode.player[self.tick_var] = self.start_value

        if self.log:
            self.log.debug("----------- Initial Values -----------")
            self.log.debug("running: %s", self.running)
            self.log.debug("start_value: %s", self.start_value)
            self.log.debug("restart_on_complete: %s", self.restart_on_complete)
            self.log.debug("_ticks: %s", self._ticks)
            self.log.debug("end_value: %s", self.end_value)
            self.log.debug("ticks_remaining: %s", self.ticks_remaining)
            self.log.debug("max_value: %s", self.max_value)
            self.log.debug("direction: %s", self.direction)
            self.log.debug("tick_secs: %s", self.tick_secs)
            self.log.debug("--------------------------------------")

        self._setup_control_events(self.config["control_events"])
Пример #37
0
    def __init__(self, options):
        self.options = options

        self.log = logging.getLogger("MediaController")
        self.log.info("Media Controller Version %s", version.__version__)
        self.log.info("Backbox Control Protocol Version %s",
                      version.__bcp_version__)
        self.log.info("Config File Version %s",
                      version.__config_version__)

        python_version = sys.version_info
        self.log.info("Python version: %s.%s.%s", python_version[0],
                      python_version[1], python_version[2])
        self.log.info("Platform: %s", sys.platform)
        self.log.info("Python executable location: %s", sys.executable)
        self.log.info("32-bit Python? %s", sys.maxsize < 2**32)

        self.config = dict()
        self.done = False  # todo
        self.machine_path = None
        self.asset_managers = dict()
        self.num_assets_to_load = 0
        self.window = None
        self.window_manager = None
        self.pygame = False
        self.pygame_requested = False
        self.registered_pygame_handlers = dict()
        self.pygame_allowed_events = list()
        self.socket_thread = None
        self.receive_queue = Queue.Queue()
        self.sending_queue = Queue.Queue()
        self.crash_queue = Queue.Queue()
        self.game_modes = CaseInsensitiveDict()
        self.player_list = list()
        self.player = None
        self.HZ = 0
        self.next_tick_time = 0
        self.secs_per_tick = 0

        Task.Create(self._check_crash_queue)

        self.bcp_commands = {'hello': self.bcp_hello,
                             'goodbye': self.bcp_goodbye,
                             'reset': self.reset,
                             'mode_start': self.bcp_mode_start,
                             'mode_stop': self.bcp_mode_stop,
                             'error': self.bcp_error,
                             'ball_start': self.bcp_ball_start,
                             'ball_end': self.bcp_ball_end,
                             'game_start': self.bcp_game_start,
                             'game_end': self.bcp_game_end,
                             'player_added': self.bcp_player_add,
                             'player_variable': self.bcp_player_variable,
                             'player_score': self.bcp_player_score,
                             'player_turn_start': self.bcp_player_turn_start,
                             'attract_start': self.bcp_attract_start,
                             'attract_stop': self.bcp_attract_stop,
                             'trigger': self.bcp_trigger,
                             'switch': self.bcp_switch,
                             'get': self.bcp_get,
                             'set': self.bcp_set,
                             'config': self.bcp_config,
                             'timer': self.bcp_timer
                            }

        # load the MPF config & machine defaults
        self.config = (
            Config.load_config_yaml(config=self.config,
                                    yaml_file=self.options['mcconfigfile']))

        # Find the machine_files location. If it starts with a forward or
        # backward slash, then we assume it's from the mpf root. Otherwise we
        # assume it's from the subfolder location specified in the
        # mpfconfigfile location

        if (options['machinepath'].startswith('/') or
                options['machinepath'].startswith('\\')):
            machine_path = options['machinepath']
        else:
            machine_path = os.path.join(self.config['mediacontroller']['paths']
                                        ['machine_files'],
                                        options['machinepath'])

        self.machine_path = os.path.abspath(machine_path)

        # Add the machine folder to our path so we can import modules from it
        sys.path.append(self.machine_path)

        self.log.info("Machine folder: %s", machine_path)

        # Now find the config file location. Same as machine_file with the
        # slash uses to specify an absolute path

        if (options['configfile'].startswith('/') or
                options['configfile'].startswith('\\')):
            config_file = options['configfile']
        else:

            if not options['configfile'].endswith('.yaml'):
                options['configfile'] += '.yaml'

            config_file = os.path.join(self.machine_path,
                                       self.config['mediacontroller']['paths']
                                       ['config'],
                                       options['configfile'])

        self.log.info("Base machine config file: %s", config_file)

        # Load the machine-specific config
        self.config = Config.load_config_yaml(config=self.config,
                                              yaml_file=config_file)

        mediacontroller_config_spec = '''
                        exit_on_disconnect: boolean|True
                        port: int|5050
                        '''

        self.config['mediacontroller'] = (
            Config.process_config(mediacontroller_config_spec,
                                  self.config['mediacontroller']))

        self.events = EventManager(self)
        self.timing = Timing(self)

        # Load the media controller modules
        self.config['mediacontroller']['modules'] = (
            self.config['mediacontroller']['modules'].split(' '))
        for module in self.config['mediacontroller']['modules']:
            self.log.info("Loading module: %s", module)
            module_parts = module.split('.')
            exec('self.' + module_parts[0] + '=' + module + '(self)')

            # todo there's probably a more pythonic way to do this, and I know
            # exec() is supposedly unsafe, but meh, if you have access to put
            # malicious files in the system folder then you have access to this
            # code too.

        self.start_socket_thread()

        self.events.post("init_phase_1")
        self.events.post("init_phase_2")
        self.events.post("init_phase_3")
        self.events.post("init_phase_4")
        self.events.post("init_phase_5")

        self.reset()
Пример #38
0
    def _initialize(self):
        # convert names to objects

        if self.config["ball_switches"]:
            for i in range(len(self.config["ball_switches"])):
                self.config["ball_switches"][i] = self.machine.switches[self.config["ball_switches"][i]]

        if self.config["eject_coil"]:
            self.config["eject_coil"] = self.machine.coils[self.config["eject_coil"]]

        if self.config["eject_switch"]:
            self.config["eject_switch"] = self.machine.switches[self.config["eject_switch"]]

        if self.config["entrance_switch"]:
            self.config["entrance_switch"] = self.machine.switches[self.config["entrance_switch"]]

        if self.config["jam_switch"]:
            self.config["jam_switch"] = self.machine.switches[self.config["jam_switch"]]

        if self.config["confirm_eject_type"] == "switch" and (self.config["confirm_eject_target"]):
            self.config["confirm_eject_switch"] = self.machine.switches[self.config["confirm_eject_switch"]]

        if self.config["eject_targets"]:
            for i in range(len(self.config["eject_targets"])):
                self.config["eject_targets"][i] = self.machine.balldevices[self.config["eject_targets"][i]]

        # make sure the eject timeouts list matches the length of the eject targets
        if len(self.config["eject_timeouts"]) < len(self.config["eject_targets"]):
            self.config["eject_timeouts"] += [None] * (
                len(self.config["eject_targets"]) - len(self.config["eject_timeouts"])
            )

        timeouts_list = self.config["eject_timeouts"]
        self.config["eject_timeouts"] = dict()

        for i in range(len(self.config["eject_targets"])):
            self.config["eject_timeouts"][self.config["eject_targets"][i]] = Timing.string_to_ms(timeouts_list[i])
        # End code to create timeouts list -------------------------------------

        # Register switch handlers with delays for entrance & exit counts
        for switch in self.config["ball_switches"]:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=1, ms=self.config["entrance_count_delay"], callback=self.count_balls
            )
        for switch in self.config["ball_switches"]:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=0, ms=self.config["exit_count_delay"], callback=self.count_balls
            )
        for switch in self.config["ball_switches"]:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=1, ms=0, callback=self._invalidate
            )
        for switch in self.config["ball_switches"]:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name, state=0, ms=0, callback=self._invalidate
            )

        # Configure switch handlers for jam switch activity
        if self.config["jam_switch"]:
            self.machine.switch_controller.add_switch_handler(
                switch_name=self.config["jam_switch"].name, state=1, ms=0, callback=self._jam_switch_handler
            )
            # todo do we also need to add inactive and make a smarter
            # handler?

        # Configure switch handlers for entrance switch activity
        if self.config["entrance_switch"]:
            self.machine.switch_controller.add_switch_handler(
                switch_name=self.config["entrance_switch"].name, state=1, ms=0, callback=self._entrance_switch_handler
            )
            # todo do we also need to add inactive and make a smarter
            # handler?

        # Configure event handlers to watch for target device status changes
        for target in self.config["eject_targets"]:
            # Target device is requesting a ball
            self.machine.events.add_handler(
                "balldevice_" + target.name + "_ball_request", self.eject, target=target, get_ball=True
            )

            # Target device is now able to receive a ball
            self.machine.events.add_handler("balldevice_" + target.name + "_ok_to_receive", self._do_eject)

        # Get an initial ball count
        self.count_balls(stealth=True)
Пример #39
0
    def __init__(self, machine, name, config, collection=None):
        self.log = logging.getLogger('Diverter.' + name)
        super(Diverter, self).__init__(machine, name, config, collection)

        self.delay = DelayManager()

        # Attributes
        self.active = False
        self.enabled = False
        self.platform = None

        # configure defaults:
        if 'type' not in self.config:
            self.config['type'] = 'pulse'  # default to pulse to not fry coils
        if 'activation_time' not in self.config:
            self.config['activation_time'] = 0
        if 'activation_switches' in self.config:
            self.config['activation_switches'] = Config.string_to_list(
                self.config['activation_switches'])
        else:
            self.config['activation_switches'] = list()

        if 'disable_switches' in self.config:
            self.config['disable_switches'] = Config.string_to_list(
                self.config['disable_switches'])
        else:
            self.config['disable_switches'] = list()

        if 'deactivation_switches' in self.config:
            self.config['deactivation_switches'] = Config.string_to_list(
                self.config['deactivation_switches'])
        else:
            self.config['deactivation_switches'] = list()

        if 'activation_coil' in self.config:
            self.config['activation_coil'] = (
                self.machine.coils[self.config['activation_coil']])

        if 'deactivation_coil' in self.config:
            self.config['deactivation_coil'] = (
                self.machine.coils[self.config['deactivation_coil']])
        else:
            self.config['deactivation_coil'] = None

        if 'targets_when_active' in self.config:
            self.config['targets_when_active'] = Config.string_to_list(
                self.config['targets_when_active'])
        else:
            self.config['targets_when_active'] = ['playfield']

        if 'targets_when_inactive' in self.config:
            self.config['targets_when_inactive'] = Config.string_to_list(
                self.config['targets_when_inactive'])
        else:
            self.config['targets_when_inactive'] = ['playfield']

        if 'feeder_devices' in self.config:
            self.config['feeder_devices'] = Config.string_to_list(
                self.config['feeder_devices'])
        else:
            self.config['feeder_devices'] = list()

        # Create a list of ball device objects when active and inactive. We need
        # this because ball eject attempts pass the target device as an object
        # rather than by name.

        self.config['active_objects'] = list()
        self.config['inactive_objects'] = list()

        for target_device in self.config['targets_when_active']:
            if target_device == 'playfield':
                self.config['active_objects'].append('playfield')
            else:
                self.config['active_objects'].append(
                    self.machine.balldevices[target_device])

        for target_device in self.config['targets_when_inactive']:
            if target_device == 'playfield':
                self.config['inactive_objects'].append('playfield')
            else:
                self.config['inactive_objects'].append(
                    self.machine.balldevices[target_device])

        # convert the activation_time to ms
        self.config['activation_time'] = Timing.string_to_ms(
            self.config['activation_time'])

        # register for events
        for event in self.config['enable_events']:
            self.machine.events.add_handler(event, self.enable)

        for event in self.config['disable_events']:
            self.machine.events.add_handler(event, self.disable)

        # register for feeder device eject events
        for feeder_device in self.config['feeder_devices']:
            self.machine.events.add_handler(
                'balldevice_' + feeder_device + '_ball_eject_attempt',
                self._feeder_eject_attempt)

        self.machine.events.add_handler('init_phase_3',
                                        self._register_switches)

        self.platform = self.config['activation_coil'].platform
Пример #40
0
    def __init__(self, slide, machine, x=None, y=None, h_pos=None,
                     v_pos=None, layer=0, **kwargs):

        super(CharacterPicker, self).__init__(slide, x, y, h_pos, v_pos, layer)

        self.fonts = machine.display.fonts
        self.slide = slide
        self.machine = machine
        self.layer = layer
        self.config = deepcopy(kwargs)
        self.char_list = deque()
        self.cursor_position = 0
        self.selected_char = ''
        self.registered_event_handlers = list()

        if 'selected_char_color' not in self.config:
            self.config['selected_char_color'] = 0

        if 'selected_char_bg' not in self.config:
            self.config['selected_char_bg'] = 15

        if 'char_width' not in self.config:
            self.config['char_width'] = 11

        if 'width' not in self.config:
            self.config['width'] = None

        if 'height' not in self.config:
            self.config['height'] = 15

        if 'char_x_offset' not in self.config:
            self.config['char_x_offset'] = 0

        if 'char_y_offset' not in self.config:
            self.config['char_y_offset'] = 0

        if 'shift_left_tag' not in self.config:
            self.config['shift_left_tag'] = 'left_flipper'

        if 'shift_right_tag' not in self.config:
            self.config['shift_right_tag'] = 'right_flipper'

        if 'select_tag' not in self.config:
            self.config['select_tag'] = 'start'

        if 'name' in self.config:
            self.name = self.config['name']
        else:
            self.name = 'character_picker'

        if 'char_list' not in self.config:
            self.config['char_list'] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'

        if 'max_chars' not in self.config:
            self.config['max_chars'] = 3

        if 'timeout' not in self.config:
            self.config['timeout'] = None
        else:
            self.config['timeout'] = (
                Timing.string_to_secs(self.config['timeout']))

        if 'back_char' not in self.config:
            self.config['back_char'] = 'back_arrow_7x7'

        if 'end_char' not in self.config:
            self.config['end_char'] = 'end_11x7'

        if 'back_char_selected' not in self.config:
            self.config['back_char_selected'] = 'back_arrow_7x7_selected'

        if 'end_char_selected' not in self.config:
            self.config['end_char_selected'] = 'end_11x7_selected'

        if 'image_padding' not in self.config:
            self.config['image_padding'] = 1

        if 'return_param' not in self.config:
            self.config['return_param'] = 'award'

        self.config['selected_char_color'] = (
            self.adjust_color(self.config['selected_char_color']))
        self.config['selected_char_bg'] = (
            self.adjust_color(self.config['selected_char_bg']))

        self.adjust_colors(**self.config)

        self.config['color'] = self.adjusted_color
        self.config['bg_color'] = self.adjusted_bg_color

        self.char_list.extend(self.config['char_list'])
        self.char_list.append('back')
        self.char_list.append('end')
        self.char_list.rotate(len(self.char_list)/2)

        self.cursor_position = len(self.char_list)/2
        self.selected_char = self.char_list[self.cursor_position]

        self.machine._set_machine_var(name=self.name + '_chars_entered',
                                      value='')

        self.setup_switch_handlers()

        self.render()
Пример #41
0
class MediaController(object):

    def __init__(self, options):
        self.options = options

        self.log = logging.getLogger("MediaController")
        self.log.debug("Command line arguments: {}".format(self.options))
        self.log.info("Media Controller Version %s", version.__version__)
        self.log.debug("Backbox Control Protocol Version %s",
                      version.__bcp_version__)
        self.log.debug("Config File Version %s",
                      version.__config_version__)

        python_version = sys.version_info

        if python_version[0] != 2 or python_version[1] != 7:
            self.log.error("Incorrect Python version. MPF requires Python 2.7."
                           "x. You have Python %s.%s.%s.", python_version[0],
                           python_version[1], python_version[2])
            sys.exit()

        self.log.debug("Python version: %s.%s.%s", python_version[0],
                      python_version[1], python_version[2])
        self.log.debug("Platform: %s", sys.platform)
        self.log.debug("Python executable location: %s", sys.executable)
        self.log.debug("32-bit Python? %s", sys.maxsize < 2**32)

        self.active_debugger = dict()

        self.config = dict()
        self.done = False  # todo
        self.machine_path = None
        self.asset_managers = dict()
        self.window = None
        self.window_manager = None
        self.pygame = False
        self.pygame_requested = False
        self.registered_pygame_handlers = dict()
        self.pygame_allowed_events = list()
        self.socket_thread = None
        self.receive_queue = Queue.Queue()
        self.sending_queue = Queue.Queue()
        self.crash_queue = Queue.Queue()
        self.modes = CaseInsensitiveDict()
        self.player_list = list()
        self.player = None
        self.HZ = 0
        self.next_tick_time = 0
        self.secs_per_tick = 0
        self.machine_vars = CaseInsensitiveDict()
        self.machine_var_monitor = False
        self.tick_num = 0
        self.delay = DelayManager()

        self._pc_assets_to_load = 0
        self._pc_total_assets = 0
        self.pc_connected = False

        Task.create(self._check_crash_queue)

        self.bcp_commands = {'ball_start': self.bcp_ball_start,
                             'ball_end': self.bcp_ball_end,
                             'config': self.bcp_config,
                             'error': self.bcp_error,
                             'get': self.bcp_get,
                             'goodbye': self.bcp_goodbye,
                             'hello': self.bcp_hello,
                             'machine_variable': self.bcp_machine_variable,
                             'mode_start': self.bcp_mode_start,
                             'mode_stop': self.bcp_mode_stop,
                             'player_added': self.bcp_player_add,
                             'player_score': self.bcp_player_score,
                             'player_turn_start': self.bcp_player_turn_start,
                             'player_variable': self.bcp_player_variable,
                             'reset': self.reset,
                             'set': self.bcp_set,
                             'shot': self.bcp_shot,
                             'switch': self.bcp_switch,
                             'timer': self.bcp_timer,
                             'trigger': self.bcp_trigger,
                            }

        FileManager.init()
        self.config = dict()
        self._load_mc_config()
        self._set_machine_path()
        self._load_machine_config()

        # Find the machine_files location. If it starts with a forward or
        # backward slash, then we assume it's from the mpf root. Otherwise we
        # assume it's from the subfolder location specified in the
        # mpfconfig file location

        if (options['machine_path'].startswith('/') or
                options['machine_path'].startswith('\\')):
            machine_path = options['machine_path']
        else:
            machine_path = os.path.join(self.config['media_controller']['paths']
                                        ['machine_files'],
                                        options['machine_path'])

        self.machine_path = os.path.abspath(machine_path)

        # Add the machine folder to our path so we can import modules from it
        sys.path.append(self.machine_path)

        self.log.info("Machine folder: %s", machine_path)

        mediacontroller_config_spec = '''
                        exit_on_disconnect: boolean|True
                        port: int|5050
                        '''

        self.config['media_controller'] = (
            Config.process_config(mediacontroller_config_spec,
                                  self.config['media_controller']))

        self.events = EventManager(self, setup_event_player=False)
        self.timing = Timing(self)

        # Load the media controller modules
        self.config['media_controller']['modules'] = (
            self.config['media_controller']['modules'].split(' '))
        self.log.info("Loading Modules...")
        for module in self.config['media_controller']['modules']:
            self.log.debug("Loading module: %s", module)
            module_parts = module.split('.')
            exec('self.' + module_parts[0] + '=' + module + '(self)')

            # todo there's probably a more pythonic way to do this, and I know
            # exec() is supposedly unsafe, but meh, if you have access to put
            # malicious files in the system folder then you have access to this
            # code too.

        self.start_socket_thread()

        self.events.post("init_phase_1")
        self.events._process_event_queue()
        self.events.post("init_phase_2")
        self.events._process_event_queue()
        self.events.post("init_phase_3")
        self.events._process_event_queue()
        self.events.post("init_phase_4")
        self.events._process_event_queue()
        self.events.post("init_phase_5")
        self.events._process_event_queue()

        self.reset()

    def _load_mc_config(self):
        self.config = Config.load_config_file(self.options['mcconfigfile'])

        # Find the machine_files location. If it starts with a forward or
        # backward slash, then we assume it's from the mpf root. Otherwise we
        # assume it's from the subfolder location specified in the
        # mpfconfigfile location

    def _set_machine_path(self):
        if (self.options['machine_path'].startswith('/') or
                self.options['machine_path'].startswith('\\')):
            machine_path = self.options['machine_path']
        else:
            machine_path = os.path.join(self.config['media_controller']['paths']
                                        ['machine_files'],
                                        self.options['machine_path'])

        self.machine_path = os.path.abspath(machine_path)
        self.log.debug("Machine path: {}".format(self.machine_path))

        # Add the machine folder to sys.path so we can import modules from it
        sys.path.append(self.machine_path)

    def _load_machine_config(self):
        for num, config_file in enumerate(self.options['configfile']):

            if not (config_file.startswith('/') or
                    config_file.startswith('\\')):

                config_file = os.path.join(self.machine_path,
                    self.config['media_controller']['paths']['config'], config_file)

            self.log.info("Machine config file #%s: %s", num+1, config_file)

            self.config = Util.dict_merge(self.config,
                Config.load_config_file(config_file))

    def _check_crash_queue(self):
        try:
            crash = self.crash_queue.get(block=False)
        except Queue.Empty:
            yield 1000
        else:
            self.log.critical("MPF Shutting down due to child thread crash")
            self.log.critical("Crash details: %s", crash)
            self.done = True

    def reset(self, **kwargs):
        """Processes an incoming BCP 'reset' command."""
        self.player = None
        self.player_list = list()

        self.events.add_handler('assets_to_load',
                                self._bcp_client_asset_loader_tick)
        self.events.replace_handler('timer_tick', self.asset_loading_counter)

        self.events.post('mc_reset_phase_1')
        self.events._process_event_queue()
        self.events.post('mc_reset_phase_2')
        self.events._process_event_queue()
        self.events.post('mc_reset_phase_3')
        self.events._process_event_queue()

    def get_window(self):
        """ Returns a reference to the onscreen display window.

        This method will set up a window if one doesn't exist yet. This method
        exists because there are several different modules and plugins which
        may want to use a window, but we don't know which combinations might
        be used, so we centralize the creation and management of an onscreen
        window here.

        """
        if not self.window:
            self.window_manager = window.WindowManager(self)
            self.window = self.window_manager.window

        return self.window

    def request_pygame(self):
        """Called by a module to let the system know it would like to use
        Pygame. We centralize the requests instead of letting each module do
        their own pygame.init() so we get it in one place and can get everthing
        initialized in the right order.

        Returns: True or False, depending on whether pygame is available or not.
        """

        if pygame and not self.pygame_requested:
            self.events.add_handler('init_phase_3', self._pygame_init)
            self.pygame_requested = True
            return True

        else:
            return False

    def _pygame_init(self):
        # performs the actual pygame initialization

        if not pygame:
            self.log.critical("Pygame is needed but not available. Please "
                              "install Pygame and try again.")
            raise Exception("Pygame is needed but not available. Please install"
                            " Pygame and try again.")

        if not self.pygame:
            self.log.debug("Initializing Pygame, version %s",
                           pygame.version.ver)

            pygame.init()
            self.pygame = True

            self.events.add_handler('timer_tick', self.get_pygame_events,
                                    priority=1000)
            self.events.add_handler('timer_tick', self.asset_loading_counter)

            self.events.post('pygame_initialized')
            self.events._process_event_queue()

    def register_pygame_handler(self, event, handler):
        """Registers a method to be a handler for a certain type of Pygame
        event.

        Args:
            event: A string of the Pygame event name you're registering this
            handler for.
            handler: A method that will be called when this Pygame event is
            posted.
        """
        if event not in self.registered_pygame_handlers:
            self.registered_pygame_handlers[event] = set()

        self.registered_pygame_handlers[event].add(handler)
        self.pygame_allowed_events.append(event)

        self.log.debug("Adding Window event handler. Event:%s, Handler:%s",
                       event, handler)

        pygame.event.set_allowed(self.pygame_allowed_events)

    def get_pygame_events(self):
        """Gets (and dispatches) Pygame events. Automatically called every
        machine loop via the timer_tick event.
        """
        for event in pygame.event.get():
            if event.type in self.registered_pygame_handlers:
                for handler in self.registered_pygame_handlers[event.type]:

                    if (event.type == pygame.KEYDOWN or
                            event.type == pygame.KEYUP):
                        handler(event.key, event.mod)
                    else:
                        handler()

    def _process_command(self, bcp_command, **kwargs):
        self.log.debug("Processing command: %s %s", bcp_command, kwargs)


        # Can't use try/except KeyError here becasue there could be a KeyError
        # in the callback which we don't want it to swallow.
        if bcp_command in self.bcp_commands:
            self.bcp_commands[bcp_command](**kwargs)
        else:
            self.log.warning("Received invalid BCP command: %s", bcp_command)
            self.send('error', message='invalid command', command=bcp_command)


    def send(self, bcp_command, callback=None, **kwargs):
        """Sends a BCP command to the connected pinball controller.

        Args:
            bcp_command: String of the BCP command name.
            callback: Optional callback method that will be called when the
                command is sent.
            **kwargs: Optional additional kwargs will be added to the BCP
                command string.

        """
        self.sending_queue.put(bcp.encode_command_string(bcp_command,
                                                         **kwargs))
        if callback:
            callback()

    def send_dmd_frame(self, data):
        """Sends a DMD frame to the BCP client.

        Args:
            data: A 4096-length raw byte string.
        """

        dmd_string = 'dmd_frame?' + data
        self.sending_queue.put(dmd_string)

    def _timer_init(self):
        self.HZ = 30
        self.next_tick_time = time.time()
        self.secs_per_tick = 1.0 / self.HZ

    def timer_tick(self):
        """Called by the platform each machine tick based on self.HZ"""
        self.timing.timer_tick()  # notifies the timing module
        self.events.post('timer_tick')  # sends the timer_tick system event
        self.tick_num += 1
        Task.timer_tick()  # notifies tasks
        DelayManager.timer_tick(self)
        self.events._process_event_queue()

    def run(self):
        """Main run loop."""
        self._timer_init()

        self.log.info("Starting the run loop at %sHz", self.HZ)

        start_time = time.time()
        loops = 0

        secs_per_tick = self.secs_per_tick

        self.next_tick_time = time.time()

        try:
            while self.done is False:
                time.sleep(0.001)

                self.get_from_queue()

                if self.next_tick_time <= time.time():  # todo change this
                    self.timer_tick()
                    self.next_tick_time += secs_per_tick
                    loops += 1

            self._do_shutdown()
            self.log.info("Target loop rate: %s Hz", self.HZ)
            self.log.info("Actual loop rate: %s Hz",
                          loops / (time.time() - start_time))

        except KeyboardInterrupt:
            self.shutdown()

    def shutdown(self):
        """Shuts down and exits the media controller.

        This method will also send the BCP 'goodbye' command to any connected
        clients.
        """
        self.socket_thread.stop()

    def _do_shutdown(self):
        if self.pygame:
            pygame.quit()

    def socket_thread_stopped(self):
        """Notifies the media controller that the socket thread has stopped."""
        self.done = True

    def start_socket_thread(self):
        """Starts the BCPServer socket thread."""
        self.socket_thread = BCPServer(self, self.receive_queue,
                                       self.sending_queue)
        self.socket_thread.daemon = True
        self.socket_thread.start()

    def get_from_queue(self):
        """Gets and processes all queued up incoming BCP commands."""
        while not self.receive_queue.empty():
            cmd, kwargs = bcp.decode_command_string(
                self.receive_queue.get(False))
            self._process_command(cmd, **kwargs)

    def bcp_hello(self, **kwargs):
        """Processes an incoming BCP 'hello' command."""
        try:
            if LooseVersion(kwargs['version']) == (
                    LooseVersion(version.__bcp_version__)):
                self.send('hello', version=version.__bcp_version__)
            else:
                self.send('hello', version='unknown protocol version')
        except KeyError:
            self.log.warning("Received invalid 'version' parameter with "
                             "'hello'")

    def bcp_goodbye(self, **kwargs):
        """Processes an incoming BCP 'goodbye' command."""
        if self.config['media_controller']['exit_on_disconnect']:
            self.socket_thread.sending_thread.stop()
            sys.exit()

    def bcp_mode_start(self, name=None, priority=0, **kwargs):
        """Processes an incoming BCP 'mode_start' command."""
        if not name:
            return
            #todo raise error

        if name == 'game':
            self._game_start()

        if name in self.modes:
            self.modes[name].start(priority=priority)

    def bcp_mode_stop(self, name, **kwargs):
        """Processes an incoming BCP 'mode_stop' command."""
        if not name:
            return
            #todo raise error

        if name == 'game':
            self._game_end()

        if name in self.modes:
            self.modes[name].stop()

    def bcp_error(self, **kwargs):
        """Processes an incoming BCP 'error' command."""
        self.log.warning('Received error command from client')

    def bcp_ball_start(self, **kwargs):
        """Processes an incoming BCP 'ball_start' command."""
        kwargs['player'] = kwargs.pop('player_num')

        self.events.post('ball_started', **kwargs)

    def bcp_ball_end(self, **kwargs):
        """Processes an incoming BCP 'ball_end' command."""
        self.events.post('ball_ended', **kwargs)

    def _game_start(self, **kargs):
        """Processes an incoming BCP 'game_start' command."""
        self.player = None
        self.player_list = list()
        self.num_players = 0
        self.events.post('game_started', **kargs)

    def _game_end(self, **kwargs):
        """Processes an incoming BCP 'game_end' command."""
        self.player = None
        self.events.post('game_ended', **kwargs)

    def bcp_player_add(self, player_num, **kwargs):
        """Processes an incoming BCP 'player_add' command."""

        if player_num > len(self.player_list):
            new_player = Player(self, self.player_list)

            self.events.post('player_add_success', num=player_num)

    def bcp_player_variable(self, name, value, prev_value, change, player_num,
                            **kwargs):
        """Processes an incoming BCP 'player_variable' command."""

        try:
            self.player_list[int(player_num)-1][name] = value
        except (IndexError, KeyError):
            pass

    def bcp_machine_variable(self, name, value, **kwargs):
        """Processes an incoming BCP 'machine_variable' command."""

        self._set_machine_var(name, value)

    def bcp_player_score(self, value, prev_value, change, player_num,
                         **kwargs):
        """Processes an incoming BCP 'player_score' command."""

        try:
            self.player_list[int(player_num)-1]['score'] = int(value)
        except (IndexError, KeyError):
            pass

    def bcp_player_turn_start(self, player_num, **kwargs):
        """Processes an incoming BCP 'player_turn_start' command."""

        self.log.debug("bcp_player_turn_start")

        if ((self.player and self.player.number != player_num) or
                not self.player):

            try:
                self.player = self.player_list[int(player_num)-1]
            except IndexError:
                self.log.error('Received player turn start for player %s, but '
                               'only %s player(s) exist',
                               player_num, len(self.player_list))

    def bcp_trigger(self, name, **kwargs):
        """Processes an incoming BCP 'trigger' command."""
        self.events.post(name, **kwargs)

    def bcp_switch(self, name, state, **kwargs):
        """Processes an incoming BCP 'switch' command."""
        if int(state):
            self.events.post('switch_' + name + '_active')
        else:
            self.events.post('switch_' + name + '_inactive')

    def bcp_get(self, **kwargs):
        """Processes an incoming BCP 'get' command by posting an event
        'bcp_get_<name>'. It's up to an event handler to register for that
        event and to send the response BCP 'set' command.

        """
        for name in Util.string_to_list(names):
            self.events.post('bcp_get_{}'.format(name))

    def bcp_set(self, **kwargs):
        """Processes an incoming BCP 'set' command by posting an event
        'bcp_set_<name>' with a parameter value=<value>. It's up to an event
        handler to register for that event and to do something with it.

        Note that BCP set commands can contain multiple key/value pairs, and
        this method will post one event for each pair.

        """
        for k, v in kwargs.iteritems():
            self.events.post('bcp_set_{}'.format(k), value=v)

    def bcp_shot(self, name, profile, state):
        """The MPF media controller uses triggers instead of shots for its
        display events, so we don't need to pay attention here."""
        pass

    def bcp_config(self, **kwargs):
        """Processes an incoming BCP 'config' command."""
        for k, v in kwargs.iteritems():
            if k.startswith('volume_'):
                self.bcp_set_volume(track=k.split('volume_')[1], value=v)

    def bcp_timer(self, name, action, **kwargs):
        """Processes an incoming BCP 'timer' command."""
        pass

    def bcp_set_volume(self, track, value):
        """Sets the volume based on an incoming BCP 'config' command.

        Args:
            track: String name of the track the volume will set.
            value: Float between 0 and 1 which represents the volume level to
                set.

        Note: At this time only the master volume can be set with this method.

        """
        if track == 'master':
            self.sound.set_volume(value)

        #if track in self.sound.tracks:
            #self.sound.tracks[track]

            # todo add per-track volume support to sound system

    def get_debug_status(self, debug_path):
        if self.options['loglevel'] > 10 or self.options['consoleloglevel'] > 10:
            return True

        class_, module = debug_path.split('|')

        try:
            if module in self.active_debugger[class_]:
                return True
            else:
                return False
        except KeyError:
            return False

    def _set_machine_var(self, name, value):
        try:
            prev_value = self.machine_vars[name]
        except KeyError:
            prev_value = None

        self.machine_vars[name] = value

        try:
            change = value-prev_value
        except TypeError:
            if prev_value != value:
                change = True
            else:
                change = False

        if change:
            self.log.debug("Setting machine_var '%s' to: %s, (prior: %s, "
                           "change: %s)", name, value, prev_value,
                           change)
            self.events.post('machine_var_' + name,
                                     value=value,
                                     prev_value=prev_value,
                                     change=change)

        if self.machine_var_monitor:
            for callback in self.monitors['machine_var']:
                callback(name=name, value=self.vars[name],
                         prev_value=prev_value, change=change)

    def _bcp_client_asset_loader_tick(self, total, remaining):
        self._pc_assets_to_load = int(remaining)
        self._pc_total_assets = int(total)

    def asset_loading_counter(self):

        if self.tick_num % 5 != 0:
            return

        if AssetManager.total_assets or self._pc_total_assets:
            # max because this could go negative at first
            percent = max(0, int(float(AssetManager.total_assets -
                                       self._pc_assets_to_load -
                                       AssetManager.loader_queue.qsize()) /
                                       AssetManager.total_assets * 100))
        else:
            percent = 100

        self.log.debug("Asset Loading Counter. PC remaining:{}, MC remaining:"
                       "{}, Percent Complete: {}".format(
                       self._pc_assets_to_load, AssetManager.loader_queue.qsize(),
                       percent))

        self.events.post('asset_loader',
                         total=AssetManager.loader_queue.qsize() +
                               self._pc_assets_to_load,
                         pc=self._pc_assets_to_load,
                         mc=AssetManager.loader_queue.qsize(),
                         percent=percent)

        if not AssetManager.loader_queue.qsize():

            if not self.pc_connected:
                self.events.post("waiting_for_client_connection")
                self.events.remove_handler(self.asset_loading_counter)

            elif not self._pc_assets_to_load:
                self.log.debug("Asset Loading Complete")
                self.events.post("asset_loading_complete")
                self.send('reset_complete')

                self.events.remove_handler(self.asset_loading_counter)
Пример #42
0
    def __init__(self, machine, name, config, collection=None):
        self.log = logging.getLogger('Diverter.' + name)
        super(Diverter, self).__init__(machine, name, config, collection)

        self.delay = DelayManager()

        # Attributes
        self.active = False
        self.enabled = False

        # configure defaults:
        if 'type' not in self.config:
            self.config['type'] = 'pulse'  # default to pulse to not fry coils
        if 'activation_time' not in self.config:
            self.config['activation_time'] = 0
        if 'activation_switches' in self.config:
            self.config['activation_switches'] = Config.string_to_list(
                self.config['activation_switches'])
        else:
            self.config['activation_switches'] = list()

        if 'disable_switches' in self.config:
            self.config['disable_switches'] = Config.string_to_list(
                self.config['disable_switches'])
        else:
            self.config['disable_switches'] = list()

        if 'deactivation_switches' in self.config:
            self.config['deactivation_switches'] = Config.string_to_list(
                self.config['deactivation_switches'])
        else:
            self.config['deactivation_switches'] = list()

        if 'activation_coil' in self.config:
            self.config['activation_coil'] = (
                self.machine.coils[self.config['activation_coil']])

        if 'deactivation_coil' in self.config:
            self.config['deactivation_coil'] = (
                self.machine.coils[self.config['deactivation_coil']])
        else:
            self.config['deactivation_coil'] = None

        if 'targets_when_active' in self.config:
            self.config['targets_when_active'] = Config.string_to_list(
                self.config['targets_when_active'])
        else:
            self.config['targets_when_active'] = ['playfield']

        if 'targets_when_inactive' in self.config:
            self.config['targets_when_inactive'] = Config.string_to_list(
                self.config['targets_when_inactive'])
        else:
            self.config['targets_when_inactive'] = ['playfield']

        if 'feeder_devices' in self.config:
            self.config['feeder_devices'] = Config.string_to_list(
                self.config['feeder_devices'])
        else:
            self.config['feeder_devices'] = list()

        # Create a list of ball device objects when active and inactive. We need
        # this because ball eject attempts pass the target device as an object
        # rather than by name.

        self.config['active_objects'] = list()
        self.config['inactive_objects'] = list()

        for target_device in self.config['targets_when_active']:
            if target_device == 'playfield':
                self.config['active_objects'].append('playfield')
            else:
                self.config['active_objects'].append(
                    self.machine.balldevices[target_device])

        for target_device in self.config['targets_when_inactive']:
            if target_device == 'playfield':
                self.config['inactive_objects'].append('playfield')
            else:
                self.config['inactive_objects'].append(
                    self.machine.balldevices[target_device])

        # convert the activation_time to ms
        self.config['activation_time'] = Timing.string_to_ms(self.config['activation_time'])

        # register for events
        for event in self.config['enable_events']:
            self.machine.events.add_handler(event, self.enable)

        for event in self.config['disable_events']:
            self.machine.events.add_handler(event, self.disable)

        # register for feeder device eject events
        for feeder_device in self.config['feeder_devices']:
            self.machine.events.add_handler('balldevice_' + feeder_device +
                                            '_ball_eject_attempt',
                                            self._feeder_eject_attempt)

        # register for deactivation switches
        for switch in self.config['deactivation_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch, self.deactivate)

        # register for disable switches:
        for switch in self.config['disable_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch, self.disable)
Пример #43
0
    def validate_config_item(spec, item='item not in config!@#'):

        try:
            if item.lower() == 'none':
                item = None
        except AttributeError:
            pass

        default = 'default required!@#'

        if '|' in spec:
            item_type, default = spec.split('|')
            if type(default) is str and default.lower() == 'none':
                default = None
        else:
            item_type = spec

        if item == 'item not in config!@#':
            if default == 'default required!@#':
                log.error(
                    'Required setting missing from config file. Run with '
                    'verbose logging and look for the last '
                    'ConfigProcessor entry above this line to see where '
                    'the problem is.')
                sys.exit()
            else:
                item = default

        if item_type == 'list':
            return Util.string_to_list(item)

        if item_type == 'list_of_dicts':
            if type(item) is list:
                return item
            elif type(item) is dict:
                return [item]

        elif item_type == 'set':
            return set(Util.string_to_list(item))

        elif item_type == 'dict':
            if type(item) is dict or type(item) is CaseInsensitiveDict:
                return item
            elif not default:
                return dict()
            else:
                log.error('Config error. "%s" is not a dictionary', item)
                sys.exit()

        elif item_type == 'int':
            try:
                return int(item)
            except TypeError:
                return None

        elif item_type == 'float':
            try:
                return float(item)
            except TypeError:
                return None

        elif item_type in ('string', 'str'):

            if item:
                return str(item)
            else:
                return None

        elif item_type in ('boolean', 'bool'):
            if type(item) is bool:
                return item
            else:
                return str(item).lower() in ('yes', 'true')

        elif item_type == 'ms':
            return Timing.string_to_ms(item)

        elif item_type == 'secs':
            return Timing.string_to_secs(item)

        elif item_type == 'list_of_lists':
            return Util.list_of_lists(item)
Пример #44
0
Файл: mode.py Проект: jherrm/mpf
    def __init__(self, machine, mode, name, config):
        self.machine = machine
        self.mode = mode
        self.name = name
        self.config = config

        self.tick_var = self.mode.name + '_' + self.name + '_tick'
        self.mode.player[self.tick_var] = 0

        self.running = False
        self.start_value = 0
        self.restart_on_complete = False
        self._ticks = 0
        self.end_value = None
        self.ticks_remaining = 0
        self.max_value = None
        self.direction = 'up'
        self.tick_secs = 1
        self.timer = None
        self.bcp = False
        self.event_keys = set()
        self.delay = DelayManager()
        self.log = None
        self.debug = False

        if 'start_value' in self.config:
            self.start_value = self.config['start_value']
        else:
            self.start_value = 0

        if 'start_running' in self.config and self.config['start_running']:
            self.running = True

        if 'end_value' in self.config:
            self.end_value = self.config['end_value']

        if 'control_events' in self.config and self.config['control_events']:
            if type(self.config['control_events']) is dict:
                self.config['control_events'] = [self.config['control_events']]
        else:
            self.config['control_events'] = list()

        if ('direction' in self.config and
                self.config['direction'].lower() == 'down'):
            self.direction = 'down'

            if not self.end_value:
                self.end_value = 0  # need it to be 0 not None

        if 'tick_interval' in self.config:
            self.tick_secs = Timing.string_to_secs(self.config['tick_interval'])

        if 'max_value' in self.config:
            self.max_value = self.config['max_value']

        if ('restart_on_complete' in self.config and
                self.config['restart_on_complete']):
            self.restart_on_complete = True

        if 'bcp' in self.config and self.config['bcp']:
            self.bcp = True

        if 'debug' in self.config and self.config['debug']:
            self.debug = True
            self.log.debug("Enabling Debug Logging")

        self.mode.player[self.tick_var] = self.start_value

        if self.log:
            self.log.debug("----------- Initial Values -----------")
            self.log.debug("running: %s", self.running)
            self.log.debug("start_value: %s", self.start_value)
            self.log.debug("restart_on_complete: %s", self.restart_on_complete)
            self.log.debug("_ticks: %s", self._ticks)
            self.log.debug("end_value: %s", self.end_value)
            self.log.debug("ticks_remaining: %s", self.ticks_remaining)
            self.log.debug("max_value: %s", self.max_value)
            self.log.debug("direction: %s", self.direction)
            self.log.debug("tick_secs: %s", self.tick_secs)
            self.log.debug("--------------------------------------")

        self._setup_control_events(self.config['control_events'])
Пример #45
0
    def preprocess_settings(self, settings, base_priority=0):
        """Takes an unstructured list of SlidePlayer settings and processed them
        so they can be displayed.

        Args:
            settings: A list of dictionary of SlidePlayer settings for a slide.
            base_priority: An integer that will be added to slide's priority
                from the config settings.

        Returns: A python list with all the settings in the right places.

        This method does a bunch of things, like making sure all the needed
        values are there, and moving certain things to the first and last
        elements when there are multiple elements used on one slide. (For
        example, if one of the elements wants to clear the slide, it has to
        happen first. If there's a transition, it has to happen last after the
        slide is built, etc.

        The returned settings list can be safely called with the by display()
        with the preprocessed=True flag.

        """

        # This is a stupid band-aid because when modes load their slideplayer
        # settings are already processed. I don't know why though, but I don't
        # have time to track it down now. $50 to anyone who figures out why!!!

        # Settings can be a list of dicts or just a dict. (Preprocessing is what
        # turns a dict into a list, though I don't know how sometimes items are
        # getting the preprocessed entry in their dict but they're not a list???
        # todo

        if type(settings) is list and 'preprocessed' in settings[0]:
            return settings
        elif type(settings) is dict and 'preprocessed' in settings:
            return [settings]

        processed_settings = list()

        if type(settings) is dict:
            settings = [settings]

        last_settings = dict()
        first_settings = dict()

        # Drop this key into the settings so we know they've been preprocessed.
        first_settings['preprocessed'] = True

        for element in settings:

            # Create a slide name based on the event name if one isn't specified
            if 'slide_name' in element:
                first_settings['slide_name'] = element.pop('slide_name')

            if 'removal_key' in element:
                first_settings['removal_key'] = element.pop('removal_key')

            # If the config doesn't specify whether this slide should be made
            # active when this event is called, set a default value of True
            if 'slide_priority' in element:
                first_settings['slide_priority'] = (
                    element.pop('slide_priority') + base_priority)

            # If a 'clear_slide' setting isn't specified, set a default of True
            if 'clear_slide' in element:
                first_settings['clear_slide'] = element.pop('clear_slide')

            # If a 'persist_slide' setting isn't specified, set default of False
            if 'persist_slide' in element:
                first_settings['persist_slide'] = element.pop('persist_slide')

            if 'display' in element:
                first_settings['display'] = element.pop('display')

            if 'transition' in element:
                last_settings['transition'] = element.pop('transition')

            if 'name' not in element:
                element['name'] = None

            if 'expire' in element:
                first_settings['expire'] = Timing.string_to_ms(
                    element.pop('expire'))
            else:
                first_settings['expire'] = 0

            processed_settings.append(element)

        if 'slide_priority' not in first_settings:
            first_settings['slide_priority'] = base_priority

        if 'removal_key' not in first_settings:
            first_settings['removal_key'] = None

        # Now add back in the items that need to be in the first element
        processed_settings[0].update(first_settings)

        # And add the settings we need to the last entry
        processed_settings[-1].update(last_settings)

        return processed_settings
Пример #46
0
    def preprocess_settings(self, settings, base_priority=0):
        """Takes an unstructured list of slide_player settings and processed them
        so they can be displayed.

        Args:
            settings: A list of dictionary of slide_player settings for a slide.
            base_priority: An integer that will be added to slide's priority
                from the config settings.

        Returns: A python list with all the settings in the right places.

        This method does a bunch of things, like making sure all the needed
        values are there, and moving certain things to the first and last
        elements when there are multiple elements used on one slide. (For
        example, if one of the elements wants to clear the slide, it has to
        happen first. If there's a transition, it has to happen last after the
        slide is built, etc.

        The returned settings list can be safely called with the by display()
        with the preprocessed=True flag.

        """

        # This is a stupid band-aid because when modes load their slide_player
        # settings are already processed. I don't know why though, but I don't
        # have time to track it down now. $50 to anyone who figures out why!!!

        # Settings can be a list of dicts or just a dict. (Preprocessing is what
        # turns a dict into a list, though I don't know how sometimes items are
        # getting the preprocessed entry in their dict but they're not a list???
        # todo

        if not settings:
            settings = list()
            settings.append(dict())
        else:
            settings = deepcopy(settings)

        if type(settings) is list and 'preprocessed' in settings[0]:
            return settings
        elif type(settings) is dict and 'preprocessed' in settings:
            return [settings]

        processed_settings = list()

        if type(settings) is dict:
            settings = [settings]

        last_settings = dict()
        first_settings = dict()

        first_settings['preprocessed'] = True
        first_settings['persist_slide'] = False
        first_settings['clear_slide'] = False
        first_settings['expire'] = 0
        first_settings['slide_name'] = None

        for element in settings:

            # Create a slide name based on the event name if one isn't specified
            if 'slide_name' in element:
                first_settings['slide_name'] = element.pop('slide_name')

            # If the config doesn't specify whether this slide should be made
            # active when this event is called, set a default value of True
            if 'slide_priority' in element:
                first_settings['slide_priority'] = (
                    element.pop('slide_priority') + base_priority)

            if 'clear_slide' in element:
                first_settings['clear_slide'] = element.pop('clear_slide')

            if 'slide' in element:
                first_settings['slide_name'] = element.pop('slide')

            if 'persist_slide' in element:
                first_settings['persist_slide'] = element.pop('persist_slide')

            if 'display' in element:
                first_settings['display'] = element.pop('display')

            if 'transition' in element:
                last_settings['transition'] = element.pop('transition')

            if 'expire' in element:
                first_settings['expire'] = Timing.string_to_ms(
                    element.pop('expire'))

            processed_settings.append(element)

        if 'slide_priority' not in first_settings:
            first_settings['slide_priority'] = base_priority

        # Now add back in the items that need to be in the first element
        processed_settings[0].update(first_settings)

        # And add the settings we need to the last entry
        processed_settings[-1].update(last_settings)

        return processed_settings
Пример #47
0
    def validate_config_item(spec, item='item not in config!@#'):

        try:
            if item.lower() == 'none':
                item = None
        except AttributeError:
            pass

        default = 'default required!@#'

        if '|' in spec:
            item_type, default = spec.split('|')
            if type(default) is str and default.lower() == 'none':
                default = None
        else:
            item_type = spec

        if item == 'item not in config!@#':
            if default == 'default required!@#':
                log.error('Required setting missing from config file. Run with '
                          'verbose logging and look for the last '
                          'ConfigProcessor entry above this line to see where '
                          'the problem is.')
                sys.exit()
            else:
                item = default

        if item_type == 'list':
            return Util.string_to_list(item)

        if item_type == 'list_of_dicts':
            if type(item) is list:
                return item
            elif type(item) is dict:
                return [item]

        elif item_type == 'set':
            return set(Util.string_to_list(item))

        elif item_type == 'dict':
            if type(item) is dict or type(item) is CaseInsensitiveDict:
                return item
            elif not default:
                return dict()
            else:
                log.error('Config error. "%s" is not a dictionary', item)
                sys.exit()

        elif item_type == 'int':
            try:
                return int(item)
            except TypeError:
                return None

        elif item_type == 'float':
            try:
                return float(item)
            except TypeError:
                return None

        elif item_type in ('string', 'str'):

            if item:
                return str(item)
            else:
                return None

        elif item_type in ('boolean', 'bool'):
            if type(item) is bool:
                return item
            else:
                return str(item).lower() in ('yes', 'true')

        elif item_type == 'ms':
            return Timing.string_to_ms(item)

        elif item_type == 'secs':
            return Timing.string_to_secs(item)

        elif item_type == 'list_of_lists':
            return Util.list_of_lists(item)
Пример #48
0
    def _initialize(self):
        # convert names to objects

        if self.config['ball_switches']:
            for i in range(len(self.config['ball_switches'])):
                self.config['ball_switches'][i] = (
                    self.machine.switches[self.config['ball_switches'][i]])

        if self.config['eject_coil']:
            self.config['eject_coil'] = (
                self.machine.coils[self.config['eject_coil']])

        if self.config['eject_switch']:
            self.config['eject_switch'] = (
                self.machine.switches[self.config['eject_switch']])

        if self.config['entrance_switch']:
            self.config['entrance_switch'] = (
                self.machine.switches[self.config['entrance_switch']])

        if self.config['jam_switch']:
            self.config['jam_switch'] = (
                self.machine.switches[self.config['jam_switch']])

        if self.config['confirm_eject_type'] == 'switch' and (
                self.config['confirm_eject_target']):
            self.config['confirm_eject_switch'] = (
                self.machine.switches[self.config['confirm_eject_switch']])

        if self.config['eject_targets']:
            for i in range(len(self.config['eject_targets'])):
                self.config['eject_targets'][i] = (
                    self.machine.balldevices[self.config['eject_targets'][i]])

        # make sure the eject timeouts list matches the length of the eject targets
        if (len(self.config['eject_timeouts']) < len(
                self.config['eject_targets'])):
            self.config['eject_timeouts'] += [None] * (
                len(self.config['eject_targets']) -
                len(self.config['eject_timeouts']))

        timeouts_list = self.config['eject_timeouts']
        self.config['eject_timeouts'] = dict()

        for i in range(len(self.config['eject_targets'])):
            self.config['eject_timeouts'][self.config['eject_targets'][i]] = (
                Timing.string_to_ms(timeouts_list[i]))
        # End code to create timeouts list -------------------------------------

        # Register switch handlers with delays for entrance & exit counts
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name,
                state=1,
                ms=self.config['entrance_count_delay'],
                callback=self.count_balls)
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name,
                state=0,
                ms=self.config['exit_count_delay'],
                callback=self.count_balls)
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name,
                state=1,
                ms=0,
                callback=self._invalidate)
        for switch in self.config['ball_switches']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=switch.name,
                state=0,
                ms=0,
                callback=self._invalidate)

        # Configure switch handlers for jam switch activity
        if self.config['jam_switch']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=self.config['jam_switch'].name,
                state=1,
                ms=0,
                callback=self._jam_switch_handler)
            # todo do we also need to add inactive and make a smarter
            # handler?

        # Configure switch handlers for entrance switch activity
        if self.config['entrance_switch']:
            self.machine.switch_controller.add_switch_handler(
                switch_name=self.config['entrance_switch'].name,
                state=1,
                ms=0,
                callback=self._entrance_switch_handler)
            # todo do we also need to add inactive and make a smarter
            # handler?

        # Configure event handlers to watch for target device status changes
        for target in self.config['eject_targets']:
            # Target device is requesting a ball
            self.machine.events.add_handler('balldevice_' + target.name +
                                            '_ball_request',
                                            self.eject,
                                            target=target,
                                            get_ball=True)

            # Target device is now able to receive a ball
            self.machine.events.add_handler(
                'balldevice_' + target.name + '_ok_to_receive', self._do_eject)

        # Get an initial ball count
        self.count_balls(stealth=True)
Пример #49
0
    def __init__(self, options):
        self.options = options

        self.log = logging.getLogger("MediaController")
        self.log.debug("Command line arguments: {}".format(self.options))
        self.log.info("Media Controller Version %s", version.__version__)
        self.log.debug("Backbox Control Protocol Version %s",
                      version.__bcp_version__)
        self.log.debug("Config File Version %s",
                      version.__config_version__)

        python_version = sys.version_info

        if python_version[0] != 2 or python_version[1] != 7:
            self.log.error("Incorrect Python version. MPF requires Python 2.7."
                           "x. You have Python %s.%s.%s.", python_version[0],
                           python_version[1], python_version[2])
            sys.exit()

        self.log.debug("Python version: %s.%s.%s", python_version[0],
                      python_version[1], python_version[2])
        self.log.debug("Platform: %s", sys.platform)
        self.log.debug("Python executable location: %s", sys.executable)
        self.log.debug("32-bit Python? %s", sys.maxsize < 2**32)

        self.active_debugger = dict()

        self.config = dict()
        self.done = False  # todo
        self.machine_path = None
        self.asset_managers = dict()
        self.window = None
        self.window_manager = None
        self.pygame = False
        self.pygame_requested = False
        self.registered_pygame_handlers = dict()
        self.pygame_allowed_events = list()
        self.socket_thread = None
        self.receive_queue = Queue.Queue()
        self.sending_queue = Queue.Queue()
        self.crash_queue = Queue.Queue()
        self.modes = CaseInsensitiveDict()
        self.player_list = list()
        self.player = None
        self.HZ = 0
        self.next_tick_time = 0
        self.secs_per_tick = 0
        self.machine_vars = CaseInsensitiveDict()
        self.machine_var_monitor = False
        self.tick_num = 0
        self.delay = DelayManager()

        self._pc_assets_to_load = 0
        self._pc_total_assets = 0
        self.pc_connected = False

        Task.create(self._check_crash_queue)

        self.bcp_commands = {'ball_start': self.bcp_ball_start,
                             'ball_end': self.bcp_ball_end,
                             'config': self.bcp_config,
                             'error': self.bcp_error,
                             'get': self.bcp_get,
                             'goodbye': self.bcp_goodbye,
                             'hello': self.bcp_hello,
                             'machine_variable': self.bcp_machine_variable,
                             'mode_start': self.bcp_mode_start,
                             'mode_stop': self.bcp_mode_stop,
                             'player_added': self.bcp_player_add,
                             'player_score': self.bcp_player_score,
                             'player_turn_start': self.bcp_player_turn_start,
                             'player_variable': self.bcp_player_variable,
                             'reset': self.reset,
                             'set': self.bcp_set,
                             'shot': self.bcp_shot,
                             'switch': self.bcp_switch,
                             'timer': self.bcp_timer,
                             'trigger': self.bcp_trigger,
                            }

        FileManager.init()
        self.config = dict()
        self._load_mc_config()
        self._set_machine_path()
        self._load_machine_config()

        # Find the machine_files location. If it starts with a forward or
        # backward slash, then we assume it's from the mpf root. Otherwise we
        # assume it's from the subfolder location specified in the
        # mpfconfig file location

        if (options['machine_path'].startswith('/') or
                options['machine_path'].startswith('\\')):
            machine_path = options['machine_path']
        else:
            machine_path = os.path.join(self.config['media_controller']['paths']
                                        ['machine_files'],
                                        options['machine_path'])

        self.machine_path = os.path.abspath(machine_path)

        # Add the machine folder to our path so we can import modules from it
        sys.path.append(self.machine_path)

        self.log.info("Machine folder: %s", machine_path)

        mediacontroller_config_spec = '''
                        exit_on_disconnect: boolean|True
                        port: int|5050
                        '''

        self.config['media_controller'] = (
            Config.process_config(mediacontroller_config_spec,
                                  self.config['media_controller']))

        self.events = EventManager(self, setup_event_player=False)
        self.timing = Timing(self)

        # Load the media controller modules
        self.config['media_controller']['modules'] = (
            self.config['media_controller']['modules'].split(' '))
        self.log.info("Loading Modules...")
        for module in self.config['media_controller']['modules']:
            self.log.debug("Loading module: %s", module)
            module_parts = module.split('.')
            exec('self.' + module_parts[0] + '=' + module + '(self)')

            # todo there's probably a more pythonic way to do this, and I know
            # exec() is supposedly unsafe, but meh, if you have access to put
            # malicious files in the system folder then you have access to this
            # code too.

        self.start_socket_thread()

        self.events.post("init_phase_1")
        self.events._process_event_queue()
        self.events.post("init_phase_2")
        self.events._process_event_queue()
        self.events.post("init_phase_3")
        self.events._process_event_queue()
        self.events.post("init_phase_4")
        self.events._process_event_queue()
        self.events.post("init_phase_5")
        self.events._process_event_queue()

        self.reset()