def validate_config_item2(self, spec, validation_failure_info, item='item not in config!@#',): default = 'default required!@#' item_type, validation, default = spec.split('|') if default.lower() == 'none': default = None 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 == 'single': item = self.validate_item(item, validation, validation_failure_info) elif item_type == 'list': item = Util.string_to_list(item) new_list = list() for i in item: new_list.append( self.validate_item(i, validation, validation_failure_info)) item = new_list elif item_type == 'set': item = set(Util.string_to_list(item)) new_set = set() for i in item: new_set.add( self.validate_item(i, validation, validation_failure_info)) item = new_set elif item_type == 'dict': item = self.validate_item(item, validation, validation_failure_info) if not item: item = dict() else: self.log.error("Invalid Type '%s' in config spec %s:%s", item_type, validation_failure_info[0][0], validation_failure_info[1]) sys.exit() return item
def configure_mode_settings(self, config): """Processes this mode's configuration settings from a config dictionary. """ if not ('priority' in config and type(config['priority']) is int): config['priority'] = 0 if 'start_events' in config: config['start_events'] = Util.string_to_list( config['start_events']) else: config['start_events'] = list() if 'stop_events' in config: config['stop_events'] = Util.string_to_list(config['stop_events']) else: config['stop_events'] = list() # register mode start events if 'start_events' in config: for event in config['start_events']: self.machine.events.add_handler(event, self.start) self.config['mode'] = config
def configure_led(self, config): if not self.rgb_connection: self.log.critical("A request was made to configure a FAST LED, " "but no connection to an LED processor is " "available") sys.exit() if not self.flag_led_tick_registered: self.machine.events.add_handler('timer_tick', self.update_leds) self.flag_led_tick_registered = True # if the LED number is in <channel> - <led> format, convert it to a # FAST hardware number if '-' in config['number_str']: num = config['number_str'].split('-') config['number'] = (int(num[0]) * 64) + int(num[1]) self.config['config_number_format'] = 'int' else: config['number'] = str(config['number']) if self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) this_fast_led = FASTDirectLED(config['number']) self.fast_leds.add(this_fast_led) return this_fast_led
def receive_sa(self, msg): self.log.info("Received SA: %s", msg) hw_states = dict() num_local, local_states, num_nw, nw_states = msg.split(',') for offset, byte in enumerate(bytearray.fromhex(nw_states)): for i in range(8): num = Util.int_to_hex_string((offset * 8) + i) if byte & (2**i): hw_states[(num, 1)] = 1 else: hw_states[(num, 1)] = 0 for offset, byte in enumerate(bytearray.fromhex(local_states)): for i in range(8): num = Util.int_to_hex_string((offset * 8) + i) if byte & (2**i): hw_states[(num, 0)] = 1 else: hw_states[(num, 0)] = 0 self.hw_switch_data = hw_states
def configure_mode_settings(self, config): """Processes this mode's configuration settings from a config dictionary. """ if not ('priority' in config and type(config['priority']) is int): config['priority'] = 0 if 'start_events' in config: config['start_events'] = Util.string_to_list( config['start_events']) else: config['start_events'] = list() if 'stop_events' in config: config['stop_events'] = Util.string_to_list( config['stop_events']) else: config['stop_events'] = list() # register mode start events if 'start_events' in config: for event in config['start_events']: self.machine.events.add_handler(event, self.start) self.config['mode'] = config
def __init__(self, slide, machine, dmd_object=None, x=None, y=None, h_pos=None, v_pos=None, layer=0, **kwargs): super(VirtualDMD, self).__init__(slide, x, y, h_pos, v_pos, layer) if not dmd_object: self.dmd_object = machine.display.displays["dmd"] else: self.dmd_object = dmd_object self.config = kwargs self.name = "VirtualDMD" if self.dmd_object.depth == 8: if "pixel_color" not in kwargs: self.config["pixel_color"] = "ff5500" if "dark_color" not in self.config: self.config["dark_color"] = "221100" if "pixel_spacing" not in self.config: self.config["pixel_spacing"] = 2 # convert hex colors to list of ints self.config["pixel_color"] = Util.hex_string_to_list(self.config["pixel_color"]) self.config["dark_color"] = Util.hex_string_to_list(self.config["dark_color"]) # This needs to match the source DMD or it could get weird self.config["shades"] = self.dmd_object.config["shades"] self.palette = mpf.media_controller.display_modules.dmd.create_palette( bright_color=self.config["pixel_color"], dark_color=self.config["dark_color"], steps=self.config["shades"], ) if "width" in self.config and "height" not in self.config: self.config["height"] = self.config["width"] / 4 elif "height" in self.config and "width" not in self.config: self.config["width"] = self.config["height"] * 4 elif "width" not in self.config and "height" not in self.config: self.config["width"] = 512 self.config["height"] = 128 # Create a Pygame surface for the on screen DMD self.element_surface = pygame.Surface( (self.config["width"], self.config["height"]), depth=self.dmd_object.depth ) if self.dmd_object.depth == 8: self.element_surface.set_palette(self.palette) self.layer = layer self.set_position(x, y, h_pos, v_pos)
def load_config_file(filename, verify_version=True, halt_on_error=True): config = FileManager.load(filename, verify_version, halt_on_error) if 'config' in config: path = os.path.split(filename)[0] for file in Util.string_to_list(config['config']): full_file = os.path.join(path, file) config = Util.dict_merge(config, Config.load_config_file(full_file)) return config
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)
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)
def load(self, filename, verify_version=True): """Loads a YAML file from disk. Args: filename: The file to load. verify_version: Boolean which specifies whether this method should verify whether this file's config_version is compatible with this version of MPF. Default is True. Returns: A dictionary of the settings from this YAML file. """ config = Util.keys_to_lower( self.byteify(json.load(open(filename, 'r')))) # if verify_version: # self.check_config_file_version(filename) # # try: # self.log.debug("Loading configuration file: %s", filename) # config = Util.keys_to_lower(json.loads(open(filename, 'r'))) # except yaml.YAMLError, exc: # if hasattr(exc, 'problem_mark'): # mark = exc.problem_mark # self.log.critical("Error found in config file %s. Line %s, " # "Position %s", filename, mark.line+1, # mark.column+1) # sys.exit() # except: # self.log.critical("Couldn't load from file: %s", filename) # raise return config
def pulse(self, milliseconds=None): """Pulses this driver. """ if not milliseconds: hex_ms_string = self.driver_settings['pulse_ms'] else: hex_ms_string = Util.int_to_hex_string(milliseconds) if self.autofire: cmd = (self.driver_settings['trigger_cmd'] + self.driver_settings['number'] + ',' + '01') if milliseconds: self.log.debug("Received command to pulse driver for %sms, but" "this driver is configured with an autofire rule" ", so that pulse value will be used instead.") else: cmd = (self.driver_settings['config_cmd'] + self.driver_settings['number'] + ',89,00,10,' + hex_ms_string + ',' + self.driver_settings['pwm1'] + ',00,00,' + self.driver_settings['recycle_ms']) self.log.debug("Sending Pulse Command: %s", cmd) self.send(cmd) self.check_auto()
def load(self, filename, verify_version=True): """Loads a YAML file from disk. Args: filename: The file to load. verify_version: Boolean which specifies whether this method should verify whether this file's config_version is compatible with this version of MPF. Default is True. Returns: A dictionary of the settings from this YAML file. """ config = Util.keys_to_lower(self.byteify(json.load(open(filename, 'r')))) # if verify_version: # self.check_config_file_version(filename) # # try: # self.log.debug("Loading configuration file: %s", filename) # config = Util.keys_to_lower(json.loads(open(filename, 'r'))) # except yaml.YAMLError, exc: # if hasattr(exc, 'problem_mark'): # mark = exc.problem_mark # self.log.critical("Error found in config file %s. Line %s, " # "Position %s", filename, mark.line+1, # mark.column+1) # sys.exit() # except: # self.log.critical("Couldn't load from file: %s", filename) # raise return config
def __init__(self, machine, config): super(FadeCandyOPClient, self).__init__(machine, config) self.log = logging.getLogger('FadeCandyClient') self.update_every_tick = True self.gamma = self.machine.config['led_settings']['gamma'] self.whitepoint = Util.string_to_list( self.machine.config['led_settings']['whitepoint']) self.whitepoint[0] = float(self.whitepoint[0]) self.whitepoint[1] = float(self.whitepoint[1]) self.whitepoint[2] = float(self.whitepoint[2]) self.linear_slope = ( self.machine.config['led_settings']['linear_slope']) self.linear_cutoff = ( self.machine.config['led_settings']['linear_cutoff']) self.keyframe_interpolation = ( self.machine.config['led_settings']['keyframe_interpolation']) self.dithering = self.machine.config['led_settings']['dithering'] if not self.dithering: self.disable_dithering() if not self.keyframe_interpolation: self.update_every_tick = False self.set_global_color_correction() self.write_firmware_options()
def get_hw_switch_states(self): if not self.initial_states_sent: if 'virtual_platform_start_active_switches' in self.machine.config: initial_active_switches = [ self.machine.switches[x].number for x in Util.string_to_list( self.machine. config['virtual_platform_start_active_switches']) ] for k, v in self.hw_switches.iteritems(): if k in initial_active_switches: self.hw_switches[k] ^= 1 self.initial_states_sent = True else: switches = [x for x in self.machine.switches if x.platform == self] for switch in switches: self.hw_switches[switch.number] = switch.state ^ switch.invert return self.hw_switches
def __init__(self, machine, name, player, config): self.machine = machine self.name = name self.player = player self.handler_keys = set() self.enabled = False config_spec = ''' enable_events: list|None disable_events: list|None reset_events: list|None restart_events: list|None restart_on_complete: boolean|False disable_on_complete: boolean|True persist_state: boolean|False ''' self.config = Config.process_config(config_spec=config_spec, source=config) if 'events_when_complete' not in config: self.config['events_when_complete'] = ([ 'logicblock_' + self.name + '_complete']) else: self.config['events_when_complete'] = Util.string_to_list( config['events_when_complete'])
def __init__(self, machine, name, config, collection=None, validate=True): self.shots = list() # list of strings for shot in Util.string_to_list(config['shots']): self.shots.append(machine.shots[shot]) # If this device is setup in a machine-wide config, make sure it has # a default enable event. # TODO add a mode parameter to the device constructor and do the logic # there. if not machine.modes: if 'enable_events' not in config: config['enable_events'] = 'ball_starting' if 'disable_events' not in config: config['disable_events'] = 'ball_ended' if 'reset_events' not in config: config['reset_events'] = 'ball_ended' if 'profile' in config: for shot in self.shots: shot.update_enable_table(profile=config['profile'], mode=None) super(ShotGroup, self).__init__(machine, name, config, collection, validate=validate) self.rotation_enabled = True if self.debug: self._enable_related_device_debugging()
def bcp_receive_get(self, names, **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.machine.events.post('bcp_get_{}'.format(name))
def getOptions(self): return { 'force_platform': self.get_platform(), 'mpfconfigfile': "mpf/mpfconfig.yaml", 'machine_path': self.getMachinePath(), 'configfile': Util.string_to_list(self.getConfigFile()), 'debug': True, 'bcp': self.get_use_bcp() }
def _load_mode(self, mode_string): """Loads a mode, reads in its config, and creates the Mode object. Args: mode: String name of the mode you're loading. This is the name of the mode's folder in your game's machine_files/modes folder. """ self.log.debug('Processing mode: %s', mode_string) config = dict() # find the folder for this mode: mode_path = os.path.join( self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string) if not os.path.exists(mode_path): mode_path = os.path.abspath( os.path.join( 'mpf', self.machine.config['media_controller']['paths']['modes'], mode_string)) # Is there an MPF default config for this mode? If so, load it first mpf_mode_config = os.path.join( 'mpf', self.machine.config['media_controller']['paths']['modes'], mode_string, 'config', mode_string + '.yaml') if os.path.isfile(mpf_mode_config): config = Config.load_config_file(mpf_mode_config) # Now figure out if there's a machine-specific config for this mode, and # if so, merge it into the config mode_config_folder = os.path.join( self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string, 'config') found_file = False for path, _, files in os.walk(mode_config_folder): for file in files: file_root, file_ext = os.path.splitext(file) if file_root == mode_string: config = Util.dict_merge( config, Config.load_config_file(os.path.join(path, file))) found_file = True break if found_file: break return Mode(self.machine, config, mode_string, mode_path)
def configure_matrixlight(self, config): if not self.net_connection: self.log.critical("A request was made to configure a FAST matrix " "light, but no connection to a NET processor is " "available") sys.exit() if self.machine_type == 'wpc': # translate number to FAST light num config['number'] = self.wpc_light_map.get( config['number_str'].upper()) elif self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) return (FASTMatrixLight(config['number'], self.net_connection.send), config['number'])
def register_mpfmc_trigger_events(self, config, **kwargs): """Scans an MPF config file and creates trigger events for the config settings that need them. Args: config: An MPF config dictionary (can be the machine-wide or a mode- specific one). **kwargs: Not used. Included to catch any additional kwargs that may be associted with this method being registered as an event handler. """ self.log.debug("Registering Trigger Events") try: for event in config['show_player'].keys(): self.create_trigger_event(event) except KeyError: pass try: for event in config['slide_player'].keys(): self.create_trigger_event(event) except KeyError: pass try: for event in config['event_player'].keys(): self.create_trigger_event(event) except KeyError: pass try: for k, v in config['sound_player'].iteritems(): if 'start_events' in v: for event in Util.string_to_list(v['start_events']): self.create_trigger_event(event) if 'stop_events' in v: for event in Util.string_to_list(v['stop_events']): self.create_trigger_event(event) except KeyError: pass
def _configure(self): self.config = self.machine.config['languages'] self.machine.language = self self.languages = Util.string_to_lowercase_list( self.machine.config['languages']) # Set the default language to the first entry in the list self.set_language(self.languages[0]) self.default_language = self.languages[0] self.find_text = re.compile('(\(.*?\))')
def create_driver_settings(self, machine, pulse_ms=None, **kwargs): return_dict = dict() if pulse_ms is None: pulse_ms = machine.config['mpf']['default_pulse_ms'] return_dict['pulse_ms'] = Util.int_to_hex_string(pulse_ms) return_dict['pwm1'] = 'ff' return_dict['pwm2'] = 'ff' return_dict['recycle_ms'] = '00' return return_dict
def _load_plugins(self): self.log.info("Loading plugins...") # TODO: This should be cleaned up. Create a Plugins superclass and # classmethods to determine if the plugins should be used. for plugin in Util.string_to_list(self.config['mpf']['plugins']): self.log.debug("Loading '%s' plugin", plugin) pluginObj = self.string_to_class(plugin)(self) self.plugins.append(pluginObj)
def configure_driver(self, config, device_type='coil'): if not self.net_connection: self.log.critical("A request was made to configure a FAST driver, " "but no connection to a NET processor is " "available") sys.exit() # If we have WPC driver boards, look up the driver number if self.machine_type == 'wpc': config['number'] = self.wpc_driver_map.get( config['number_str'].upper()) if ('connection' in config and config['connection'].lower() == 'network'): config['connection'] = 1 else: config['connection'] = 0 # local driver (default for WPC) # If we have FAST IO boards, we need to make sure we have hex strings elif self.machine_type == 'fast': if self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) # Now figure out the connection type if ('connection' in config and config['connection'].lower() == 'local'): config['connection'] = 0 else: config['connection'] = 1 # network driver (default for FAST) else: self.log.critical("Invalid machine type: {0{}}".format( self.machine_type)) sys.exit() return (FASTDriver(config, self.net_connection.send, self.machine), (config['number'], config['connection']))
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 _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['mpf']['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 _load_mode(self, mode_string): """Loads a mode, reads in its config, and creates the Mode object. Args: mode: String name of the mode you're loading. This is the name of the mode's folder in your game's machine_files/modes folder. """ self.log.debug('Processing mode: %s', mode_string) config = dict() # find the folder for this mode: mode_path = os.path.join(self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string) if not os.path.exists(mode_path): mode_path = os.path.abspath(os.path.join('mpf', self.machine.config['media_controller']['paths']['modes'], mode_string)) # Is there an MPF default config for this mode? If so, load it first mpf_mode_config = os.path.join( 'mpf', self.machine.config['media_controller']['paths']['modes'], mode_string, 'config', mode_string + '.yaml') if os.path.isfile(mpf_mode_config): config = Config.load_config_file(mpf_mode_config) # Now figure out if there's a machine-specific config for this mode, and # if so, merge it into the config mode_config_folder = os.path.join(self.machine.machine_path, self.machine.config['media_controller']['paths']['modes'], mode_string, 'config') found_file = False for path, _, files in os.walk(mode_config_folder): for file in files: file_root, file_ext = os.path.splitext(file) if file_root == mode_string: config = Util.dict_merge(config, Config.load_config_file(os.path.join(path, file))) found_file = True break if found_file: break return Mode(self.machine, config, mode_string, mode_path)
def adjust_colors(self, **kwargs): """Takes a settings dictionary and converts the object and background colors into a format Pygame can use. Args: **kwargs: A settings dictionary for this display element. Specific key / value pairs this method uses are shade, bg_shade, color, and bg_color. This method sets the adjusted_color and adjusted_bg_color attributes. """ if self.slide.depth == 8: if 'shade' in kwargs: self.adjusted_color = (kwargs['shade'], 0, 0) else: self.adjusted_color = (15, 0, 0) # todo default config if 'bg_shade' in kwargs: self.adjusted_bg_color = (kwargs['bg_shade'], 0, 0) else: self.adjusted_bg_color = None else: # 24-bit if 'color' in kwargs: color_list = Util.hex_string_to_list(kwargs['color']) self.adjusted_color = (color_list[0], color_list[1], color_list[2]) else: self.adjusted_color = (255, 255, 255) # todo default config if 'bg_color' in kwargs: color_list = Util.hex_string_to_list(kwargs['color']) self.adjusted_bg_color = (color_list[0], color_list[1], color_list[2]) else: self.adjusted_bg_color = None
def collect_balls(self, target='home, trough'): """Used to ensure that all balls are in contained in ball devices with the tag or list of tags you pass. Typically this would be used after a game ends, or when the machine is reset or first starts up, to ensure that all balls are in devices tagged with 'home' and/or 'trough'. Args: target: A string of the tag name or a list of tags names of the ball devices you want all the balls to end up in. Default is ['home', 'trough']. """ # I'm embarrassed at how ugly this code is. But meh, it works... tag_list = Util.string_to_list(target) self.log.debug("Collecting all balls to devices with tags '%s'", tag_list) target_devices = set() source_devices = set() balls_to_collect = False for tag in tag_list: for device in self.machine.ball_devices.items_tagged(tag): target_devices.add(device) for device in self.machine.ball_devices: if device not in target_devices: if device.balls: source_devices.add(device) balls_to_collect = True self.log.debug("Ejecting all balls from: %s", source_devices) if balls_to_collect: self.machine.events.post('collecting_balls') for device in target_devices: self.machine.events.replace_handler( 'balldevice_{}_ball_enter'.format(device.name), self._collecting_balls_entered_callback, target=target) for device in source_devices: device.eject_all() else: self.log.debug("All balls are collected")
def _load_plugins(self): self.log.info("Loading plugins...") # TODO: This should be cleaned up. Create a Plugins superclass and # classmethods to determine if the plugins should be used. for plugin in Util.string_to_list( self.config['mpf']['plugins']): self.log.debug("Loading '%s' plugin", plugin) pluginObj = self.string_to_class(plugin)(self) self.plugins.append(pluginObj)
def _load_config_file(self, filename, verify_version=True, halt_on_error=True): config_file = MPFConfigFile(filename, FileManager.load(filename, verify_version, halt_on_error, True)) try: if 'config' in config_file.config: path = os.path.split(filename)[0] for file in Util.string_to_list(config_file.config['config']): full_file = os.path.join(path, file) new_config = self._load_config_file(full_file) config_file.add_child_file(new_config) return config_file except TypeError: return dict()
def are_balls_collected(self, target=None, antitarget=None): """Checks to see if all the balls are contained in devices tagged with the parameter that was passed. Note if you pass a target that's not used in any ball devices, this method will return True. (Because you're asking if all balls are nowhere, and they always are. :) Args: target: String or list of strings of the tags you'd like to collect the balls to. Default of None will be replaced with 'home' and 'trough'. """ if not target: target = ['home', 'trough'] self.log.debug( "Checking to see if all the balls are in devices tagged" " with '%s'", target) if type(target) is str: target = Util.string_to_list(target) count = 0 devices = set() for tag in target: for device in self.machine.ball_devices.items_tagged(tag): devices.add(device) if len(devices) == 0: # didn't find any devices matching that tag, so we return True return True for device in devices: count += device.get_status('balls') self.log.debug('Found %s ball(s) in %s. Found %s total', device.get_status('balls'), device.name, count) if count == self.machine.ball_controller.num_balls_known: self.log.debug("Yes, all balls are collected") return True else: self.log.debug( "No, all balls are not collected. Balls Counted: %s. " "Total balls known: %s", count, self.machine.ball_controller.num_balls_known) return False
def _get_merged_settings(self, section_name): # Returns a dict_merged dict of a config section from the machine-wide # config with the mode-specific config merged in. if section_name in self.machine.config: return_dict = copy.deepcopy(self.machine.config[section_name]) else: return_dict = CaseInsensitiveDict() if section_name in self.config: return_dict = Util.dict_merge(return_dict, self.config[section_name], combine_lists=False) return return_dict
def are_balls_collected(self, target=None, antitarget=None): """Checks to see if all the balls are contained in devices tagged with the parameter that was passed. Note if you pass a target that's not used in any ball devices, this method will return True. (Because you're asking if all balls are nowhere, and they always are. :) Args: target: String or list of strings of the tags you'd like to collect the balls to. Default of None will be replaced with 'home' and 'trough'. """ if not target: target = ['home', 'trough'] self.log.debug("Checking to see if all the balls are in devices tagged" " with '%s'", target) if type(target) is str: target = Util.string_to_list(target) count = 0 devices = set() for tag in target: for device in self.machine.ball_devices.items_tagged(tag): devices.add(device) if len(devices) == 0: # didn't find any devices matching that tag, so we return True return True for device in devices: count += device.get_status('balls') self.log.debug('Found %s ball(s) in %s. Found %s total', device.get_status('balls'), device.name, count) if count == self.machine.ball_controller.num_balls_known: self.log.debug("Yes, all balls are collected") return True else: self.log.debug("No, all balls are not collected. Balls Counted: %s. " "Total balls known: %s", count, self.machine.ball_controller.num_balls_known) return False
def process_config(config_spec, source, target=None): config_spec = yaml.load(config_spec) processed_config = source for k in config_spec.keys(): if k in source: processed_config[k] = Config.validate_config_item( config_spec[k], source[k]) else: log.debug('Processing default settings for key "%s:"', k) processed_config[k] = Config.validate_config_item( config_spec[k]) if target: processed_config = Util.dict_merge(target, processed_config) return processed_config
def process_random_event_player(self, config, mode=None, priority=0): # config is localized to 'event_player' if self.debug: self.log.debug("Processing random_event_player configuration. Priority:" " %s", priority) event_keys = set() for event_name, events in config.iteritems(): if type(events) is not list: events = Util.string_to_list(events) event_keys.add(self.machine.events.add_handler(event_name, self._random_event_player_callback, priority, event_list=events)) return self.unload_event_player_events, event_keys
def adjust_color(self, color, transparent=False): if self.slide.depth == 8: if color: # Non-black return ((color, 0, 0)) elif transparent: return None else: # Black return ((0, 0, 0)) else: # 24-bit if color: # Non-black color_list = Util.hex_string_to_list(color) return ((color_list[0], color_list[1], color_list[2])) elif transparent: return None else: # Black return ((0, 0, 0))
def process_random_event_player(self, config, mode=None, priority=0): # config is localized to 'event_player' if self.debug: self.log.debug( "Processing random_event_player configuration. Priority:" " %s", priority) event_keys = set() for event_name, events in config.iteritems(): if type(events) is not list: events = Util.string_to_list(events) event_keys.add( self.machine.events.add_handler( event_name, self._random_event_player_callback, priority, event_list=events)) return self.unload_event_player_events, event_keys
def load(self, filename, verify_version=True, halt_on_error=False): """Loads a YAML file from disk. Args: filename: The file to load. verify_version: Boolean which specifies whether this method should verify whether this file's config_version is compatible with this version of MPF. Default is True. halt_on_error: Boolean which controls what happens if the file can't be loaded. (Not found, invalid format, etc. If True, MPF will raise an error and exit. If False, an empty config dictionary will be returned. Returns: A dictionary of the settings from this YAML file. """ if verify_version and not Config.check_config_file_version(filename): raise Exception( "Config file version mismatch: {}".format(filename)) try: self.log.debug("Loading configuration file: %s", filename) with open(filename, 'r') as f: config = Util.keys_to_lower(yaml.load(f)) except yaml.YAMLError, exc: if hasattr(exc, 'problem_mark'): mark = exc.problem_mark self.log.critical( "Error found in config file %s. Line %s, " "Position %s", filename, mark.line + 1, mark.column + 1) if halt_on_error: sys.exit() else: config = dict()
def __init__(self, machine, name, config, collection=None, validate=True): config['number_str'] = str(config['number']).upper() super(LED, self).__init__(machine, name, config, collection, platform_section='leds', validate=validate) self.config['default_color'] = Util.hex_string_to_list( input_string=self.config['default_color'], output_length=3) self.hw_driver = self.platform.configure_led(self.config) self.fade_in_progress = False self.fade_task = None self.fade_destination_color = [0.0, 0.0, 0.0] self.fade_end_time = None self.state = { # current state of this LED 'color': [0.0, 0.0, 0.0], 'priority': 0, 'destination_color': [0.0, 0.0, 0.0], 'destination_time': 0.0, 'start_color': [0.0, 0.0, 0.0], 'start_time': 0.0 } self.cache = { # cached state of last manual command 'color': [0.0, 0.0, 0.0], 'priority': 0, 'destination_color': [0.0, 0.0, 0.0], 'destination_time': 0.0, 'start_color': [0.0, 0.0, 0.0], 'start_time': 0.0 } self.set_brightness_compensation(self.config['brightness_compensation']) self.current_color = [] # one item for each element, 0-255
def get_merged_config(self): merged_config = self.config for key in self.child_files: merged_config = Util.dict_merge(merged_config, self.child_files[key].get_merged_config()) return merged_config
parser.add_argument("-C", action="store", dest="mpfconfigfile", default=os.path.join("mpf", "mpfconfig.yaml"), metavar='config_file', help="The MPF framework default config file. Default is " "mpf/mpfconfig.yaml") parser.add_argument("--version", action="version", version=version.version_str, help="Displays the MPF, config file, and BCP version info " "and exits") args = parser.parse_args() args.configfile = Util.string_to_list(args.configfile) # Configure logging. Creates a logfile and logs to the console. # Formatting options are documented here: # https://docs.python.org/2.7/library/logging.html#logrecord-attributes try: os.makedirs('logs') except OSError as exception: if exception.errno != errno.EEXIST: raise logging.basicConfig( level=args.loglevel, format='%(asctime)s : %(levelname)s : %(name)s : %(message)s', filename=args.logfile,
def rotate(self, direction=None, steps=1, states=None, exclude_states=None, mode=None, **kwargs): """Rotates (or "shifts") the state of all the shots in this group. This is used for things like lane change, where hitting the flipper button shifts all the states of the shots in the group to the left or right. This method actually transfers the current state of each shot profile to the left or the right, and the shot on the end rolls over to the taret on the other end. Args: direction: String that specifies whether the rotation direction is to the left or right. Values are 'right' or 'left'. Default of None will cause the shot group to rotate in the direction as specified by the rotation_pattern. steps: Integer of how many steps you want to rotate. Default is 1. states: A string of a state or a list of strings that represent the targets that will be selected to rotate. If None (default), then all targets will be included. exclude_states: A string of a state or a list of strings that controls whether any targets will *not* be rotated. (Any targets with an active profile in one of these states will not be included in the rotation. Default is None which means all targets will be rotated) Note that this shot group must, and rotation_events for this shot group, must both be enabled for the rotation events to work. """ if not self.machine.game: return if not self.rotation_enabled: if self.debug: self.log.debug("Received rotation request. " "Rotation Enabled: %s. Will NOT rotate", self.rotation_enabled) return # if we don't have states or exclude_states, we'll see if the first shot # in the group has them and use those. Since all the shots should have # the same profile applied, it's ok to just pick from the first one. if states: states = Util.string_to_lowercase_list(states) else: states = self.shots[0].enable_table[mode]['settings']['state_names_to_rotate'] if exclude_states: exclude_states = Util.string_to_lowercase_list(exclude_states) else: exclude_states = ( self.shots[0].enable_table[mode]['settings']['state_names_to_not_rotate']) shot_list = list() # build of a list of shots we're actually going to rotate for shot in self.shots: if ((not states or shot.enable_table[mode]['current_state_name'] in states) and shot.enable_table[mode]['current_state_name'] not in exclude_states): shot_list.append(shot) # shot_state_list is deque of tuples (state num, light show step num) shot_state_list = deque() for shot in shot_list: try: current_state = shot.running_light_show.current_location except AttributeError: current_state = -1 shot_state_list.append( (shot.player[shot.enable_table[mode]['settings']['player_variable']], current_state)) if self.debug: self.log.debug('Rotating. Mode: %s, Direction: %s, Include states:' ' %s, Exclude states: %s, Shots to be rotated: %s', mode, direction, states, exclude_states, [x.name for x in shot_list]) for shot in shot_list: shot.log.debug("This shot is part of a rotation event. Current" " state: %s", shot.enable_table[mode]['current_state_name']) # figure out which direction we're going to rotate if not direction: direction = shot_list[0].enable_table[mode]['settings']['rotation_pattern'][0] shot_list[0].enable_table[mode]['settings']['rotation_pattern'].rotate(-1) if self.debug: self.log.debug("Since no direction was specified, pulling from" " rotation pattern: '%s'", direction) # rotate that list if direction == 'right': shot_state_list.rotate(steps) else: shot_state_list.rotate(steps * -1) # step through all our shots and update their states for i in range(len(shot_list)): shot_list[i].jump(mode=mode, state=shot_state_list[i][0], lightshow_step=shot_state_list[i][1])
def do_load(self, callback, show_actions=None): self.show_actions = list() self.asset_manager.log.debug("Loading Show %s", self.file_name) if not show_actions: show_actions = self.load_show_from_disk() for step_num in range(len(show_actions)): step_actions = dict() step_actions['tocks'] = show_actions[step_num]['tocks'] # look for empty steps. If we find them we'll just add their tock # time to the previous step. if len(show_actions[step_num] ) == 1: # 1 because it still has tocks show_actions[-1]['tocks'] += step_actions['tocks'] continue # Events # make sure events is a list of strings if ('events' in show_actions[step_num] and show_actions[step_num]['events']): event_list = (Util.string_to_lowercase_list( show_actions[step_num]['events'])) step_actions['events'] = event_list # slide_player if ('display' in show_actions[step_num] and show_actions[step_num]['display']): step_actions['display'] = ( self.machine.display.slide_builder.preprocess_settings( show_actions[step_num]['display'])) # Sounds if ('sounds' in show_actions[step_num] and show_actions[step_num]['sounds']): # make sure we have a list of dicts if type(show_actions[step_num]['sounds']) is dict: show_actions[step_num]['sounds'] = ([ show_actions[step_num]['sounds'] ]) for entry in show_actions[step_num]['sounds']: try: entry['sound'] = self.machine.sounds[entry['sound']] except KeyError: self.asset_manager.log.critical( "Invalid sound '%s' " "found in show. ", entry['sound']) raise step_actions['sounds'] = show_actions[step_num]['sounds'] self.show_actions.append(step_actions) # count how many total locations are in the show. We need this later # so we can know when we're at the end of a show self.total_locations = len(self.show_actions) self.loaded = True if callback: callback() self._asset_loaded()
def validate_config_item2( self, spec, validation_failure_info, item='item not in config!@#', ): default = 'default required!@#' item_type, validation, default = spec.split('|') if default.lower() == 'none': default = None 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 == 'single': item = self.validate_item(item, validation, validation_failure_info) elif item_type == 'list': item = Util.string_to_list(item) new_list = list() for i in item: new_list.append( self.validate_item(i, validation, validation_failure_info)) item = new_list elif item_type == 'set': item = set(Util.string_to_list(item)) new_set = set() for i in item: new_set.add( self.validate_item(i, validation, validation_failure_info)) item = new_set elif item_type == 'dict': item = self.validate_item(item, validation, validation_failure_info) if not item: item = dict() else: self.log.error("Invalid Type '%s' in config spec %s:%s", item_type, validation_failure_info[0][0], validation_failure_info[1]) sys.exit() return item
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